반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

우스 커서를 움직여서
특정 글자위에 위치하게 되면,
해당 글자의 의존 속성(Dependency Property)이
변경되는 프로젝트를 만들어 보자.

 

내용 요약

 

우선 Trigger에 대해서 다시한번 살펴보면,

 

Trigger

- 정의 : 트리거란 특정 조건, 예를들어 특정 속성의 값이 변경되는 경우 또는

이벤트가 발생하도록하여, 원하는 속성의 값을 바꿀 수 있도록해주는 개체이다.

-종류 : 트리거의 종류로는 크게 네가지가 있다.

1.Property Trigger : Dependency Property가 변경될 때 실행

2.Event Trigger : Routed Event가 발생할 때 실행

3.Data Trigger : Binding으로 연결된 Property 값이 변경되었을 때 실행

4.Multi Trigger : 다수의 Property 조건이 만족할 때 실행

이 포스팅에서는 Property Trigger에 대한 내용을 살펴보도록 하겠다.

 

Dependency Property의 정의

Property값이 변경 되었을 때 자동으로 어떤 값을

처리할 수 있게 해주는 것으로 스타일링, 데이터 바인딩, 애니메이션 등의

WPF 주요 부분에 사용된다.

Property Trigger의 예시를 위해

Reference Site에서 TextBlock의 Dependency Property를 살펴보면 매우 많은

의존 속성이 있는 것을 알 수 있다.

 

https://learn.microsoft.com/en-us/dotnet/api/system.windows.uielement?view=windowsdesktop-8.0Trigger

 

UIElement Class (System.Windows)

UIElement is a base class for WPF core level implementations building on Windows Presentation Foundation (WPF) elements and basic presentation characteristics.

learn.microsoft.com

 

본 포스팅에서는 IsMouseOver 의존속성을 이용하여

Property Trigger에 대한 이해를 할 수 있는 시간을 가져보도록 하겠다.

 

 

목차
1.Property Trigger(IsMouseOver)를 위한 UI(.xaml) 만들기
2.설명 및 실행결과

 

하나의 버튼과 텍스트 블록을 만들고,

마우스 커서가 각각의 Control에 위치할 때 글자의 색깔과

크기 및 Text를 변경하는 예제를 만들어 보자.

 

 

1.Property Trigger(IsMouseOver)를 위한 UI 만들기

 

마우스가 특정 버튼 혹은 글자(TextBlock)위에 있을 경우 원하는 속성이 변경하도록

.xaml 파일에 아래와 같이 작성해보자.

 

(본 포스팅에서는 마우스 포인터가 올라갈 경우

글자색과 글자 내용이 변경되도록 하였다.)

 

<Window x:Class="PropertyTrigger.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:PropertyTrigger"
        mc:Ignorable="d"
        Title="MainWindow" Height="217" Width="479">
    <Window.Resources>
        <Style x:Key="MyStyle">
            <Setter Property="Control.Foreground" Value="Red"/>
            <Setter Property="TextBlock.Text" Value="Hello WPF!"/>
            <Setter Property="Button.Content" Value="Property Trigger&#10;(Original)"/>
            <Style.Triggers>
                <Trigger Property="Control.IsMouseOver" Value="True">
                    <Setter Property="Control.Foreground" Value="Blue"/>
                    <Setter Property="TextBlock.Text" Value="버튼으로 진입했습니다."/>
                    <Setter Property="Button.Content" Value="Property Trigger&#10;(Changed)"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button Width="100" Height="70"
                Style="{StaticResource MyStyle}"/>
        <TextBlock Style="{StaticResource MyStyle}"
                   FontSize="30" FontWeight="Bold"
                   HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </StackPanel>

</Window>

 

비하인드 코드에는 특별히 추가된 코드가 없다.

 

 

2. 설명 및 실행 결과

 

기본적인 속성은 아래의 코드에서처럼 글자색은 "Red",

글자는 TextBlock의 경우 "Hello WPF!"

Button의 경우 "Property Trigger (Original)"

인데,

 

<Setter Property="Control.Foreground" Value="Red"/>
<Setter Property="TextBlock.Text" Value="Hello WPF!"/>
<Setter Property="Button.Content" Value="Property Trigger&#10;(Original)"/>

 

아래의 코드에서처럼 Button과 TextBlock의 Style을 MyStyle로 설정해놓고,

<Button Width="100" Height="70" Style="{StaticResource MyStyle}"/>
<TextBlock Style="{StaticResource MyStyle}" FontSize="30" FontWeight="Bold"
          HorizontalAlignment="Center" VerticalAlignment="Center"/>

 

