내용 보기

작성자

관리자 (IP : 172.17.0.1)

날짜

2020-07-10 02:38

제목

[WPF] 데이터 바인딩시 특정 속성에 대해서만 동적으로 바인딩 처리를 해야 할 경우


WPF에서 List타입의 데이터를 그리드나 리스트뷰 같은 컨트롤에 바인딩 하여 표시 할 경우 xaml상에 Binding처리를 해두고
특정 경우에 따라 
어떤 List 데이터는 A속성만 바인딩 처리하고 어떤 List 데이터는 A, B속성을 바인딩 처리를 해야 할 경우
추상 클래스 PropertyDescriptor를 상속받아 구현 해주면 된다.

바인딩 처리된 컨트롤이 화면에 표시 될 경우 표시 되는 영역의 속성 정보를 추상클래스 CustomTypeDescriptor  의

public override PropertyDescriptorCollection GetProperties()
cs

위 메서드를 호출하여 얻어 오게 된다.
따라서 바인딩 처리 되는 List의 모델 클래스에 CustomTypeDescriptor  클래스를 상속받아 재구현하여 원하는 속성 정보를 참조 하도록 할 수 있다.

바인딩 처리된 컨트롤은 다시 속성에 대한 정보를 추상클래스 PropertyDescriptor

public override object GetValue(object component)
cs

위 메서드를 호출하여 속성 값을 얻어 오게 된다.
위 두개의 클래스를 사용하여 동적으로 바인딩 처리를 구현할 수 있다.

CustomPropertyDescriptor.cs

using System;
using System.ComponentModel;
 
namespace IntegratedManagementConsole.Commons
{
    /// <summary>
    /// 수동으로 속성 정보를 제공한다.
    /// 컨트롤 바인딩시 CLR에서 참조되도록 되어 있다.
    /// </summary>
    /// <typeparam name="T">속성 타입</typeparam>
    public class CustomPropertyDescriptor<T> : PropertyDescriptor
    {
        #region Member Fields
        private Type propertyType;
        private Type componentType;
        private T propertyValue;
        #endregion
 
        #region Constructor
        public CustomPropertyDescriptor(string propertyName, Type componentType)
            : base(propertyName, new Attribute[] { })
        {
            this.propertyType = typeof(T);
            this.componentType = componentType;
        }
        #endregion
 
        #region PropertyDescriptor Implementation Overriden Methods
        public override bool CanResetValue(object component) { return true; }
        public override Type ComponentType { get { return componentType; } }
 
        public override object GetValue(object component)
        {
            return propertyValue;
        }
 
        public override bool IsReadOnly { get { return false; } }
        public override Type PropertyType { get { return propertyType; } }
        public override void ResetValue(object component) { SetValue(component, default(T)); }
        public override void SetValue(object component, object value)
        {
            if (!value.GetType().IsAssignableFrom(propertyType))
            {
                throw new System.Exception("Invalid type to assign");
            }
 
            propertyValue = (T)value;
        }
 
        public override bool ShouldSerializeValue(object component) { return true; }
        #endregion
    }
}
cs

ModelBase.cs

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
 
namespace IntegratedManagementConsole.Commons
{
    public class ModelBase : CustomTypeDescriptor, INotifyPropertyChanged
    {
        #region Member Fields
        /// <summary>
        /// 모델에서 사용되는 속성 리스트
        /// 주로 바인딩되는 속성들이다.
        /// </summary>
        List<PropertyDescriptor> _properties = new List<PropertyDescriptor>();
        #endregion
 
        #region Constructor
        public ModelBase() { }
        #endregion
 
        #region INotifyPropertyChange Implementation
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
        }
        #endregion INotifyPropertyChange Implementation
 
