레이블이 #WPF강좌인 게시물을 표시합니다. 모든 게시물 표시
레이블이 #WPF강좌인 게시물을 표시합니다. 모든 게시물 표시

2021년 11월 7일 일요일

WPF 데이터 바인딩(Data Binding)

6.1 데이터 바인딩(Data Binding)

  • 데이터 바인딩이란 컨트롤과 엘리먼트를 데이터에 연결시키는 기술이다. 데이터 바인딩은 CheckBox 컨트롤을 Boolean 변수에 연결하는 것처럼 간단할 수도 있고 복잡할 수도 있다.

  • 컨트롤은 데이터를 사용자에게 보여주는 것과 사용자가 데이터를 변경할 수 있게 해주는 두 가지 기능을 제공한다. 최근 컨트롤과 데이터 사이의 많은 반복 작업들이 단순화 되면서 CheckBox를 Boolean 변수로 초기화하고 사용자가 작업이 끝난 후 Boolean 변수를 다시 CheckBox 값으로 초기화하는 코드들을 만들어야 했는데 CheckBox와 변수 사이의 바인딩을 이용하게 간단하게 처리할 수 있다.

  • 대부분의 경우 데이터 바인딩은 이벤트 핸들러를 대체할 수 있는데 이는 C#코드를 줄이는 역할을 한다. XAML에서 정의된 데이터 바인딩은 C# 코드 비하인드 파일에서 이벤트 핸들러를 정의할 필요가 없으며 코드 비하인드 파일 자체가 필요없는 경우도 있다.

  • 데이터 바인딩은 소스와 타겟이 필요하다. 일반적으로 소스는 데이터이고 타겟은 컨트롤이다. 하지만 어느 경우엔 소스와 타겟의 구분이 모호할 때도 있다. 어떤 경우에는 반대로 타겟이 소스에게 데이터를 전달하기도 한다,

  • 가장 간단한 바인딩은 두 개의 컨트롤 사이에서 존재하는데 스크롤바의 Value 프로퍼티를 보여주기 위한 Label 컨트롤을 생각하면 스크롤바의 ValueChanged 이벤트 핸들러를 이용하거나 다음 Stand Alone XAML 파일과 같이 데이터 바인딩을 정의할 수 있다.

  • MainWindow.xaml

<Window x:Class="BindingLabelToScroll.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        

        Title="Label, ScrollBar 데이터 바인딩" Height="350" Width="525">

    <StackPanel >

 

        <!-- Source. -->

        <ScrollBar Name="scrollbar"

               Orientation="Horizontal" Margin="24" 

               Maximum="100" LargeChange="10" SmallChange="1" />

 

        <!-- Target. -->

        <Label HorizontalAlignment="Center" 

           Content="{Binding ElementName=scrollbar, Path=Value}" />

    </StackPanel>

</Window>

XjT-QdWDKLCDScPH2LyfHVyX4JE5Hi1MiCX3lZ48

  • 바인딩 자체는 언제나 타겟에 설정한다. Label 컨트롤의 Content 프로퍼티에 다음과 같이 설정되었다.

<Label HorizontalAlignment="Center" Content="{Binding ElementName=scrollbar, Path=Value}" />

Binding 키워드는 마크업 확장으로 중괄호안에 Binding 키워드가 있다. Binding의 프로퍼티 중 ElementName에는 ScrollBar의 Name 속성에 정의된 이름이 사용되었고 Path 프로퍼티에는 ScrollBar의 Value 프로퍼티로 설정되었다.

  • 위 XAML에서 바인딩 정의 내에 따옴표를 사용하고 싶다면 아래과 같이 사용하면 된다.

<!-- Target. -->

<Label HorizontalAlignment="Center">

    <Label.Content>

        <Binding ElementName="scrollbar" Path="Value"/>

    </Label.Content>

</Label>

  • 바인딩 정의가 있는 컨트롤은 항상 타겟이다. 바인딩 타겟은 DependencyObject로부터 상속되며 바인딩이 설정되는 프로퍼티는 반드시 의존 프로퍼티의 지원을 받아야 한다. 따라서 이 경우에 Label은 DependencyProperty 타입의 Public static 필드인 ContentProperty가 필요하다. (당연히 존재한다. 바인딩 소스는 반드시 의존 프로퍼티 일 필요는 없다.)

  • 프로퍼티는 프로퍼티가 변경 될 때 알려주는 이벤트와 연결되어 있는 것이 이상적이다. 하지만 몇몇 바인딩은 통지 이벤트 없이도 동작한다.

  • 바인딩을 C#코드로 구현한 예문을 보면 명확히 알수 있는데 아래 코드를 보자.

  • MainWindow.xaml

<Window x:Class="BindingLabelToScroll2.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        

        Title="Label, ScrollBar 데이터 바인딩" Height="350" Width="525">

    <StackPanel >

 

        <!-- Source. -->

        <ScrollBar Name="scrollbar"

               Orientation="Horizontal" Margin="24" 

               Maximum="100" LargeChange="10" SmallChange="1" />

 

        <!-- Target. -->

        <Label HorizontalAlignment="Center" Name="label"/>

    </StackPanel>

</Window>

  • MainWindow.xaml.cs

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Controls.Primitives;

 

namespace BindingLabelToScroll2

{

public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

 

            Binding bind = new Binding();

            bind.Source = scrollbar;

            bind.Path = new PropertyPath(ScrollBar.ValueProperty);

            label.SetBinding(Label.ContentProperty, bind);

        }

    }

}


  • 소스와 타겟의 의미는 소스 엘리먼트에서의 변화를 타겟 엘리먼트에 반영되도록 하는 의미를 담고 있다. 시실 이것은 바인딩으로 가능한 4가지 모드 중 하나일 뿐이다. 바인딩 모드는 Mode 프로퍼티에 BindingMode 열거형 멤버로 설정할 수 있다.

Content = “{Binding ElementName=scrollbar, Path=Value, Mode=OneWay}”

 