Trigger의 Property를 아래처럼 IsMouseOver로 설정한다음

IsMouseOver가 되었을 경우

변경하고 싶은 속성을 아래와 같이 작성해주면 된다.

여기에서는 글자색(.Foreground), TextBlock의 텍스트(.Text) 그리고 Button의 콘텐트(.Content)를

변경해 주었다.

<Trigger Property="Control.IsMouseOver" Value="True">
     <Setter Property="Control.Foreground" Value="Blue"/>
     <Setter Property="TextBlock.Text" Value="버튼으로 진입했습니다."/>
     <Setter Property="Button.Content" Value="Property Trigger&#10;(Changed)"/>
</Trigger>

 

프로그램을 실행하면 앞서 설명한 것 처럼 아래 그림과 같이 나타난다.

여기에서 만약 마우스 포인터를 버튼(Property Trigger(Original))위에 올리게 되면,

아래 그림과 같이 글자색은 파란색으로 바뀌고, 글자 또한

(original)에서 (Changed)로 바뀌게 된다.

그리고 TextBlock인 Hollo WPF!의 위에

마우스 포인터가 놓이게 되면,

아래 그림과 같이 바뀌게 된다.

Tip.

기존 C/C++에서의 줄바꿈에 해당하는 ‘\n’ 대신에

C# WPF .xaml에서는 '&#10'을 사용하면,

버튼 또는 TextBlock등에서

줄 바꿈이 가능하다.

 

 

"

마치며...

본 포스팅에서는 IsMouseOver라는 Property를 이용하여

버튼과 텍스트블럭의 글자 색과 내용을

원하는 Style로 변경하는 프로그램을

작성해 보았다.

"

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

# WPF에서의 Data Trigger에 대해 알아보고,

이해하는 시간을 가져보자.

 

 

 

내용 요약

 

Trigger

- 정의 : 트리거란 특정 조건, 예를들어 특정 속성의 값이 변경되는 경우 또는

이벤트가 발생하도록하여, 원하는 속성의 값을 바꿀 수 있도록해주는 개체이다.

-종류 : 트리거의 종류로는 크게 네가지가 있다.

1.Property Trigger : Dependency Property가 변경될 때 실행

2.Event Trigger : Routed Event가 발생할 때 실행

3.Data Trigger : Binding으로 연결된 Property 값이 변경되었을 때 실행

4.Multi Trigger : 다수의 Property 조건이 만족할 때 실행

이 포스팅에서는 Data Trigger에 대한 내용을 살펴보도록 하겠다.

 

목차
1.Data Binding을 위한 속성 만들기
2.UI 만들기
3.실행 결과

 

Slider, ProgressBar 그리고 TextBox가 MainWindow의 속성

TheValue에 Binding되어있으며,

Slider를 움직이거나 TextBox에 값을 입력하면,

나머지 두개의 UI Control의 속성이 변한다.

이때 속성이 특정 값이 될 경우

Data Trigger가 발생하여

특정 속성을 변경할 수 있으며,

CheckBox의 Check여부를 확인하여

TextBox의 FontSize, FontWeight 및 Text를

변경하는 DataTrigger를 발생시키는 예제를

하도록 하겠다.

 

1.Data Binding을 위한 속성 만들기

 

접근 지정자를 통해 private 멤버와 public 멤버를 생성하고

두 멤버를 get, set을 통해 값을 대입 해준다.

여기서, set의 경우 속성값 변경에 대한

자동 알림 정의(INotifyPropertyChanged)를 해준다.

 

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    /*===============================================================*/
    private int _theValue;
    public int TheValue
    {
        get
        {
            return _theValue;
        }
        set
        {
            _theValue = value;
            OnPropertyChanged();
        }
    }
        
    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string parameter = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(parameter));
    }
    /*===============================================================*/
}

 

 

2. UI 만들기

 

TheValue 속성과 연동하기 위한 UI Controller를 아래와 같이

코딩 하였다.

 

