2020년 8월 17일 월요일

WPF Command 패턴의 이해 및 Command, 데이터바인딩 실습

 WPF Command 패턴의 이해 및 Command, 데이터바인딩 실습



 

WPF Command 패턴의 이해 및 Command, 데이터바인딩 실습

 

n  전통적인 이벤트 기반 프로그래밍에서 컨트롤에 이벤트 핸들러 메소드를 코드 비하인드에서 연결하여 사용자의 이벤트를 처리했다그러나 이방식은 이벤트처리 핸들러를 재사용하거나 단위 테스트를 어렵게 한다.

n  XAML UI에서 버튼을 클릭시 MVVM에서는 Click 이벤트 핸들러를 이용하기 보다는 Commamd를 이용하기를 권장한다여러 버튼에서 하나의 Command를 공유할 수 있으므로 모든 컨트롤마다 Click 이벤트를 만드는 방법 보다는 효율적이기 때문이다.

n  WPF의 명령(Command) ICommand 인터페이스를 구현하여 만들며 ICommand Execute  CanExecute라는 두 가지 메서드와 CanExecuteChanged 이벤트를 제공한다.

n  Execute 메서드는 실제 처리해야 하는 작업을 기술하고 CanExecute 메소드에서는 Execute 메소드의 코드를 실행할지 여부를 결정하는 코드를 기술한다. CanExecute가 false를 리턴하면 Execute 메소드는 호출되지 않는다.

n  즉 CanExecute 메소드는 명령을 사용 가능하게 하거나 사용 불가능하게 할 때 사용되며 명령을 사용할 수 있는지 여부를 확인하기 위해 WPF에 의해 호출된다. 이 메소드는 키보드 GET포커스, LOST포커스마우스 업 등과 같은 UI 상호 작용 중에 대부분 발생한다.

n  사용자 정의 명령의 경우 CanExucute 메서드가 대부분의 시나리오에서 호출되지는 않으므로 어떤 조건에 따라 버튼을 활성화비활성화 해야 할 수도 있는데 ICommand 구현체에서 CanExecuteChanged 이벤트를 CommandManager RequerySuggested 이벤트에 연결하면 된다.

n  CanExecute 메소드가 호출되어 CanExecute의 상태가 변경되면 CanExecuteChanged 이벤트가 발생해야 하며 WPF는 CanExecute를 호출하고 Command에 연결된 컨트롤의 상태를 변경한다.

 

// CanExecuteChanged 이벤트는 해당 ICommand에 바인딩 된

// 모든 명령 소스( : Button 또는 MenuItem)

// CanExecute에 의해 반환 된 값이 변경 되었음을 알린다.

 

// 커맨드 소스는 일반적으로 상태를 적절히 업데이트해야 하는데

// 예를 들면 CanExecute() false를 반환하면 버튼이 비활성화 된다).

 

// CommandManager.RequerySuggested 이벤트는 CommandManager가 명령 실행에

// 영향을 줄 수있는 변경 사항이 있다고 생각할 때마다 발생하며 이때마다

// CanExecute가 호출된다.

 

// 예를 들어이는 포커스의 변화 일 수 있는데이 이벤트가 많이 발생한다.

// 따라서 본질적으로 이 코드의 역할은 CommandManager가 명령 실행 기능이 변경되었다고 생각할 때마다(실제로 변경되지 않은 경우에도) CanExecuteChanged를 발생시키는 것이다.

public event EventHandler CanExecuteChanged

{

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested -= value; }

}

n  CommandManager.RequerySuggested 이벤트는 CanExecute 메서드를 강제로 실행할 수 있다.

n  CanExecuteChanged 이벤트는 CommandManager의 RequerySuggested에 위임되어 모든 종류의 UI 상호작용을 통해 변경사항이 호출되는 정확한 알림을 제공한다.

n  RequerySuggested 이벤트의 CommandManager.InvalidateRequerySuggested()를 호출하여 CommandManager RequerySuggested 이벤트를 발생하도록 할 수도 있는데 다음 예제를 참조 하자.

356c10c7f7a281e4f3b5b6e43a63f643_1597660