<Label.Content>

<Binding ElementName=”scrollbar” Path=”Value” Mode=”OneWay”/>

</Label.Content>

또한 Mode는 TwoWay로 설정할 수 있는데 Label의 Content 프로퍼티의 변화도 scrollbar의 Value 프로퍼티에 반영된다. 

OneTime 모드도 있는데 타겟이 소스로부터 초기화 되지만 소스의 변화가 계속 반영되지 않고 초기 한 번만 반영된다.

OneWayToSource 모드는 소스, 타겟의 의미와 반대가 되도록 타겟이 소스를 갱신하는 모양이다. 본 예제의 경우 Label은 ScrollBar에 넘겨줄 숫자 데이터가 없기에 Label은 비어 있고 ScrollBAr를 움직여도 반응이 없는 형태가 된다.

 

  • 이번에는 Label과 ScrollBar의 역할을 바꾸어 테스트 해보자.

  • 프로젝트 : BindingScrollBarToLabel

  • MainWindow.xaml

<Window x:Class="BindingScrollBarToLabel.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:BindingScrollBarToLabel"

        mc:Ignorable="d"

        Title="MainWindow" Height="350" Width="525">

    <StackPanel>

        <!-- Binding Target. -->

        <ScrollBar Orientation="Horizontal" Margin="24" 

               Maximum="100" LargeChange="10" SmallChange="1"

               Value="{Binding ElementName=lbl, Path=Content}" />

 

        <!-- Binding Source. -->

        <Label Name="lbl" Content="50"

           HorizontalAlignment="Center" />

    </StackPanel>

</Window>

H93O2sR3MSDBHhJjM73XuCrtEqneIYJ8ttfkn9YG

  • Label이 소스가 되고 ScrollBar가 타겟이 되었는데 Label의 Content를 “50”으로 설정했으므로 최초 ScrollBar의 Value 프로퍼티에 반영이 되어 스크롤바가 가운데에 위치한다. 스크롤바를 움직이면 라벨이 갱신되는 TwoWay 바인딩이 기본적으로 설정되어 있다. 만약 여기에서 Mode를 OneWay로 설정하면 스크롤바를 움직여도 라벨의 Content 프로퍼티는 갱신되지 않을 것이다. 테스트 해보자.

Mode를 OneWayToSource로 하면 동작한다. 이것은 소스와 타겟을 바꾸어서 동작하기에 그렇다. 라벨은 초기값이 50, 스크롤바는 초기값이 0이므로 스크롤바의 초기값 0이 라벨에 할당되고 이 후 스크롤바를 움직이면 라벨이 갱신된다.

  • 다음 예제는 클라이언트 영역의 너비와 높이를 표시하기 위해 데이터 바인딩을 사용한 예제이다.

  • 프로젝트명 : WidthHeightDisplay

  • MainWindow.xaml

<Window x:Class="WidthHeightDisplay.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:WidthHeightDisplay"

        mc:Ignorable="d"

        Title="MainWindow" Height="350" Width="525">

    <Grid Name="grid">

        <StackPanel Orientation="Horizontal"

                HorizontalAlignment="Center"

                VerticalAlignment="Center">

            <TextBlock Text="{Binding ElementName=grid, Path=ActualWidth}" />

            <TextBlock Text=" &#x00D7; " />

            <TextBlock Text="{Binding ElementName=grid, Path=ActualHeight}" />

            <TextBlock Text=" device independent units" />

        </StackPanel>

    </Grid>

</Window>

mrRYzts9S7WnTTZKyjjbMTmH1s-Id3hq22wFVIZV

  • 2개의 TextBlock은 Grid의 ActualWith, ActualHeight 프로퍼티와 바인딩 되었는데 이 두 속성은 읽기전용이므로 OneWay로 바인딩 된다.

  • TextBlock의 Text 프로퍼티는 TextProperty라는 의존 프로퍼티에 의해 지원되는데 바인딩에서 타겟은 반드시 의존 프로퍼티가 되어야 된다”는 사실을 명심하자. C#코드를 통해 바인딩 시켜보면 명확해지는데 SetBinding의 첫 번째 인자는 DependencyProperty 타입이 되어야 한다.


6.1.1 DataContext를 이용한 데이터 바인딩

  • 바인딩 소스객체를 명시하는 또 다른 방법이 있는데 DataContext를 이용하면 된다.

  • MainWindow.xaml

<Window x:Class="BindingWithDataContext.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        

        Title="MainWindow" Height="350" Width="525">

    <StackPanel>

        <!-- Binding Source. -->

        <ScrollBar Name="scroll"

               Orientation="Horizontal" Margin="24" 

               Maximum="100" LargeChange="10" SmallChange="1" />

 

        <!-- Binding Target. -->

        <Label HorizontalAlignment="Center"

           DataContext="{Binding ElementName=scroll}"

           Content="{Binding Path=Value}" />

    </StackPanel>

</Window>

V3CSfT0ElwsvZb5o3ABjYVTfB6K-2QNinb_Y3Duf

  • DataContext와 Label의 Content 속성 모두에 두 개로 분리하여 바인딩을 정의했는데 첫 번째 DataContext의 바인딩 정의는 ElementName을 명시하고 두 번째 Content의 바인딩 정의는 스크롤바의 Value 프로퍼티를 명시한다.

  • 이 예제에서는 DataContext를 사용한 이점은 없지만 DataContext 프로퍼티가 엘리먼트 트리를 통해 상속되기에 하나의 엘리먼트에 DataContext를 이용하여 바인딩 시키면 모든 자식 엘리먼트에 동일하게 적용되는 장점이 있다.

  • 다른 예제를 작성해 보자. 이 예제에서는 DataContext는 StackPanel에 한 번 설정되었고 Label과 Button은 ScrollBar와 바인딩 되었다. Label에 바인딩 된 프로퍼티는 Content이지만 Button에 바인딩 된것은 FontSize이므로 ScrollBar를 움직이면 Button 내부의 텍스트가 점점 커지고 Button도 커진다.

  • 프로젝트명 : BindingTwoControll

  • MainWindow.xaml