<Window.Resources>
    <!-- DataTrigger : Progress Bar -->
        <Style TargetType="{x:Type ProgressBar}">
        <Setter Property="Foreground" Value="Blue"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding TheValue}" Value="50">
                <Setter Property="Foreground" Value="Red"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <!-- DataTrigger : TextBox -->
    <Style TargetType="{x:Type TextBox}">
        <Setter Property="Foreground" Value="Black"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding TheValue}" Value="50">
                <Setter Property="Foreground" Value="Red"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
    <!-- DataTrigger : TextBlock -->
    <Style x:Key="MyStyle" TargetType="TextBlock">
        <Setter Property="Visibility" Value="Visible"/>
        <Setter Property="Text" Value="CheckBox Un-Selected!"/>
        <Setter Property="FontSize" Value="15"/>
        <Style.Triggers>                
            <DataTrigger Binding="{Binding ElementName=cb1, Path=IsChecked}" Value="True">
                <Setter Property="FontSize" Value="25"/>
                <Setter Property="FontWeight" Value="Bold"/>
                <Setter Property="Text" Value="CheckBox Selected"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="1*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <!-- Slider -->
    <StackPanel Grid.Row="0" Grid.ColumnSpan="2" >
        <TextBlock Text="Slider" FontSize="15" FontWeight="Bold" Padding="5"/>
        <Slider x:Name="MySlider" Margin="5 10 5 5" Minimum="0" Maximum="100"
                Value="{Binding TheValue}"/>
    </StackPanel>
        
    <!-- Progress Bar -->
    <StackPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal">
        <TextBlock Text="Progress Bar : " FontSize="15" FontWeight="Bold"  Padding="5" HorizontalAlignment="Center" VerticalAlignment="Center" />
        <TextBox   Width="100" FontSize="15" FontWeight="Bold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
                   Text="{Binding TheValue, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
    <StackPanel Grid.Row="2" Grid.ColumnSpan="2">
            <ProgressBar Height="20" Minimum="0" Maximum="100" Value="{Binding TheValue}"/>
    </StackPanel>

 

 

3. 실행 결과

 

프로그램을 처음 실행하면 아래 그림과 같이

Slider 및 Progress Bar의 초기 값 0으로 나타난다.

이후 Slider를 움직이면,

아래와 같이 Progress Bar와 TextBox의 값이

모두 Binding되어있어서 같은 값을 나타내며

아래 그림과 같이

Slider를 마우스로 드래그하다가

값이 50을 가리킬때

아래 그림과 같이 ProgressBar의 색상과 값을 나타내는 TextBox의

글자 색이 빨간색으로 변하는 것을 볼 수 있다.

CheckBox를 클릭하게 되면, 아래와 같이 오른쪽 TextBox의

FontSize, FontWeight, Text등의

속성이 변경된다.

"

마치며...

특정 UI Controller의 값을 다른 UI Controller와 Binding하여

설정된 값이 되었을 경우 Trigger를 발생시켜서

원하는 Controller의 속성을

변경 시킬 수 있음을 본 포스팅을 통해 알게 되었다.

"

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C# WPF에서
Data를 Binding하여 UI앱을 통해 입력 받은 값을
다른 UI Control을 통해 보여주거나
User가 정의한 Class의 객체 속성 값을
변경하는 프로젝트를 만들어 보자.

 

 

내용 요약

데이터 바인딩

- 정의 : 앱 UI와 해당 UI가 표시하는 데이터를 연결하는 프로세스

- 동작

1.조건 : 올바른 바인딩 설정 / 알림 제공의 경우

→ 데이터가 변경되는 경우 데이터에 바인딩된 요소에 변경된 사항이 자동 반영되거나

반대로 요소에서 변경된 사항이 요소에 바인딩된 데이터에 전달되어

변경 사항이 자동으로 데이터에 반영된다.

데이터 바인딩을 하기 위해서는 Source와 Target이 필요하다.

즉, Source객체의 특정 속성을 Target객체의 특정 속성에 Binding하여

방향성(Mode)을 가지고 전달하게 된다.

UI Control은 특정 데이터를 사용자에게 보여줄 뿐만 아니라 데이터를 변경하는데

사용되는 두가지 기능을 제공하는데,

이러한 UI Control 및 클래스 객체를 이용하여

아래와 같은 두가지 예제를 통해 Binding에 대해 자세히 살펴보도록 하겠다.

1.UI Control (Source) <---(Binding)---> UI Control (Target) : OneWay, TwoWay

2.UI Control <---(Binding)---> Class Object property : OneWay, TwoWay

 

 

목차

1.UI Control (Source) <---(Binding)---> UI Control (Target) : OneWay, TwoWay
2.UI Control <---(Binding)---> Class Object property : OneWay, TwoWay

 

 

1.UI Control(Source) <---(Binding)---> UI Control : OneWay, TwoWay

 

두개의 TextBox 1/2를 UI 앱(xaml)에 만들고,

TextBox1는 Source로

TextBox2는 Target으로하여

