[C#프로그래밍] [WPF] PC 카카오톡 만들기 #09 - Attached Property 사용하기

728x90

 

https://www.youtube.com/watch?v=nBjCRGC0Ua8&list=PLlrfTSXS0LLKHfOfwM31jJw3SHuDnkF49&index=10

 

- 매번 백그라운드 코드에 컬러를 지정하면 MVVC에 위배 되고 번거롭기 때문에 종속성 속성을 사용

- 컨트롤에 Dependency Property를 사용하여 해당 속성을 쉽게 사용할 수 있도록 사용자 정의 속성 생성 
  해당 엘리먼트의 하위 속성들에도 추가 가능 

- WpfAttachedProperty에 클래스파일 ComboBoxBackgroundManager 생성

- prodp 탭탭 했을때 나왔던 아래의 코드는 Register로 종속성 속성을 만들면 해당 사용자 정의 컨트롤에서만 사용가능하다.

        public int MyProperty
        {
            get { return (int)GetValue(MyPropertyProperty); }
            set { SetValue(MyPropertyProperty, value); }
        }
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));

 

-  다른 컨트롤에서도 사용자 커스텀 속성을 범용적으로 사용 할 수 있도록 하고싶을 때 사용하는 것이 Attached Property이다.

 

📌 Attached Property 구조  만들기

-  propa 탭탭을 누르면 기본 구조가 생성된다. 

namespace WpfAttachedProperty
{
    public class ComboBoxBackgroundManager
    {
        public static int GetMyProperty(DependencyObject obj)
        {
            return (int)obj.GetValue(MyPropertyProperty);
        }

        public static void SetMyProperty(DependencyObject obj, int value)
        {
            obj.SetValue(MyPropertyProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.RegisterAttached("MyProperty", typeof(int), typeof(ownerclass), new PropertyMetadata(0));
    }
}

 

- 여기서 자료형은 네군데가 모두 같아야하고 typeof 안의 ownerclass에는 클래스명이와야한다

- ComboBoxBackgroundManager.cs

namespace WpfAttachedProperty
{
    public class ComboBoxBackgroundManager
    {

        public static Brush GetBackground (DependencyObject obj)
        {
            return (Brush)obj.GetValue(BackgroundProperty);
        }

        public static void SetBackground(DependencyObject obj, Brush value)
        {
            obj.SetValue(BackgroundProperty, value);
        }

        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(ComboBoxBackgroundManager), new UIPropertyMetadata(Brushes.Transparent));

    }
}

 

- 이 속성을 사용하고 싶은 엘리먼트로 들어가서 local 로 지정을 해 준다.

 

- MainWindow.xaml

    <StackPanel>
        <TextBlock Text="combobox"/>
        <ComboBox x:Name="cmb" 
                  IsEditable="True" 
                  Background="Red"
                  Margin="5 0"
                  local:ComboBoxBackgroundManager.Background="Red"/>
    </StackPanel>

 

📌 BackgroundProperty가 추가 되었을 때 실행 될 이벤트 만들기

- xaml에서 컬러를 지정했기 때문에 이벤트 발생 DependencyObject는 해당 속성이 발생된 해당 엘리먼트가 된다. (combobox)

- ComboBoxBackgroundManager.cs

        //BackgroundProperty가 추가 되었을 때 실행 될 이벤트 
        private static void BackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //콤보박스로 형변환
            var cmb = d as ComboBox;
            if (cmb == null) return;

            //아까 넘겼던 red가 NewValue가 된다. 
            //oldValue는 그 전에 있던 값 (기본값으로 지정하였던 Transparent가 됨)
            if (e.NewValue != e.OldValue)
            {
                //색을 여러번 바꾸면 이벤트가 중첩되기 때문에 이벤트를 먼저 지운 후 생성한다.
                cmb.Loaded -= Cmb_Loaded;
                cmb.Loaded += Cmb_Loaded;

                cmb.Unloaded -= Cmb_Unloaded;
                cmb.Unloaded += Cmb_Unloaded;
            }
        
        
                private static void Cmb_Unloaded(object sender, RoutedEventArgs e)
        {
            //종료되면 이벤트를 지우고 메모리를 제거
            throw new NotImplementedException();
        }

        private static void Cmb_Loaded(object sender, RoutedEventArgs e)
        {

            //sender 는 Combobox가 된다.
            var cmb = (ComboBox)sender;
            cmb.SetBackground(Brushes.Red);

           
        }

- NewValue가 Cmb_Loaded의 cmb.SetBackground의 Brushes를 가리키고있기때문에 NewValue를 ComboboxLoaded로 전달을 해줘야한다.

- ComboBoxBackgroundManager.cs

namespace WpfAttachedProperty
{
    public class ComboBoxBackgroundManager
    {
        private static Brush? _newBrush = null;
        