<Window x:Class="BindingTwoControll.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        

        Title="MainWindow" Height="350" Width="525">

    <StackPanel DataContext="{Binding ElementName=scrollbar}">

        <!-- Binding Source. -->

        <ScrollBar Name="scrollbar"

               Orientation="Horizontal" Margin="24" 

               Minimum="1" Maximum="100" LargeChange="10" SmallChange="1" />

 

        <!-- Binding Targets. -->

        <Label HorizontalAlignment="Center"

           Content="{Binding Path=Value, Mode=OneWay}" />

 

        <Button HorizontalAlignment="Center" Margin="24"

            FontSize="{Binding Path=Value, Mode=OneWay}">

            Bound Button

        </Button>

    </StackPanel>

</Window>

gYUmKics3hVQtfhfza3MJ8wlBLyWElj8NSSUy8s8


6.1.2 사용자정의 엘리먼트와 ScrollBar 데이터 바인딩

  • 비주얼 스튜디오 -> WPF 응용프로그램 -> 프로젝트명 : CustomElementBiding

  • MyElement.cs(사용자 정의 Element)

using System;

using System.Globalization;

using System.Windows;

using System.Windows.Media;

 

namespace CustomElementBiding

{

    /// //////////////////////////////////////////////////////////////

    // Number라는 double 타입의 프로퍼티를 정의하고

    // 이 프로퍼티는 NumberProperty라는 의존 프로퍼티의 지원을 받는다

    // FrameworkPropertyMetadata는 초기값 이이며, 프로퍼티에 변화가 생기면 

    // OnRender를 호출하여 화면을 갱신(Number 프로퍼티를 노출)한다.

    //////////////////////////////////////////////////////////////////

    public class MyElement : FrameworkElement

    {

        // 의존 프로퍼티 선언

        // 데이터 바인딩 통보 메커니즘은 이를 통해 저절로 확보된다.

        public static DependencyProperty NumberProperty;

 

        // static 생성자를 통한 의존 프로퍼티 생성

        static MyElement()

        {

            NumberProperty =

                DependencyProperty.Register("Number", typeof(double),

                                            typeof(MyElement),

                    new FrameworkPropertyMetadata(0.0,

                            FrameworkPropertyMetadataOptions.AffectsRender));

        }

         

        // DependencyProperty를 CLR 프로퍼티로 노출

        public double Number

        {

            set { SetValue(NumberProperty, value); }

            get { return (double)GetValue(NumberProperty); }

        }

 

        // MeasureOverride를 오버라이딩해 Size를 리턴하는데 원래 이함수는 자식

        // 엘리먼트를 톨면서 다식컨트롤의 크기를 계산하고 업데이트하는 메소드

        // 이함수를 재정의 한하면 자식 컨트롤의 크기가 이상해 질 수 있다.

        // MyElement의 크기를 측정해서 FrameworkElement 파생클래스(MyElement)

        // 의 Size를 리턴하는 것이다.레이아웃을 만들 때 자동으로 호출된다.

        protected override Size MeasureOverride(Size sizeAvailable)

        {

            return new Size(200, 50);

        }

 

        // Number 프로퍼티를 보여주는 OnRender

        protected override void OnRender(DrawingContext dc)

        {

            dc.DrawText(

                new FormattedText(Number.ToString(),

                        CultureInfo.CurrentCulture, FlowDirection.LeftToRight,

                        new Typeface("Times New Roman"), 12,

                        SystemColors.WindowTextBrush),

                new Point(0, 0));

        }

    }

}


  • MainWindow.xaml

<Window x:Class="CustomElementBiding.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:src="clr-namespace:CustomElementBiding"

        Title="Custom Element Binding" Height="350" Width="525">

    <StackPanel>

        <!-- 처음 스크롤바는 MyElement의 Number 프로퍼티와 OneWayToSource 바인딩을 하므로 스크롤바의 값이 바뀌면 MyElement의 Number 속성도 바뀐다. 이때 아래쪽은 두번째 스크롤바의 Value 프로퍼티와 MyElement가 TwoWay로 바인딩 되었으므로 두번째 스크롤바도 같이 움직인다.-->

        <ScrollBar Orientation="Horizontal"

                   Margin="24" 

                   Maximum="100"

                   LargeChange="10"

                   SmallChange="1"  

                   Value="{Binding ElementName=simple, Path=Number, 

                                   Mode=OneWayToSource}" />

 

        <!-- 

        x:Name 속성은 FrameworkElement를 성속받지 않은 XAML 엘리먼트를 위한

        속성이다. MyElement가 동일 어셈블리상이므로 Name대신 x:Name사용

        -->

        <src:MyElement x:Name="simple"

                       HorizontalAlignment="Center" />

 

        <!-- 두번째 스크롤바를 움직이면 MyElement의 Number 속성도 바뀌지만

             이 값이 바뀐다고 이것이 첫번째 스크롤바와는 바인딩 되지 않으므로

             첫번째 스크롤바는 그대로 있다. -->             

        <ScrollBar Name="scroll"

               Orientation="Horizontal"

               Margin="24" 

               Maximum="100"

               LargeChange="10"

               SmallChange="1" 

               Value="{Binding ElementName=simple, Path=Number, 

                               Mode=TwoWay}" />

 

        <src:MyElement HorizontalAlignment="Center" 

                       Number="{Binding ElementName=scroll, Path=Value,

                                        Mode=OneWay}" />

    </StackPanel>

</Window>

  • 실행 결과

p_-8S1Ri5JORxo9uBJFNMKriZWSYPNjLy9DMopW4

   