Source(TextBox1)의 속성 중 하나인 Text를

Target(TextBox2)의 속성 중 하나인 Text에

Mode를 OneWay 방식으로

Binding 시켜보도록 하겠다.

여기서 OneWay로 Binding하게 되면 TextBox1의 Text 속성이

변할 경우 TextBox2의 Text 속성은 변하지만

그 반대로 TextBox2가 변하는 거에 대해 TextBox1은

변하지 않는다.

Binding의 Mode는 기본값이 TwoWay이기 때문에 Target UI Control에서

Mode값을 OneWay로 변경해 주어야 Target -> Source로의

데이터 바인딩이 발생하지 않는다.

즉, Target UI Control의 Text 속성 값을 바꾸더라도 Source UI Control의 Text 속성값이

변경되지 않는다.

(Behind code에서는 아무런 코드가 필요 없다.)

 

<Grid>
    <StackPanel>
        <TextBlock Text="TextBox1(Source)" FontWeight="Bold" FontSize="10"/>
        <TextBox x:Name="txtBox1" Margin="10 0 10 0" Padding="5"/>
        <TextBlock Text=“TextBox2(Target)” FontWeight=“Bold” FontSize=“10”/>
        <TextBox x:Name=“txtBox2” Margin=“10 0 10 0” Padding=“5” 
                 Text=“{Binding Source={x:Reference txtBox1}, Path=Text, Mode=OneWay, 
                 UpdateSourceTrigger=PropertyChanged}"
                 />
    </StackPanel>
</Grid>

 

실행 화면

 

 

이러한 OneWay Mode(Source -> Target)를 TwoWay로 변경하기 위해서는 Mode의 값을

TwoWay로 변경해 주면 된다.

그렇게 하면, Target의 Text를 변경할 때 Source의 Text도 같이 변경이 된다.

그런데 한가지 이상한 점은 TextBox2의 Text창에 값(문자)을 입력 후 해당 UI Control이

포커스를 잃을 경우(TextBox 1을 마우스로 클릭 할때)에만

Target의 속성(Text)값이 Source의

속성(Text)에 반영되는 것을 볼 수 있는데,

이를 변경해 주는 옵션 설정이 UpdateSourceTrigger이다.

여기에서 UpdateSourceTrigger PropertyChanged로 설정하게 되면

Target의 Text 속성이 바뀔때

Trigger가 발생되어 Source의 Text 속성도 동시에 바뀌게 된다.

 

 

2.UI Control(Source) <---(Binding)---> Class Object Prop. : OneWay, TwoWay

 

해당 예제를 위해서는 사용자 클래스를 하나 정의 해주어야 한다.

여기서는 TextClass라고 하는 클래스를 만들도록 하겠다.

 

public class TextClass : INotifyPropertyChanged
{
    private string _strTextBox1;
    public string StrTextBox1
    {
        get
        {
            return _strTextBox1;
        }
        set
        {
            _strTextBox1 = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string parameter = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(parameter));
    }
}

 

기존의 TextBox2(Name="txtBox2")는 그대로 TextBox1에 Binding을 유지한 채로

TextBox1의 Binding만 아래와 같이 변경해 주었다.

 

<Grid>
    <StackPanel>
        <TextBlock Text="TextBox1(Source)" FontWeight="Bold" FontSize="10"/>
        <TextBox x:Name="txtBox1" Margin="10 0 10 0" Padding="5" Text="{Binding Path=StrTextBox1}"/>
        <TextBlock Text="TextBox2(Target)" FontWeight="Bold" FontSize="10"/>
        <TextBox x:Name="txtBox2" Margin="10 0 10 0" Padding="5" 
                 Text="{Binding Source={x:Reference txtBox1}, Path=Text,
                        Mode=OneWay}" />
        <Button Content="Show TextClass Member" Margin="20 10 20 0"
                Click="ShowTextClassMember"/>
    </StackPanel>
</Grid>

 

기존에 생성자에서 InitializeComponent()만 수행하던 MainWindow.xaml.cs 파일은

아래와 같이

우선, TextClass에 대한 객체를 생성해 주었으며, 생성하면서

멤버(속성)의 값을 ‘first String’으로 주었다.

 

그리고

버튼 클릭에 대한 함수가 추가 되었고,

MainWindow 생성자에서

TextBox와 TextClass를 Binding하기 위한

TextBox의 DataContext에 TextClass 객체를 할당해 주었다.

 