        #region Public Methods
        /// <summary>
        /// 속성 설정
        /// </summary>
        /// <typeparam name="T">속성 타입</typeparam>
        /// <param name="propertyName">속성 이름</param>
        /// <param name="propertyValue">속성 값</param>
        public void SetPropertyValue<T>(string propertyName, T propertyValue)
        {
            var properties = this.GetProperties()
                                    .Cast<PropertyDescriptor>()
                                    .Where(prop => prop.Name.Equals(propertyName));
 
            if (properties == null || properties.Count() != 1)
            {
                throw new Exception("The property doesn't exist.");
            }
 
            var property = properties.First();
            property.SetValue(this, propertyValue);
 
            OnPropertyChanged(propertyName);
        }
 
        /// <summary>
        /// 속성 값 불러오기
        /// </summary>
        /// <typeparam name="T">속성 타입</typeparam>
        /// <param name="propertyName">속성 이름</param>
        /// <returns></returns>
        public T GetPropertyValue<T>(string propertyName)
        {
            var properties = this.GetProperties()
                                .Cast<PropertyDescriptor>()
                                .Where(prop => prop.Name.Equals(propertyName));
 
            if (properties == null || properties.Count() != 1)
            {
                throw new Exception("The property doesn't exist.");
            }
 
            var property = properties.First();
            return (T)property.GetValue(this);
        }
 
        /// <summary>
        /// 속성 추가
        /// </summary>
        /// <typeparam name="T">속성 타입</typeparam>
        /// <typeparam name="U">속성 Onwer (모델)</typeparam>
        /// <param name="propertyName"></param>
        public void AddProperty<T, U>(string propertyName) where U : ModelBase
        {
            var customProperty =
                    new CustomPropertyDescriptor<T>(
                                            propertyName,
                                            typeof(U));
 
            _properties.Add(customProperty);
            customProperty.AddValueChanged(
                                        this,
                                        (o, e) => { OnPropertyChanged(propertyName); });
        }
        #endregion
 
        #region CustomTypeDescriptor Implementation Overriden Methods
        public override PropertyDescriptorCollection GetProperties()
        {
            var properties = base.GetProperties();
            return new PropertyDescriptorCollection(
                                properties.Cast<PropertyDescriptor>()
                                          .Concat(_properties).ToArray());
        }
        #endregion
    }
}
cs

TestModel.cs

using System;
using System.ComponentModel;
using IntegratedManagementConsole.Commons;
 
namespace IntegratedManagementConsole.Windows.TestWindow
{
    public class TestModel : ModelBase
    {
        private Int64 _userNum;
        private string _userID;
        private string _userName;
        private bool _userChecked;
 
        public TestModel()
        {
            //base.AddProperty<Int64, TestModel>("UserNum");
            //base.AddProperty<string, TestModel>("UserID");
            base.AddProperty<string, TestModel>("UserName");
            //base.AddProperty<bool, TestModel>("UserChecked");
        }
 
        public Int64 UserNum
        {
            get { return _userNum; }
            set
            {
                _userNum = value;
                base.SetPropertyValue<Int64>("UserNum", value);
            }
        }
 
        public string UserID
        {
            get { return _userID; }
            set
            {
                _userID = value;
                base.SetPropertyValue<string>("UserID", value);
            }
        }
 
        public string UserName
        {
            get { return _userName; }
            set
            {
                _userName = value;
                base.SetPropertyValue<string>("UserName", value);
            }
        }
 
        public bool UserChecked
        {
            get { return _userChecked; }
            set
            {
                _userChecked = value;
                base.SetPropertyValue<bool>("UserChecked", value);
            }
        }
    }
}
cs

위 TestModel의 List를 바인딩 처리 할 경우 xaml에서 바인딩 처리가 되어 있어도 UserName 속성만 바인딩 처리가 된다.

출처1

출처2




2022-12-27 17:22
이 글은 다음글로 다시 설명 되었습니다.

[WPF] ICustomTypeDescriptor를 사용한 동적 바인딩
https://arong.info/List/ContentsView/2365
관리자
((223.62.21.139))