6.1.3 x:Static, Source를 통한 데이터 바인딩(DateTimeFormatInfo를 이용한 요일정보를 ListBox, TextBlock

  • ElementName으로 바인딩의 소스를 명시하는 것 대신 Binding 클래스의 Source 프로퍼티를 사용하여 바인딩 하는 것이 가능한데 Source 프로퍼티는 객체를 참조하고 Path는 객체의 프로퍼티를 참조한다.

  • Source를 사용하기 위한 한 가지 방법은 x:Static 마크업을 확장해야 되는데 x:StaticXAML에서 클래스 내의 정적필드나 프로퍼티를 참조할 수 있게 한다.

  • System.Globalization 네임스페이스에 있는 DateTimeFormatInfo 클래스의 DayName 프로퍼티는 요일에 해당하는 문자열 배열을 반환하는 인스턴스 프로퍼티다. DateTimeFormatInfo 클래스는 두 개의 정적 프로퍼티를 제공하는데 InvariantInfo는 변함 없는 문화권을 지원하며 CurrentInfo는 사용자 자신의 현재 문화권을 지원한다.

C#에서는 다음과 같이 요일에 해당하는 문자열 배열을 현재 사용자의 문화권으로 지정한다.

string[] dayNames = DateTimeFormatInfo.CurrentInfo.DayNames;

  • MainWindow.xaml

<Window x:Class="DayNames.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:g="clr-namespace:System.Globalization;assembly=mscorlib"

        Title="MainWindow" Height="350" Width="525">

    <StackPanel>

 

        <!-- 한번만 바인딩 -->

        <ListBox Name="lstbox"

             HorizontalAlignment="Center"

             Margin="24"

             ItemsSource="{Binding 

                            Source={x:Static g:DateTimeFormatInfo.CurrentInfo},

                            Path=DayNames,

                            Mode=OneTime}" />

 

       <!-- 리스트박스의 SelectedItem 속성과 TextBlock의 Text속성을 바인딩 -->

        <TextBlock HorizontalAlignment="Center"

               Text="{Binding ElementName=lstbox, 

                                    Path=SelectedItem, Mode=OneWay}" />

    </StackPanel>

</Window>


  • 실행결과

7PMy_0xoPYk_lPeVgOAV_ZZyloTSYLq7kf077pF8

6.1.4 Source, SourceResource를 통한 데이터 바인딩

  • FrameworkElement를 상속받지 않은 객체를 바인딩 하기 위해서는 Source를 사용하여 데이터 바인딩을 할 수 있는데 마크업의 Resource 절에 객체를 정의하고 StaticResource로 접근하면 된다.

<Window>

<Window.Resources>

       <src:ClockTicker1 x:Key="clock" />

</Window.Resources>

<Window.Content>

       <Binding Source="{StaticResource clock}" Path="DateTime" />

</Window.Content>

</Window>

Resource 절에서 ClockTicker1을 정의하고 clock 이라는 key를 정의했고 이 키를 이용하여 Binding 절에서 Source, StaticResource를 사용하여 지정하고 ColckTicker1의 DateTime 프로퍼티와 바인딩 시켰다.

  • 전자시계를 만들어 보는데 현재 시각이 변했을 때 통지하는 메커니즘이 필요할 것이다.

  • DigitalClock 이라는 이름으로 WPF 응용프로그램 프로젝트를 생성하고 MainWindow.xaml.cs 파일을 삭제 하자.

  • ClockTicker1.cs

//---------------------------------------------

// ClockTicker1.cs (c) by Charles Petzold

//---------------------------------------------

using System;

using System.Windows;

using System.Windows.Threading;

 

namespace DigitalClock

{

    public class ClockTicker1 : DependencyObject

    {

        // 의존 프로퍼티 선언

        public static DependencyProperty DateTimeProperty =

                DependencyProperty.Register("DateTime", typeof(DateTime),

                                            typeof(ClockTicker1));

 

        // CLR property로 의존 프로퍼티를 노출

        public DateTime DateTime

        {

            set { SetValue(DateTimeProperty, value); }

            get { return (DateTime)GetValue(DateTimeProperty); }

        }

 

        // 생성자.

        public ClockTicker1()

        {

            DispatcherTimer timer = new DispatcherTimer();

            timer.Tick += TimerOnTick;

            timer.Interval = TimeSpan.FromSeconds(0.1);  //1초에 한번씩

            timer.Start();

        }

 

        // DateTime property 값을 설정하기 위한 Timer 이벤트 핸들러

        void TimerOnTick(object sender, EventArgs args)

        {

            DateTime = DateTime.Now;

        }

    }

}


  • MainWindow.xaml

<!-- =====================================================

      DigitalClockWindow.xaml (c) by Charles Petzold

     ===================================================== -->

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 

        xmlns:src="clr-namespace:DigitalClock"

        Title="Digital Clock"

        SizeToContent="WidthAndHeight"

        ResizeMode="CanMinimize"

        FontFamily="Bookman Old Style" 

        FontSize="36pt">

    <Window.Resources>

        <src:ClockTicker1 x:Key="clock"/>

    </Window.Resources>

    <Window.Content>

        <Binding Source="{StaticResource clock}" Path="DateTime" />

    </Window.Content>

</Window>

  • 실행결과

w6Ls3FfWir-dMm4SRw0UaA42VTTquU7msxaOBUta


6.1.5 PropertyChanged이벤트, PropertyChangedEventHandler를 통한 데이터 바인딩

  • 의존 프로퍼티를 정의하는 것은 데이터 바인딩의 소스로서 역할을 수행하는데 이 방법이 유일한 방법은 아니다. 전통적인 방법은 이벤트를 만드는 것인데 이벤트를 특정한 방법으로 정의하면 WPF내의 데이터 바인딩 로직이 이벤트를 사용할 수 있다.

  • 이벤트를 데이터 바인딩의 소스로 사용하기 위해 DateTime 프로퍼티를 선언했다면 DateTimeChange라는 이름으로 이벤트를 정의하면 된다. 

  • 이전 소스코드를 수정해 보자

  • 의존 프로퍼티인 DateTimeProperty 선언부를 삭제하고 DateTimeChange라는 public 이벤트를 정의하자.