public partial class MainWindow : Window
 {
     public TextClass txtClass { get; set; } = new TextClass() { StrTextBox1 = "first String" };

     public MainWindow()
     {
         InitializeComponent();
         this.DataContext = this;
     }

     /*===============================================================*/
     private void ShowTextClassMember(object sender, RoutedEventArgs e)
     {
         MessageBox.Show($"TextClass member value : {txtClass.StrTextBox1}");
     }
     /*===============================================================*/
 }

 

위 프로젝트를 실행하게 되면,

TextBox1은 DataContext가 발생하면서 txtClass의 속성 StrTextBox1이

가지고 있는 값 ‘first String’으로 초기화 되며, 동시에,

Binding 되어있던 TextBox2의 Text 속성도 같은 값으로 변경이 된다.

이때 버튼(Show TextClass Member)을 누르면,

아래와 같은 메세지 박스가 나오며,

여기서 TextBox1을 변경하든, TextBox2를 변경하든

(기존 TwoWay Mode로 되어있는 경우)

버튼을 눌렀을 때 변경된 값이

나타나는 것을 확인 할 수 있다.

여기서 중요한 것 한가지는 TextBox2와 TextBox1이 TwoWay Mode로

Binding 되어있기는 하지만,

TextBox2에서 Text의 속성을 변경한 것이

TextBox1의 Text에 Binding되어있는 TextClass 객체의

StrTextBox1값을 변경하려면,

TextBox1의 Text 속성에서

UpdateSourceTrigger=PropertyChanged 를 설정해 주어야 한다.

 

<Grid>
    <StackPanel>
        <TextBlock Text="TextBox1(Source)" FontWeight="Bold" FontSize="10"/>
        <TextBox x:Name="txtBox1" Margin="10 0 10 0" Padding="5" 
                 Text="{Binding Path=txtClass.StrTextBox1, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Text="TextBox2(Target)" FontWeight="Bold" FontSize="10"/>
        <TextBox x:Name="txtBox2" Margin="10 0 10 0" Padding="5" 
                 Text="{Binding Source={x:Reference txtBox1}, Path=Text,
                        UpdateSourceTrigger=PropertyChanged,
                        Mode=TwoWay}" />
        <Button Content="Show TextClass Member" Margin="20 10 20 0"
                Click="ShowTextClassMember"/>
    </StackPanel>
</Grid>

 

그렇게 하면,

TextBox1에서 Text를 변경하고 Button을 눌러도

TextBox2의 Text가 변경되면서 TextClass의 StrTextBox1이 같이 바뀌며,

TextBox2에서 Text를 변경하고 Button을 눌러도

TextBox1의 Text와 TextClass의 StrTextBox1이 같이

바뀌는 것을 볼 수 있다.

 

 

 

 

"

마치며...

UI Control과 객체의 속성을

연결하는 방법을 알게 되었고,

이 방법을 이용하여 사용자로부터 입력받는 데이터를

특정 객체의 속성에

자동 적용 되도록 할 수 있게 되었다.

"

 

 

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#의 WPF를 활용하여 User Control을 만들고,
MVVM패턴에서 Model객체의 멤버(Property)를
Binding하는 방법에 대해 알아보겠습니다.




우선, C# WPF에 대해 공부를 시작하면서...

 

C# 으로 WPF를 처음 접하게 되면 기존에 Winows Form Apps으로 구현하던 방식과

매우 다르다는 것을 느끼게된다.

Winows Form Apps이 Drag and Drop 방식으로 Window App을 꾸민 다음,

원하는 동작을 구현하는 방식으로 했다면,

WPF(Windows Presentation Foudation)에서는

철저히 화면 UI와 UI에서 보여주기 위한 정보가

독립적인 공간에 존재하게 되어

유지/보수성 및 분업화에

매우 효율적일 것으로 생각되었다.

또한, WPF는

XAML(Extensible Application Markup Language)을 이용하여

내가 원하는 컨트롤(Button, TextBox 등)을

내 스타일 대로 만들 수 있다는 장점이 있다.

 

 

 

내용 요약

내용을 요약하면,

MVVM 패턴 방식으로 프로젝트를 만들기 위해 Model, View, ViewModel, MVVM 폴더를 생성하여

1.Model에 Family class를 정의 (2번의 BaseViewModel을 상속)

2.MVVM에 속성(=Family 객체 멤버) 변경에 대한 Binding을 위해 BaseViewModel 생성

3.View에 MainWindow에 삽입하기 위한 User Control 생성

4.VIewModel에 Data Context를 통해 UI와 데이터를 주고받을 MainWindowVIewModel 생성 및