n  Command 패턴에서는 몇가지 주체가 있는데 서비스를 요청하는 클라이언트(손님), 명령을 서술하는 Command Object(주문서), 명령을 요청하는 Command Invoker(웨이터), 특정 명령을 실제 처리하는 Command Receiver(Target, 요리사)가 있다.

[먼저 간단한 예제를 만들고 이해를 해보자.]

n  프로젝트 명 CommandExam

n  [실행화면]

356c10c7f7a281e4f3b5b6e43a63f643_1597660

n  Model : [Emp.cs]

namespace CommandExam

{

    class Emp

    {

        public string Ename { getset; }

        public string Job { getset; }

public override string ToString()

        {

            return "[" + Ename + "," + Job + "]";

        }

    }

}

 

n  Command Object : [RelayCommand.cs]

 

 

using System;

using System.Windows.Input;

 

namespace CommandExam

{

    class RelayCommand : ICommand

    {

        #region Variables

        Func<object, bool> canExecute;

        Action<object> executeAction;    

        #endregion

 

        #region Construction/Initialization

        public RelayCommand(Action<object> executeAction) : this(executeAction, null)

        {

        }

 

        public RelayCommand(Action<object> executeAction, Func<objectbool> canExecute)

        {

            //if (executeAction == null) throw new ArgumentNullException("Execute Action was null for ICommanding Operation.");

            //this.executeAction = executeAction;

            this.executeAction = executeAction ?? throw new ArgumentNullException("Execute Action was null for ICommanding Operation.");

            this.canExecute = canExecute;

        }

        #endregion

 

        #region ICommand Member

//CommandManager.RequerySuggested 이벤트가 호출될 때마다 실행

// CanExecuteChanged 이벤트가 호출될 때마다 실행

        public bool CanExecute(object param)

        {

            // 사원이름을 입력하지 않으면 Add 버튼은 비활성화 된다.

            if (param?.ToString().Length == 0) return false;

            bool result = this.canExecute == null ? true : this.canExecute.Invoke(param);

            return result;

        }

 

        public void Execute(object param)

        {

            //System.Windows.MessageBox.Show(param.ToString());

            this.executeAction.Invoke(param);

        }

        public event EventHandler CanExecuteChanged

        {

            add { CommandManager.RequerySuggested += value; }

            remove { CommandManager.RequerySuggested -= value; }

        }

        #endregion

    }

}

 

n  Command Receiver : [MainWindowViewModel]

 

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Runtime.CompilerServices;

 

namespace CommandExam

{

    class MainWindowViewModel : INotifyPropertyChanged

    {

        public Emp _SelectedEmp;

        public Emp SelectedEmp

        {

            get

            {

                return _SelectedEmp;

            }

            set

            {

                _SelectedEmp = value;

                OnPropertyChanged("SelectedEmp");

            }

        }

 

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string Pname = null)

        {

            PropertyChanged?.Invoke(thisnew PropertyChangedEventArgs(Pname));

        }

 

        public RelayCommand AddEmpCommand { getset; }

 

// 항목을 추가 하거나 제거할  알림을 제공 하는 컬렉션 클래스

        public ObservableCollection<Emp> Emps { getset; }

 

        public MainWindowViewModel()

        {

            Emps = new ObservableCollection<Emp>();

            Emps.Add(new Emp { Ename = "홍길동", Job = "Salesman" });

            Emps.Add(new Emp { Ename = "김길동", Job = "Clerk" });

            Emps.Add(new Emp { Ename = "정길동", Job = "Manager" });

            Emps.Add(new Emp { Ename = "박길동", Job = "Salesman" });

            Emps.Add(new Emp { Ename = "성길동", Job = "Clerk" });

            AddEmpCommand = new RelayCommand(AddEmp);

        }

 

        public void AddEmp(object param)

        {

            Emps.Add(new Emp { Ename = param.ToString(),Job="New Job"});

        }

    }

}

 