public event EventHandler DateTimeChange;

  • DateTime 프로퍼티를 아래와 같이 수정하자.

public DateTime {

get { return DateTime.Now; }

  • TimerOnTick 메소드를 수정하자.

void TimerOnTick(object sender, EventArgs e) {

   //DateTimeChanged 이벤트에 가입자가 있으면

if (DateTimeChanged != null) DateTimeChanged(this, new EventArgs());

}

  • WPF의 데이터 바인딩 로직은 이벤트가 InotifyPropertyChanged 인터페이스를 구현한 클래스안에 정의되어 있으면 이벤트를 찾는다. 이 인터페이스는 클래스에게 PropertyChangedEventHandler 델리게이트를 기본으로 하는 PropertyChanged 이벤트를 정의할 것을 요구한다.

public event PropertyChangedEventHandler PropertyChanged;

  • PropertyChanged 이벤트를 발생시킬 때의 처음인자는 this이며 두번째 인자는 PropertyChangedEventArgs타입의 객체이다. PropertyChangedEventArgs는 EventArgs를 상속받아 추가적인 string 타입의 PropertyName 이라는 프로퍼티를 정의하는데 이 프로퍼티는 변경될 프로퍼티를 구별하기 위해 사용한다. 또한 PropertyChangedEventArgs는 string 인자를 가진 생성자를 통해 PropertyName을 설정한다.

  • 클래스에서 DateTime 이라는 프로퍼티를 정의했다면 DateTime이 변경될 때 PropertyChanged 이벤트가 발생되어야 하는데 아래 코드를 통해 가능하다.

PropertyChanged(this, new PropertyChangedEventArgs(“DateTime”));

  • 이 방법이 변경 가능한 다수의 프로퍼티를 다루는 좋은 방법이다. 하나의 PropertyChanged 이벤트가 이들을 다 처리하기 때문이다. 아래는 전체 소스코드이니 ClockTicker2.cs를 만들고 MainWindow.xaml를 수정해서 확인해 보자.

  • ColckTicker2.cs

using System;

using System.Windows;

using System.Windows.Threading;

using System.ComponentModel;

 

namespace DigitalClock

{

    public class ClockTicker2 : INotifyPropertyChanged

    {

        // INotyfyPropertyChanged 인터페이스가 요구하는 이벤트

        public event PropertyChangedEventHandler PropertyChanged;

 

        // public 프로퍼티

        public DateTime DateTime

        {

            get { return DateTime.Now;  }

        }

 

        // 생성자에서 Timer를 설정

        public ClockTicker2()

        {

            DispatcherTimer timer = new DispatcherTimer();

            timer.Tick += TimerOnTick;

            timer.Interval = TimeSpan.FromSeconds(0.1);

            timer.Start();

        }

 

        // PropertyChanged 이벤트를 발생시키는 타이버 이벤트 핸들러

        void TimerOnTick(object sender, EventArgs args)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs("DateTime"));

            }

        }

    }

}


6.1.6 MVVM(Model-View-ViewModel) 이란?

  • Model : 비즈니스 로직과 데이터를 캡슐화 한것이다.

  • View : 사용자가 보게 되는 화면을 정의하는 것이다.

  • ViewModel : View를 나타내주기 위한 Model이며  프리젠테이션 로직과 뷰를 위한 데이터를 캡슐화하고 있다. 뷰와 뷰가 요구하는 모델 클래스들 간의 상호작용을 조직할 책임이 있으며 뷰가 표시하는 데이터와 Command를 구현한 것이다. View보다는 Model과 유사하게 디자인 되며, View의 바인딩 될 때 가장 강력하다. 

  • Model-View-ViewModel(MVVM) 패턴에 기반하여 XAML을 탄생시켰으며 기본 패턴은 View와 Model 이다(즉 ViewModel). XAML은 사용자 인터페이스와 (*.xaml) 기본데이터(Model)와의 연결은 코드 비하인드 C#(*.cs)으로 가능하도록 완벽한 분리를 해놓았다. View와 ViewModel은 XAML XML파일의 Data Binding 정의를 통해 연결될 수도 있으며 View에 대한 BindingContext는 일반적으로 ViewModel의 인스턴스 이다.

  • Model-View-ViewModel(MVVM)



vWow32xoysMiSO5-HWpgv7IgKJh7_M0qz15g89Np


Model : 비즈니스 로직과 데이터를 캡슐화 한것이다.

View : 사용자가 보게 되는 화면을 정의하는 것이다.

ViewModel : View를 나타내주기 위한 Model이며  프리젠테이션 로직과 뷰를 위한 데이터를 캡슐화하고 있다. 뷰와 뷰가 요구하는 모델 클래스들 간의 상호작용을 조직할 책임이 있으며 뷰가 표시하는 데이터와 Command를 구현한 것이다. View보다는 Model과 유사하게 디자인 되며, View의 바인딩 될 때 가장 강력하다. ViewModel의 Property에 뷰가 바인딩 된다.


  • Model-View-ViewModel(MVVM) 패턴에 기반하여 XAML을 탄생시켰으며 기본 패턴은 View와 Model 이다(즉 ViewModel). XAML은 사용자 인터페이스와 (*.xaml) 기본데이터(Model)와의 연결은 코드 비하인드 C#(*.cs)으로 가능하도록 완벽한 분리를 해놓았다. View와 ViewModel은 XAML XML파일의 Data Binding 정의를 통해 연결될 수도 있으며 View에 대한 BindingContext는 일반적으로 ViewModel의 인스턴스 이다.



6.1.7 데이터 바인딩을 심플 예제

  • View에는 Buttonm Code Behind가 포함되어 있고 ViewModel에 바인딩 되어 있는 예제이다. ViewModel 객체가 View의 DataContext로 설정되어 있으므로 View와 ViewModel의 바인딩은 단순하게 구성되어 있다.

  • ViewModel의 속성 값이 변경되면 자동으로 데이터 바인딩을 통해 View로 전파되고 사용자가 View에서 Button을 클릭하면 ViewModel의 명령이 실행되어 요청한 작업을 수행한다.