        ...

 

그리고 기본 브러쉬를 할당시켜준다.
_newBrush 가 null이면 Treansparent로 설정한다.

- ComboBoxBackgroundManager.cs

        private static void BackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var cmb = d as ComboBox;
            if (cmb == null) return;

            if (e.NewValue != e.OldValue)
            {
			//brush 색깔이 바뀌면 NewValue에 색깔 전달 
                _newBrush = (Brush)e.NewValue;
                
                
                ...

		private static void Cmb_Loaded(object sender, RoutedEventArgs e)
        {

            //sender 는 Combobox가 된다.
            var cmb = (ComboBox)sender;
            
            //전달된 값은 로드가 되는 경우 SetBackground 확장 함수에 추가되어 Combobox 색상 변경 
           cmb.SetBackground(_newBrush ?? Brushes.Transparent);

        }

 

- ComboBoxBackgroundManager.cs

        private static void Cmb_Unloaded(object sender, RoutedEventArgs e)
        {
            //종료되면 이벤트를 지우고 메모리를 제거
            var cmb = (ComboBox)sender;
            cmb.Loaded -= Cmb_Loaded;
            cmb.Unloaded -= Cmb_Unloaded;
            _newBrush = null;
        }

 

- ComboBoxBackgroundManager.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfAttachedProperty
{
    public class ComboBoxBackgroundManager
    {
        private static Brush? _newBrush = null;
        public static Brush GetBackground(DependencyObject obj)
        {
            return (Brush)obj.GetValue(BackgroundProperty);
        }

        public static void SetBackground(DependencyObject obj, Brush value)
        {
            obj.SetValue(BackgroundProperty, value);
        }

        // Using a DependencyProperty as the backing store for Background.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.RegisterAttached("Background", typeof(Brush), typeof(ComboBoxBackgroundManager), new UIPropertyMetadata(Brushes.Transparent, BackgroundChanged));

        private static void BackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var cmb = d as ComboBox;
            if (cmb == null) return;

            if (e.NewValue != e.OldValue)
            {
                _newBrush = (Brush)e.NewValue;

                cmb.Loaded -= Cmb_Loaded;
                cmb.Loaded += Cmb_Loaded;
                cmb.Unloaded -= Cmb_Unloaded;
                cmb.Unloaded += Cmb_Unloaded;
            }
        }

        private static void Cmb_Unloaded(object sender, RoutedEventArgs e)
        {
            var cmb = (ComboBox)sender;
            cmb.Loaded -= Cmb_Loaded;
            cmb.Unloaded -= Cmb_Unloaded;
            _newBrush = null;
        }

        private static void Cmb_Loaded(object sender, RoutedEventArgs e)
        {
            var cmb = (ComboBox)sender;
            cmb.SetBackground(_newBrush ?? Brushes.Transparent);
        }
    }
}

 

- 비하인드 코드에 작성했던 색상 지정은 삭제해준다.

-MainWindow.xaml.cs

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {

        }

 

- MainWindowd에도 지정했던 컬러 코드를 삭제해준 뒤 실행을 해 본다.

- MainWindow.xaml

    <StackPanel>
        <TextBlock Text="combobox"/>
        <ComboBox x:Name="cmb" 
                  IsEditable="True" 
                  Margin="5 0"
                  local:ComboBoxBackgroundManager.Background="Red"/>
    </StackPanel>

 

📌 (예제) 폼을 하나 더 추가 해 보기

- WpfAttachedProperty에 창 파일 MainView2 추가

 

- MainWindow.xaml

    <StackPanel>
        <TextBlock Text="combobox"/>
        <Button Content="ToView2" Click="Button_Click"/>
    </StackPanel>

 

- MainWindow.xaml.cs

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;

        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var view2 = new MainView2();
            view2.Show();

        }
    }

 

-MainView2.xaml

    <Grid>
        <ComboBox local:ComboBoxBackgroundManager.Background="Red"/>
    </Grid>

 

-MainView2.xaml.cs

namespace WpfAttachedProperty
{
    /// <summary>
    /// MainView2.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainView2 : Window
    {
        public MainView2()
        {
            InitializeComponent();
        }
    }
}

실행을 해 보면 BackgroundChanged가 발생되어 Load되면서 Background를 적용시킨다. 

 

728x90