Family 객체에 대한 정보를 보여줄 FamilyDisplay User Control 생성

하고,

Button을 통해

Family 객체 속성(멤버)값을

MessageBox를 통해 보여주는

App을 제작할 것이다.

 

 
목차
  1. MVVM 패턴 설정
  2. Family class(Model) / BaseViewModel 만들기
  3. FamilyDisplay(View) 만들기
  4. MainWindowViewModel 만들기
  5. Model 객체와 User Control 연결하기(Binding)
  6. 실행 결과

 

1. MVVM 패턴 설정

Visual Studio를 실행하여 WPF Application으로

Creat Project를 하고 나면 아래 그림과 같이 나타나는데,

여기에서 MVVM 패턴(아직 자세히는 모르지만 기본적으로 Model, View, ViewModel을 따로 구분해주고, 각각의 영역을 서로 침범(?)하지 않도록 하는 패턴)으로 하기 위해

아래 그림처럼 프로젝트 파일명을 마우스 우 클릭하여

Add >> New Folder

하여 Model, View, ViewModel, MVVM 이라는 폴더4개를 각각 생성해준다.

폴더를 다 만들고 나면, 4개의 폴더가 아래와 같이 생성된다.

그러면 우선 MainWindow.xaml을 View 폴더로 Drag and Drop 방식으로 옮겨 준다.

그리고나서

아래와 같이 App.xaml 파일의 StartupUri의 값을 변경 시켜 주어야 한다.

MainWindow.xaml의 파일 경로가 바뀌었기 때문에...

기존 App.xaml

변경 App.xaml

위와 같이 변경한 후 실행하여 MainWindow가 보이는지를 확인해야 한다.

정상적으로 보인다면 App.xaml이 실행 될때

View 폴더 밑의 MainWindow.xaml이

정상적으로 연결이 되었다는 뜻이다.

2. Family class(Model) / BaseViewModel 만들기

그럼 다음으로

UI와의 데이터 바인딩을 위한 모델, Family class를 만들어 준다.

Family class는 아래 그림과 같이

4개의 속성(FirstName, LastName, FamilyMember, Address)을 가지고 있다.

해당 클래스는 Model 폴더를 마우스 우 클릭하여

Add >> class...

하여 만들 수 있다.

 

using WpfDataBinding.MVVM;

namespace WpfDataBinding.Model
{
    public class Family : BaseViewModel
    {
        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set {  
                _firstName = value;
                OnPropertyChanged();
            }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set {
                _lastName = value;
                OnPropertyChanged();
            }
        }

        private int _familyMember;
        public int FamilyMember
        {
            get { return _familyMember; }
            set {
                _familyMember = value;
                OnPropertyChanged();
            }
        }

        private string _address;
        public string Address
        {
            get { return _address; }
            set { 
                _address = value;
                OnPropertyChanged();
            }

        }
    }
}

 

그 다음으로는 INotifyPropertyChanged를 상속받는 BaseViewModel 클래스 정의 및

ICommand를 상속받는 RelayCommand라는 클래스를 하나 만든다.

이 두 클래스를 만드는 이유는 추후에 만들게 될 TextBox와 Button의 속성이

변경될때 발생할 이벤트를 처리기 위함이다.

해당 클래스는 MVVM 폴더를 마우스 우 클릭하여

Add >> class...

하여 만들 수 있다.

(INotifyPropertyChanged 및 ICommand에 대한 자세한 동작 설명은 추후에 진행 하도록 하겠다.)

 

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;

namespace WpfDataBinding.MVVM
{
    public class BaseViewModel : INotifyPropertyChanged
    {

        public event PropertyChangedEventHandler? PropertyChanged;
        

        public void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }

    public class RelayCommand : ICommand
    {
        Action<object> _execute;
        Func<object>? _canExecute;

        public RelayCommand() { }

        public RelayCommand(Action<object> execute)
        {
            _execute = execute;
            _canExecute = null;
        }

        public RelayCommand(Action<object> execute, Func<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }


        public bool CanExecute(object? parameter)
        {
            return true;
        }

        public void Execute(object? parameter)
        {
            _execute(parameter);
        }

        public event EventHandler? CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

    }
}

 

3. FamilyDisplay(View) 만들기

이제 User Control을 추가해 주자.

해당 클래스는 View 폴더를 마우스 우 클릭하여

Add >> User Control (WPF)...

하여 만들 수 있다.

이름은 마음대로 적어도 되며, 여기에서는 FamilyDisplay로 하였다.