3q0DnSuB0rphDHh16vX5pdbWJ93Y_dMmPXmv0jJB


  • 1. View(MainWindow.xaml)

<Window x:Class="MVVMTest.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:MVVMTest"

        mc:Ignorable="d"

        Title="MainWindow" Height="150" Width="370">

    <Window.DataContext>

        <local:MainWindowViewModel/>

    </Window.DataContext>

    <Grid>

        <Button Content="Click" 

                Height="23" 

                HorizontalAlignment="Left" 

                Margin="77,45,0,0" 

                Name="btnClick" 

                VerticalAlignment="Top" 

                Width="203"

                Command="{Binding ButtonCommand}" 

                CommandParameter="Hai" />

    </Grid>

</Window>


  • 2. ViewModel(C# 클래스)- ViewModel에서 RelayCommand를 통해 메시지박스를 띄움

using System;

using System.Windows.Input;

using System.Windows;

 

namespace MVVMTest

{

    class MainWindowViewModel

    {

        private ICommand m_ButtonCommand;

        public ICommand ButtonCommand

        {

            get

            {

                return m_ButtonCommand;

            }

            set

            {

                m_ButtonCommand = value;

            }

        }

 

        public MainWindowViewModel()

        {

            ButtonCommand = new RelayCommand(new Action<object>(ShowMessage));

        }

 

        public void ShowMessage(object obj)

        {

            MessageBox.Show(obj.ToString());

        }

    }

}



  • ICommand를 상속받은 RelayCommand.cs

using System;

using System.Windows.Input;

 

namespace MVVMTest

{

    class RelayCommand : ICommand

    {

        private Action<object> _action;

 

        public RelayCommand(Action<object> action)

        {

            _action = action;

        }

 

        #region ICommand Members

 

        public bool CanExecute(object parameter)

        {

            return true;

        }

 

        public event EventHandler CanExecuteChanged;

 

        public void Execute(object parameter)

        {

            if (parameter != null)

            {

                _action(parameter);

            }

            else

            {

                _action("Hello World");

            }

        }

        #endregion

    }

}


6.1.8 INotifyPropertyChanged 인테페이스를 이용한 데이터 바인딩 예제

  • INotifyPropertyChanged 인터페이스는 속성 값이 변경되면 클라이언트에 알리는 데 사용된다. INotifyPropertyChanged 인터페이스에는 PropertyChanged라는 이벤트가 포함되어 있는데 ViewModel / Model 개체의 속성이 변경되면 PropertyChanged 이벤트를 발생시켜 WPF 바인딩 시스템에 새 값을 알려준다. 

ZzSF4CdU8lRaC2ra0iA22dZRpT7bG4ZU7QlFc0mi


  • INotifyTest 라는 이름으로 WPF 프로젝트 생성

  • Person.cs

using System.ComponentModel;

namespace INotifyTest{

    public class Person : INotifyPropertyChanged    {

        private string name;

        // Declare the event 

        public event PropertyChangedEventHandler PropertyChanged;

        public Person() { }

        public Person(string value) { this.name = value; }

        public string PersonName

        {

            get { return name; }

            set

            {

                name = value;

                // Call OnPropertyChanged whenever the property is updated 

                OnPropertyChanged("PersonName");

            }

        }

        // Create the OnPropertyChanged method to raise the event 

        protected void OnPropertyChanged(string name)

        {

            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));

        }

    }

}


  • MainWindow.xaml

<Window x:Class="INotifyTest.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:INotifyTest"

        xmlns:src="clr-namespace:INotifyTest"

        mc:Ignorable="d"

        Title="MainWindow" Height="350" Width="525">

    <!--<SnippetBindingSource>-->

    <Window.Resources>

        <src:Person x:Key="myDataSource" PersonName="홍길동"/>

        <!--</SnippetInstantiation>-->

        <Style TargetType="{x:Type Label}">

            <Setter Property="DockPanel.Dock" Value="Top"/>

            <Setter Property="FontSize" Value="12"/>

        </Style>

        <Style TargetType="{x:Type TextBox}">

            <Setter Property="Width" Value="100"/>

            <Setter Property="Height" Value="25"/>

            <Setter Property="DockPanel.Dock" Value="Top"/>

        </Style>

        <Style TargetType="{x:Type TextBlock}">

            <Setter Property="Width" Value="100"/>

            <Setter Property="Height" Value="25"/>

            <Setter Property="DockPanel.Dock" Value="Top"/>

            <Setter Property="Padding" Value="3"/>

        </Style>

        <!--<Snippet2>-->

    </Window.Resources>

    <!--</Snippet2>-->

    <Border Margin="5" BorderBrush="Aqua" BorderThickness="1" Padding="8" CornerRadius="3">

        <DockPanel Width="200" Height="100" Margin="35">

            <!-- <Snippet1> -->

            <Label>Enter a Name:</Label>

            <TextBox>

                <TextBox.Text>

                    <Binding Source="{StaticResource myDataSource}" Path="PersonName" UpdateSourceTrigger="PropertyChanged"/>

                </TextBox.Text>

            </TextBox>

            <!-- </Snippet1> -->

            <!--</SnippetBindingSource>-->

            <Label>The name you entered:</Label>

            <!--<SnippetBDO1>-->

            <TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>

            <!--</SnippetBDO1>-->

        </DockPanel>

    </Border>

    <!--<SnippetEndWindow>-->

</Window>


6.1.7 데이터 바인딩을 이용한 계산기 실습(MVVM, ViewModel)

  • 지금까지 배운 데이터 바인딩 및 MVVM을 이용하여 간단한 계산기를 구현해 보자.

  • MainWindow.xaml

