Wpf xaml에서 많은 버튼들을 어떻게 배치하나요? + dependency Property 질문

저는 WPF로 주기율표를 만드려고 해서 page를 하나 만들어서 거기에 버튼들을 배치하고 싶습니다. 근데 버튼이 100개가 넘어가는데 그것을 다 margin으로 배치해야 하는것인가요?


이런식으로 버튼들을 배치하고 싶습니다.
아 그리고 다른 질문이 하나 더 있는데 수많은 버튼에다가 이미지를 다 씌울 수 없으니까 사용자 정의 컨트롤을 하나 만들어서 거기에 속성값을 추가해서 여러번 쓰고 싶습니다. 근데 찾아보니까 dependency Property라는 것이 있더라고요… 근데 예시가 다 text로 되어있던데 저는 backGround, cornerRadius 등등 다른 속성 값도 추가해보고 싶은데 어떻게 해야 하나요??

xaml코드는 이렇게 했습니다.

네. Margin으로 컨트롤 간격을 결정할 수 있습니다. 그런데 어떤 레이아웃을 사용 하느냐에 따라 달라질 수 있습니다. 가령 전체 사이즈에서 버튼을 가변 사이즈로 배치하고 싶을 때는 Gird의 RowDefinitions, ColumnDefinition에 높이, 너비를 "*"로 주면 가능합니다.

<Window x:Class="WpfApp17.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp17"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Background="#353040">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <local:ElementControl Grid.Row="0" Grid.Column="0" Weight="1.00" ElementName="Hydrogen">H</local:ElementControl>
        <local:ElementControl Grid.Row="0" Grid.Column="17">He</local:ElementControl>

        <local:ElementControl Grid.Row="1" Grid.Column="0">Li</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="1">Be</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="12">B</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="13">C</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="14">N</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="15">O</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="16">F</local:ElementControl>
        <local:ElementControl Grid.Row="1" Grid.Column="17">Ne</local:ElementControl>

        <local:ElementControl Grid.Row="2" Grid.Column="0">Na</local:ElementControl>
        <local:ElementControl Grid.Row="2" Grid.Column="1">Mg</local:ElementControl>

        <!-- .. -->
    </Grid>
</Window>

자신이 만든 컨트롤에 다양한 속성을 부여하려면 말한 대로 Dependency Property를 구현하면 됩니다.

    public class ElementControl : ContentControl
    {
        public static DependencyProperty WeightProperty = DependencyProperty.Register("Weight", typeof(string), typeof(ElementControl), new PropertyMetadata());
        public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName", typeof(string), typeof(ElementControl), new PropertyMetadata());

        public string Weight { get => (string)GetValue(WeightProperty); set => SetValue(WeightProperty, value); }
        public string ElementName {  get => (string)GetValue(ElementNameProperty); set => SetValue(ElementNameProperty, value);}


        static ElementControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ElementControl), new FrameworkPropertyMetadata(typeof(ElementControl)));
        }
    }

위의 구현은 ContentControl을 상속받아 Content를 그대로 사용하고 있습니다.

아래는 해당 컨트롤의 스타일입니다.

| Thmes\Generic.xaml

    <Style TargetType="{x:Type local:ElementControl}">
        <Setter Property="Margin" Value="2" />
        <Setter Property="Padding" Value="0" />
        <Setter Property="Background" Value="White" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ElementControl}">
                    <Border Background="{TemplateBinding Background}" Margin="{TemplateBinding Margin}" Padding="{TemplateBinding Padding}" CornerRadius="10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                        <Viewbox>
                            <Grid Width="100" Height="100">
                                <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="15" Margin="5,0,0,0" Text="{TemplateBinding Weight}" />
                                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="40" FontFamily="Araial Rounded MT Bold">
                                    <ContentPresenter />
                                </TextBlock>
                                <TextBlock HorizontalAlignment="Center" VerticalAlignment="Bottom" FontSize="20"  FontFamily="Araial Rounded MT Bold" Margin="0,0,0,5" Text="{TemplateBinding ElementName}" />
                            </Grid>
                        </Viewbox>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

여기에서 Background는 기본 구현된 속성이여서 TemplateBinding으로 넘겨주면 되고, CornerRadiusContentControl에 없으므로 Dependency Property를 구현해서 Border에 넘겨줘도 됩니다.