만들어진 FamilyDisplay.xaml파일에 아래와 같이 작성해 준다.

총 3개의 Row와 4개의 Column을 설정하여 TextBlock, TextBox, Button을 만들어 주었다.

 

<UserControl x:Class="WpfDataBinding.View.FamilyDisplay"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfDataBinding.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="White">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40*"/>
            <RowDefinition Height="40*"/>
            <RowDefinition Height="30*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="25*"/>
            <ColumnDefinition Width="25*"/>
            <ColumnDefinition Width="25*"/>
            <ColumnDefinition Width="25*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" Text="First Name : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxFirstName" Grid.Row="0" Grid.Column="1"  Margin="5"/>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="Last Name : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxLastName" Grid.Row="0" Grid.Column="3"  Margin="5"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Family Member : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxFamilyMember" Grid.Row="1" Grid.Column="1"  Margin="5"/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="City : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxAddress" Grid.Row="1" Grid.Column="3"  Margin="5"/>
        <Button Grid.Row="2" Grid.ColumnSpan="4" Margin="20" Content="Show Family Info."
                FontSize="20" FontWeight="Bold"/>
    </Grid>
</UserControl>

만들어진 TextBox 및 Button은

현재까지는 MVVM 패턴에서 Model과 UI를 연결하는

ViewModel 객체와 아무런 바인딩(Binding)이

되어있지 않기 때문에

실행했을 때 TextBox에는 아무런 글자가 나타나지 않으며,

Button을 눌러도 아무런 동작을 하지 않는다.

 

using WpfDataBinding.ViewModel;

namespace WpfDataBinding
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            MainWindowViewModel vm = new MainWindowViewModel();
            this.DataContext = vm;
        }
    }
}

 

4. MainWindowViewModel 만들기

MVVM 패턴 방식을 사용하여 프로젝트를 만들 경우

대부분의 경우 MainWindowViewModel.cs 파일을 새로 만들어서 아래와 같이

기존의 MainWindow에서 DataContext를 통해

UI의 컨트롤과 Model의 속성을 연결 시켜줄 수 있다.

여기서 만든 MainWindowViewModel.cs 파일은

Solution Explorer에서 아래와 같이 ViewModel폴더에 만들어주면 된다.

그리고 나서

MainWindowViewModel에

TextBox, Button의 속성과 Binding을 하기 위한

객체를 선언해 준다.

 

public class MainWindowViewModel : RelayCommand
{
    public Family Family { get; set; } = new Family() {
      FirstName = "KilDong", LastName = "Hong", FamilyMember = 4, Address = "Seoul" };

    private ICommand _showFamilyInfo;
    public ICommand ShowFamilyInfo
    {
        get 
        {
            _showFamilyInfo = new RelayCommand(ShowFamilyInfoMessage);
            return _showFamilyInfo; 
        }
        set
        { 
            _showFamilyInfo = value; 
        }
        
    }