<Window x:Class="CalculatorNew.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:local="clr-namespace:Calc"

        Title="Topcredu.co.kr WPF Calculator" Height="350" Width="425">

 

    <Window.Resources>

        <local:CalcViewModel x:Key="calcViewModel"/>

    </Window.Resources>

    

    <Grid HorizontalAlignment="Center"

        VerticalAlignment="Center">

 

        <!-- Grid의 행과 열을 정의 -->

        <Grid.RowDefinitions>

            <RowDefinition Height="60" />

            <RowDefinition Height="60" />

            <RowDefinition Height="60" />

            <RowDefinition Height="60" />

        </Grid.RowDefinitions>

 

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="60" />

            <ColumnDefinition Width="60" />

            <ColumnDefinition Width="60" />

            <ColumnDefinition Width="60" />

        </Grid.ColumnDefinitions>

        

        <!-- 최상단 행을 위한 내부 그리드 정의, 출력텍스트박스, BACK버튼, Clear버튼 -->

        <Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="120" />

                <ColumnDefinition Width="50*" />

                <ColumnDefinition Width="50*" />

            </Grid.ColumnDefinitions>

 

            <!-- ViewModel 클래스의 DisplayText 속성과 바인딩 -->

            <Border BorderThickness="1" BorderBrush="Black">

                <TextBlock FontSize="15" VerticalAlignment="Center"

                           HorizontalAlignment="Center"                           

                       Text="{Binding DisplayText,Source={StaticResource calcViewModel}}" />

            </Border>

 

 

            <!-- 버튼의 Command 이벤트에 Command명을 지정하고 바인딩-->

            <!-- ViewModel 클래스에서 DeleteCharCommand 속성이 정의되야 하고 -->

            <!-- 실제 버튼이 눌러졌을 때 Command에 대한 이벤트 핸들러를 정의해야 한다.-->

            <Button Content="BACK"

              Command="{Binding DeleteCharCommand,Source={StaticResource calcViewModel}}"

              Grid.Column="1"/>

            <Button Content="Clear"

              Command="{Binding ClearCommand,Source={StaticResource calcViewModel}}"

              Grid.Column="2"/>

        </Grid>

 

        <Button Content="1"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="1"

            Grid.Row="1" Grid.Column="0" />

 

        <Button Content="2"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="2"

            Grid.Row="1" Grid.Column="1" />

 

        <Button Content="3"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="3"

            Grid.Row="1" Grid.Column="2" />

        <Button Content="+"

            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="+"

            Grid.Row="1" Grid.Column="3" />

 

        <Button Content="4"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="4"

            Grid.Row="2" Grid.Column="0" />

 

        <Button Content="5"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="5"

            Grid.Row="2" Grid.Column="1" />

 

        <Button Content="6"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="6"

            Grid.Row="2" Grid.Column="2" />

        <Button Content="-"

            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="-"

            Grid.Row="2" Grid.Column="3" />

 

        <Button Content="7"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="7"

            Grid.Row="3" Grid.Column="0" />

 

        <Button Content="8"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="8"

            Grid.Row="3" Grid.Column="1" />

 

        <Button Content="9"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="9"

            Grid.Row="3" Grid.Column="2" />

        <Button Content="*"

            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="*"

            Grid.Row="3" Grid.Column="3" />

 

        <Button Content="0"

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="0"

            Grid.Row="4" Grid.Column="0" />

 

        <Button Content="."

            Command="{Binding AddCharCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="."

            Grid.Row="4" Grid.Column="1" />

 

        <Button Content="="

            Command="{Binding CalcCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="="

            Grid.Row="4" Grid.Column="2" />

        <Button Content="/"

            Command="{Binding OperationCommand,Source={StaticResource calcViewModel}}"

            CommandParameter="/"

            Grid.Row="4" Grid.Column="3" />

    </Grid>

</Window>

  • CalcViewModel.cs

using System;

using System.ComponentModel;

using System.Windows.Input;

 

namespace Calc

{    

    public class CalcViewModel : INotifyPropertyChanged

    {

        //아래 두 필드는 속성으로 구현되어 있다.

        //출력될 문자들을 담아둘 변수

        string inputString = "";

 

        //계산기화면의 출력 텍스트박스에 대응되는 필드

        string displayText = "";

 

        //속성이 바뀔때 이벤트 발생하도록 이벤트 정의

        public event PropertyChangedEventHandler PropertyChanged;

 

        //생성자

        public CalcViewModel()

        {

            //이벤트 핸들러 정의

            //숫자 버튼을 클릭할 때 실행

            this.AddCharCommand = new AddCharCommand(this);

 

            //백스페이스 버튼을 클릭할 때 실행, 한글자 삭제

            this.DeleteCharCommand = new DeleteCharCommand(this);

 

            //Clear 버튼을 클릭할 때 실행, 출력창을 전부 지운다.

            this.ClearCommand = new ClearCommand(this);

 

            //+,-,*,/ 버튼을 클릭할 때 실행

            //현재출력창의 숫자를 Op1 속성에 저장, Op속성에 클릭한 연산자 저장

            //계산기의 출력화면을 Clear

            this.OperationCommand = new OperationCommand(this);

 

            // =버튼을 클릭시 실행

            this.CalcCommand = new CalcCommand(this);

        }

 

        // Public 속성들을 정의

        public string InputString

        {

            internal set

            {                

                if (inputString != value)

                {

                    inputString = value;

                    OnPropertyChanged("InputString");

                    this.DisplayText = inputString;

                    ((DeleteCharCommand)this.DeleteCharCommand).OnCanExecuteChanged();

                }                

            }

 

            get { return inputString; }

        }

 

        //계산기의 출력창과 바인딩된 속성

        public string DisplayText

        {

            protected set

            {

                if (displayText != value)

                {

                    displayText = value;

                    OnPropertyChanged("DisplayText");

                }

            }

            get { return displayText; }

        }

 

        public string Op { get; set; }

        public double Op1 { get; set; }

        public double Op2 { get; set; }

 

        // 숫자 클릭

        public ICommand AddCharCommand { protected set; get; }

 

        // <- 클릭, 한글자씩 삭제 