그런데 어짜피 Border가 최상위 UI 구성이므로 저처럼 ContentControl에서 상속 받는게 아니라 그냥 Border에서 상속 받으면 CornerRadius를 바로 쓸 수 있습니다.

좋아요 3

와… 이렇게 자세하게 설명해주시다니 너무 감사합니다! 많은 도움이 되었습니다!!!

좋아요 2

^^; 열심히 하는 모습에 자세히 답변을 달아야겠다 싶었습니다. 계속 전진해서 멋진 프로그래머 되세요.

좋아요 3

눈여겨보던 글이었는데 학생이라는 …
정말 기대가 되는 프로그래머입니다.
winform에서 WPF로 넘어오기를 기대했는데 개인적으로 반갑습니다…

위 내용처럼 연습해 보고 더 나아가서는
model 만들고 ViewModel 생성해서
View에서 ItemsControl 이용하고 Panel은 WrapPanel 을 가로로 배치하면
위 내용은 간단하게 되리라 생각됩니다…
사이즈 또는 Blank 모델도 만들면 위 배치 그대로 구현 가능하다 생각됩니다.
https://cafe.naver.com/wince5/543
참고하면 도움이 될 것 같습니다.

좋아요 2

혹시 usercontrol과 contentcontrol의 차이점을 알 수 있을까요??

차이점이 있습니다. 컨트롤은 다음의 상속 구조로 구성되는데요,

Object → DispatcherObject → DependencyObject → Visual → ULElement → FrameworkElement → Control → ContentControl → UserControl

UserControl은 하위 클래스의 기능을 가졌다 라고 이해하면 쉽습니다. 즉, UserControl 역시 ContentControl에서 제공하는 기능이 있습니다. 그런데, UserControl을 안쓰고 ContentControl을 썼는가 하면, 굳이 UserControl에서 제공하는 기능이 필요없을 때 그런 선택을 할 수 있어요.

가령, ContentControl에는 Content를 쓸 수 있도록 구현되어 있습니다. 그런데 그 상위 클래스인 Control은 Content가 없어서 Content를 쉽게 쓰고 싶을 때는 ContentControl을 선택하는 식입니다.

그렇다면 UserControl만의 기능이 무엇인지 살펴보면 선택 시 수월하겠지요.

    //
    // 요약:
    //     Provides a simple way to create a control.
    public class UserControl : ContentControl
    {
        //
        // 요약:
        //     Initializes a new instance of the System.Windows.Controls.UserControl class.
        public UserControl()
        {
        }

        //
        // 요약:
        //     Creates and returns an System.Windows.Automation.Peers.AutomationPeer for this
        //     System.Windows.Controls.UserControl.
        //
        // 반환 값:
        //     A new System.Windows.Automation.Peers.UserControlAutomationPeer for this System.Windows.Controls.UserControl.
        protected override AutomationPeer OnCreateAutomationPeer()
        {
            throw null;
        }
    }

흠… (거의) 차이가 없네요 ^^; 어짜피 ContentControl이든 UserControl이든 XAML 디자이너의 도움을 받을 수 있으니 차이가 “없다고” 봐도 됩니다.

좋아요 2

UserControl, ContentControl의 큰 차이점은
Control의 속성을 사용하느냐 차이인 것 같습니다.
예를 들자면 Background 속성 같은 것들입니다.
오래되어서 가물 하네요.

좋아요 1

귀찮으실 수도 있는데… 질문 몇개만 더 드리자면 local:은 무슨 뜻이고 contentControl은 어떻게 생성하나요??

  1. local:은 xmlns(네임스페이스)를 의미하고 위 샘플에서는 d, x, mc, local이 이에 해당합니다.
  2. 상속의 정의인지, 컨트롤 생성인지요?

저는 사용자 정의 컨트롤을 생성했는데 contentControl은 어떻게 생성하나요?
보통 public class 이름 : UserControl이라고 되어있는데 UserControl을 그냥 ContentControl로 바꾸면 되는 것인가요?

UserControl을 만드는 것이라면 그냥 UserControl에서 상속 받아 만들면 됩니다 CustomControl로 만드는거라면 클래스 구현부와 스타일을 따로 만들게 됩니다. 추가 파일에 커스텀클래스 있으니까 그걸로 확인 해보면 됩니다.

좋아요 1