    public void ShowFamilyInfoMessage(object obj)
    {
        MessageBox.Show($"FirstName : {Family.FirstName}, LastName : {Family.LastName}\n 
                       FamilyMember : {Family.FamilyMember}, Address : {Family.Address}");
    }

}

위 코드 상에서 ShowFamilyInfo 객체는 Button과 Binding하기 위한 속성이며,

ICommand를 상속받은 RelayCommand class를 통해 생성 되면서,

파라미터 1개를 받는 생성자가 호출 될 것이다.

ICommand를 상속받는 RelayCommand 클래스는 EventHandler를 멤버로 가지고 있으며,

넘겨받은 ShowFamilyInfoMessage 메서드를

특정 이벤트 발생 시 실행 하게 된다.

(EventHandler에 대한 자세한 설명은 추후 좀 더 Study를 한 후에 포스팅 하도록 하겠다.)

 

5. Model 객체와 User Control 연결하기(Binding)

이제 MainWindow.xaml에 앞서 만든 UserContorl을

삽입해 보자.

UserControl을 MainWindow.xaml에 삽입하기 위해서는 아래와 같이 해주어야 한다.

우선, MainWindow.xaml에 해당 경로를 uc라는 이름으로

추가를 해준다.

StackPanel에 아래와 같이 삽입해 주면 된다.

(꼭 StackPanel 내에 삽입 할 필요는 없다.)

<uc:FamilyDisplay/>

 

위와 같이 하게 되면,

MainWindow.xaml이 아래와 같이 나타난다.

그럼 이제 마지막으로,

TextBox의 Text 속성과 Button의 Command 속성을

MainWindowViewModel에서 설정한

객체(Family, ShowFamilyInfo)와 Binding 시켜준다.

 

<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxFirstName" Grid.Row="0" Grid.Column="1"  Margin="5"
                 Text="{Binding Family.FirstName}"/>
        <TextBlock Grid.Row="0" Grid.Column="2" Text="Last Name : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxLastName" Grid.Row="0" Grid.Column="3"  Margin="5"
                 Text="{Binding Family.LastName}"/>
        <TextBlock Grid.Row="1" Grid.Column="0" Text="Family Member : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxFamilyMember" Grid.Row="1" Grid.Column="1"  Margin="5"
                 Text="{Binding Family.FamilyMember}"/>
        <TextBlock Grid.Row="1" Grid.Column="2" Text="City : " FontSize="15" FontWeight="Bold" 
                   VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox x:Name="txtBoxAddress" Grid.Row="1" Grid.Column="3"  Margin="5"
                 Text="{Binding Family.Address}"/>
        <Button Grid.Row="2" Grid.ColumnSpan="4" Margin="20" Content="Show Family Info."
                FontSize="20" FontWeight="Bold"
                Command="{Binding ShowFamilyInfo}"/>

 

여기에서 Binding 모드는 Default가 TwoWay 방식으로

TextBox에서 값을 바꾸면 Family 객체의 값도 바뀌며

반대로

Family 객체의 값이 바뀔 경우 TextBox의 값도 바뀌게 된다.

 

6. 실행 결과

실행하게 되면, 아래와 같은 창이 뜨고,

Button(Show Family Info.)을 누르면 아래와 같이

MessageBox 가 나타나며 Family 정보가

Display 된다.

여기에서 메시지 박스의 확인 버튼을 누르고,

MainWindow의 TextBox 내용을 변경 후 다시

Button을 누르면 바뀐 내용이 나타나는 것을

확인(Binding mode=TwoWay) 할 수 있다.

 

 

"

마치며...

C#으로 WPF를 이용하여 첫번째 프로젝트를 만들어 보았다.

아직 부족한 부분이 많다는 것을 많이 느낄 수 있었으며,

어떤 부분에 대한 공부를 더 해야 하는지 알게되어

계획을 세울 수 있게 되었다.

"

 

 

반응형
반응형

잇몸이 내려앉아서 여름에도 이가 시리거나 
잘 부어서 양치할때 피가 가끔 나시는
분들에게 추천할 만한 치약 소개드립니다.

 

잇몸이 건강하지 못하여 이 치약 저 치약

칫솔도 이것 저것 사다가 두~세달씩 써본

결과

치솔은 지극히 개취일거라

여기서 굳이 얘기할 필요는 없을 것 같고,

치약에 대해서

얘기 드려볼게요.

지금 쓰는 치약은 미국 암웨이에서 나온

glister라는 치약인데,

처음에는 치약 튜브에 써있는 plant-based라는 문구만 보고

오! 이거 괜찮을 것 같은데~!? 함 써볼까???

하는 마음으로 한번에 3개를 사서

써보았어여.

한개 사서 한 두달 써보고 좋다 나쁘다 판단하기

너무 짧은 것 같아서 3개를 사서

와이프와 같이 쓰게 되었는데,

...

결론적으로, 매우 만족하고 있어요.

와이프도 이 치약은 순한 것 같으면서도

저녁에 양치하고 난 후 아침에 일어났을 때의

입냄새 라던가 잇몸 질환,

예를 들어 피가 나거나 붓거나 하는 그런 것들이

거의 없다면서

다음에도 이 치약을 사서 쓰자고 했는데,

역시나 성분을 살펴보니

불소 함유량이 950ppm으로 꽤 높은 편이었어요.

민트향이어서 은은한 청량감도 주고

뚜껑도 원터치 방식이며,

치솔에 치약을 짜고 난 후 찌꺼기도 잘 안생겨서 좋아요.

현재 집에서 반정도 쓴 glister치약

역쉬나, 애초에 뚜껑에 묻는 치약이 없어

처음 상태 그대로 치약 뚜껑이 깨끗한 상태네요.

뚜껑이 분리되는 타입이면, 열고 닫기가 여간 귀찮은 일이

아닐 수 없는데,

그런 부분도 아주 편해요.

 


암웨이 glister 치약, 치아 미백과 시린이 그리고 잇몸 건강에

신경쓰이신다면 강추 드립니다.

암웨이 글리스터 프로액션 치약 200g 3개 + 여행용치약 1개





"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."

반응형

+ Recent posts