n  Client : [MainWindow.xaml], 여기서 Button은 Command Invoker

 

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

        mc:Ignorable="d"

        Title="MainWindow" SizeToContent="Width">

    <Window.DataContext>

        <local:MainWindowViewModel/>

    </Window.DataContext>

    <StackPanel>

        <TextBlock>사원 이름을 입력하세요.</TextBlock>

        <TextBox x:Name="txtName"  Text={Binding SelectedEmp.Ename}/>

        <Button Command="{Binding AddEmpCommand}" CommandParameter="{Binding Text, ElementName=txtName}">Add</Button>

        <ListBox ItemsSource="{Binding Emps}"

                 SelectedItem="{Binding SelectedEmp}"

                 DisplayMemberPath="Ename"

                 x:Name="empListBox"/>

        <Label x:Name="label" Content="{Binding SelectedItem, ElementName=empListBox}"

               HorizontalAlignment="Center"

               Height="40"

               Margin="10,0,0,0" Width="137"/>   

</StackPanel>

</Window>

 

n  위 예제에서 Command Invoker는 MainWindow.xaml의 버튼 이며  Command Target(Receiver)은 MainWindowViewModel, ConcreteCommand Object는 RelayCommand 객체이다.

 356c10c7f7a281e4f3b5b6e43a63f643_1597660

 


 

2020년 8월 15일 토요일

(동영상)C#, WPF 데이터바인딩, DataContext를 이용한 TwoWay DataBinding 실습 n XAML XML에서 데

 


 

http://ojc.asia/bbs/board.php?bo_table=WPF&wr_id=144


(동영상)C#, WPF 데이터바인딩, DataContext를 이용한 TwoWay DataBinding 실습 

n  XAML XML에서 데이터바인딩을 위한 소스객체는 Binding의 Source, ElementName 속성을 이용한다.

<!-- ElementName=txt1 대신 Source={x:Reference txt1}가능 -->

<Label HorizontalAlignment="Center" BorderBrush="Black"

BorderThickness="2"

Content="{Binding ElementName=txt1, Path=Text}" />

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

n  WPF 프로젝트를 생성 후 콘솔에 사원의 Name, City를 출력해야 하니 프로젝트 속성에서 출력형식을 "콘솔 응용프로그램"으로 설정하자.

n  Emp.cs

namespace 

WpfApp6

{

    public class Emp

    {

        public string Ename

        {

            get;

            set;

        }

 

        public string City

        {

            get;

            set;

        }

    }

}

 

n  MainWindow.xaml

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

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <Grid x:Name="Grid1">

        <Grid.RowDefinitions>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="Auto"/>

            <RowDefinition Height="Auto"/>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="Auto" />

            <ColumnDefinition Width="*"/>

        </Grid.ColumnDefinitions>

        <TextBlock Grid.Column="0" Grid.Row="0">Name:</TextBlock>

        <TextBlock Grid.Column="0" Grid.Row="1">City:</TextBlock>

        <TextBox x:Name="TextBox1" Grid.Column="1"

                 Grid.Row="0" Text="{Binding Path=Ename}"></TextBox>

        <TextBox x:Name="TextBox2" Grid.Column="1"

                 Grid.Row="1" Text="{Binding Path=City}"></TextBox>

        <Button Grid.Column=" 1" Grid.Row="2"  

                Name="button1" Click="OnClicked">Control To Context</Button>

    </Grid>

</Window>

n  MainWindow.xaml.cs

using System.Windows;

namespace WpfApp6

{

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            Emp e = new Emp()

            {

                Ename = "홍길동",

                City = "서울"

            };

            this.DataContext = e;

        }

        private void OnClicked(object sender, RoutedEventArgs args)

        {

            Emp e = this.DataContext as Emp;

            System.Console.WriteLine(e.Ename);

            System.Console.WriteLine(e.City);

        }

 

    }

}

n  실행 화면

최초 실행시 XAML 파일의 Resource절에서 설정한 Emp 클래스 Ename, City 속성을 기본값으로 해서 화면이 로딩된다.

Name, City를 다른 내용으로 입력하면 데이터 바인딩으로 인해 Emp 객체의 Ename, City 속성값이 채워지고 이것을 콘솔화면에 출력했다.

 eb0469448be32ef629be251932a651d4_1597543

(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...