        public ICommand DeleteCharCommand { protected set; get; }

 

        // C 클릭시 DisplayText 전체를 지운다.

        public ICommand ClearCommand { protected set; get; }

 

        // +, -, *, / 클릭

        public ICommand OperationCommand { protected set; get; }

 

        // = 클릭

        public ICommand CalcCommand { protected set; get; }

 

 

        protected void OnPropertyChanged(string propertyName)

        {

            //이벤트를 발생시킨다.

            if (PropertyChanged != null)

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

        }

    }

 

    // CanExecute를 호출하기 위한 메소드

    // 예를들면

    // 처음 화면이 뜰때 BACK 버튼이 비활성화 되었다가

    // 입력이 들어오묜 활성화 되는때 이 메소드를 호출한다.

    public interface IBaseCommand : ICommand

    {

        void OnCanExecuteChanged();

    }

 

    class AddCharCommand : ICommand

    {

        private CalcViewModel viewModel;

        public event EventHandler CanExecuteChanged;

 

        public AddCharCommand(CalcViewModel viewModel)

        {

            this.viewModel = viewModel;

        }

        public bool CanExecute(object parameter)

        {

            return true;

        }

        // 1,2,,,, 숫자들을 눌렀을때 실행됨

        public void Execute(object parameter)

        {

            viewModel.InputString += parameter;                        

        }        

    }

 

    class DeleteCharCommand : IBaseCommand

    {

        private CalcViewModel viewModel;

 

        // OnCanExecuteChanged 메소드의

        // ommandManager.InvalidateRequerySuggested()를 호출하면

        // CanExecuteChanged 이벤트가 호출되어 

        // CanExecute로 해당 Command가 있는 버튼을 활성화 또는 비활성화

        public event EventHandler CanExecuteChanged

        {

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested += value; }

        }        

        public DeleteCharCommand(CalcViewModel viewModel)

        {

            this.viewModel = viewModel;

        }

        public bool CanExecute(object parameter)

        {            

            return viewModel.InputString.Length > 0;

        }

        // BACK 버튼을 눌렀을 때 실행됨

        public void Execute(object parameter)

        {

            viewModel.InputString = viewModel.InputString.Substring(0, viewModel.InputString.Length - 1);

        }

        public void OnCanExecuteChanged()

        {

            CommandManager.InvalidateRequerySuggested();

        }

    }

 

    class ClearCommand : IBaseCommand

    {

        private CalcViewModel viewModel;

 

        public event EventHandler CanExecuteChanged

        {

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested += value; }

        }

 

        public ClearCommand(CalcViewModel viewModel)

        {

            this.viewModel = viewModel;

        }

        public bool CanExecute(object parameter)

        {

            return viewModel.InputString.Length > 0; ;

        }

        public void Execute(object parameter)

        {

            // Clear

            viewModel.InputString = "";

        }

        public void OnCanExecuteChanged()

        {

            CommandManager.InvalidateRequerySuggested();

        }

    }

 

    class OperationCommand : IBaseCommand

    {

        private CalcViewModel viewModel;

 

        public event EventHandler CanExecuteChanged

        {

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested += value; }

        }

 

        public OperationCommand(CalcViewModel viewModel)

        {

            this.viewModel = viewModel;

        }

        public bool CanExecute(object parameter)

        {

            return viewModel.InputString.Length > 0; 

        }

        public void Execute(object parameter)

        {

            viewModel.Op = parameter.ToString();

            viewModel.Op1 = Convert.ToDouble(viewModel.InputString);

            viewModel.InputString = "";

        }

        public void OnCanExecuteChanged()

        {

            CommandManager.InvalidateRequerySuggested();

        }

    }

 

    class CalcCommand : IBaseCommand

    {

        private CalcViewModel viewModel;

 

        public event EventHandler CanExecuteChanged

        {

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested += value; }

        }

 

        public CalcCommand(CalcViewModel viewModel)

        {

            this.viewModel = viewModel;

        }

        public bool CanExecute(object parameter)

        {

            return (viewModel.Op1.ToString() != string.Empty

                    && viewModel.Op2.ToString() != string.Empty);

        }

        public void Execute(object parameter)

        {

            viewModel.Op2 = Convert.ToDouble(viewModel.InputString);

 

            switch (viewModel.Op)

            {

                case "+": viewModel.InputString = (viewModel.Op1 + viewModel.Op2).ToString(); break;

                case "-": viewModel.InputString = (viewModel.Op1 - viewModel.Op2).ToString(); break;

                case "*": viewModel.InputString = (viewModel.Op1 * viewModel.Op2).ToString(); break;

                case "/": viewModel.InputString = (viewModel.Op1 / viewModel.Op2).ToString(); break;

            }

            viewModel.Op1 = Convert.ToDouble(viewModel.InputString);

        }

        public void OnCanExecuteChanged()

        {

            CommandManager.InvalidateRequerySuggested();

        }

    }

 

}


  • 실행결과

aeh0l1Uzis_2tzONfAjIFRwX9tpzqZuL6dGme_is


6.1.8 데이터 바인딩을 이용하여 오라클 EMP 테이블 데이터를 ListView에 뿌리기(ODP.NET, ItemTemplate)

  • ODP.NET을 이용하여 오라클 DB의 EMP 테이블의 내용을 ListView에 출력하는 예제를 작성해 보자.

  • Visual Studio 2015에서 ODP.NET을 사용하기 위해서는 Oracle Developer Tool for Visual Studio 2015를 다운받아 설치하면 된다.( http://www.oracle.com/technetwork/topics/dotnet/downloads/odacmsidownload-2745497.html )

  • 오라클홈 디렉토리 아래 NETWORK\Admin 폴더에 tnsnames.ora 파일에 다음 접속하고자 하는 Oracle Server의 접속정보 추가

ONJ =

  (DESCRIPTION =

    (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))

    (CONNECT_DATA =

      (SERVER = DEDICATED)

      (SERVICE_NAME = onj) 

 

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...