【问题标题】:WPF Data Binding - Example of "Custom Type Descriptor"WPF 数据绑定 - “自定义类型描述符”示例
【发布时间】:2010-12-22 12:15:23
【问题描述】:

我看到有几个人说 WPF 可以将“自定义类型描述符”用于“更改通知”。

我知道如何进行更改通知的方法是:

object.GetBindingExpression(Bound.property).UpdateTarget();

或者让我的对象实现INotifiyPropertyChanged

我看到 cmets 说自定义类型描述符也可以工作,但没有人给出一个很好的例子来说明它是如何工作的。我现在要求那个例子(IE 是 WPF 数据绑定和通过自定义类型描述符更新的一个很好的例子。)

【问题讨论】:

    标签: c# wpf data-binding binding


    【解决方案1】:

    这是一个非常简单的例子。

    Window1.xaml

    <Window x:Class="CTDExample.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
    
            <TextBlock>Name:</TextBlock>
            <TextBox Grid.Column="1" Text="{Binding Name}"/>
    
            <TextBlock Grid.Row="1">Age:</TextBlock>
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Age}"/>
    
            <TextBlock Grid.Row="2" Grid.ColumnSpan="2">
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} is {1} years old.">
                        <Binding Path="Name"/>
                        <Binding Path="Age"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Grid>
    </Window>
    

    Window1.xaml.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Windows;
    
    namespace CTDExample
    {
        public partial class Window1 : Window
        {
            public Window1()
            {
                InitializeComponent();
    
                var ctd = new CTD();
                ctd.AddProperty("Name");
                ctd.AddProperty("Age");
                DataContext = ctd;
            }
        }
    
        public class CTD : CustomTypeDescriptor
        {
            private static readonly ICollection<PropertyDescriptor> _propertyDescriptors = new List<PropertyDescriptor>();
    
            public void AddProperty(string name)
            {
                _propertyDescriptors.Add(new MyPropertyDescriptor(name));
            }
    
            public override PropertyDescriptorCollection GetProperties()
            {
                return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
            }
    
            public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                return GetProperties();
            }
    
            public override EventDescriptorCollection GetEvents()
            {
                return null;
            }
    
            public override EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                return null;
            }
        }
    
        public class MyPropertyDescriptor : PropertyDescriptor
        {
            private readonly IDictionary<object, object> _values;
    
            public MyPropertyDescriptor(string name)
                : base(name, null)
            {
                _values = new Dictionary<object, object>();
            }
    
            public override bool CanResetValue(object component)
            {
                throw new NotImplementedException();
            }
    
            public override Type ComponentType
            {
                get { throw new NotImplementedException(); }
            }
    
            public override object GetValue(object component)
            {
                object value = null;
                _values.TryGetValue(component, out value);
                return value;
            }
    
            public override bool IsReadOnly
            {
                get { return false; }
            }
    
            public override Type PropertyType
            {
                get { return typeof(object); }
            }
    
            public override void ResetValue(object component)
            {
                throw new NotImplementedException();
            }
    
            public override void SetValue(object component, object value)
            {
                var oldValue = GetValue(component);
    
                if (oldValue != value)
                {
                    _values[component] = value;
                    OnValueChanged(component, new PropertyChangedEventArgs(base.Name));
                }
            }
    
            public override bool ShouldSerializeValue(object component)
            {
                throw new NotImplementedException();
            }
    
            public override void AddValueChanged(object component, EventHandler handler)
            {
                // set a breakpoint here to see WPF attaching a value changed handler
                base.AddValueChanged(component, handler);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我使用Kent Boogart 的出色且非常清晰的示例作为我的自定义类型的基础。

      我认为应该对示例程序进行一些小改动,以阐明CustomTypeDescriptorPropertyDescriptor 之间的关系。

      1. 我认为数据应该存储在类型对象的实例上,而不是属性描述符上。
      2. 通常我希望每个自定义类型实例都保留它自己的属性描述符集合,而不是静态的。为了澄清这一点,我添加了更多信息(Type)来键入属性描述符。

      第二点确实是域问题,但我希望更典型的使用需要实例属性数据,因为在编译时不知道属性时会使用这种类型。

      MainWindow.xaml

      <Window
        x:Class="CTDExample.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">
      
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
      
            <TextBlock>Name:</TextBlock>
            <TextBox Grid.Column="1" Text="{Binding Name}"/>
      
            <TextBlock Grid.Row="1">Age:</TextBlock>
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Age}"/>
      
            <TextBlock Grid.Row="2" Grid.ColumnSpan="2">
                <TextBlock.Text>
                    <MultiBinding StringFormat="{}{0} is {1} years old.">
                        <Binding Path="Name"/>
                        <Binding Path="Age"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Grid>
      </Window>
      

      MainWindow.xaml.cs

      using System.Windows;
      
      namespace CTDExample
      {
          public partial class MainWindow : Window
          {
              public MainWindow()
              {
                  InitializeComponent();
      
                  var ctd = new MyCustomType();
                  ctd.AddProperty("Name", typeof(string)); // Now takes a Type argument.
                  ctd.AddProperty("Age", typeof(int));
                  DataContext = ctd;
              }
          }
      }
      

      MyCustomType.cs

      using System;
      using System.Collections.Generic;
      using System.ComponentModel;
      using System.Linq;
      
      namespace CTDExample
      {
          public class MyCustomType : CustomTypeDescriptor
          {
              // This is instance data.
              private readonly ICollection<PropertyDescriptor> _propertyDescriptors = new List<PropertyDescriptor>();
      
              // The data is stored on the type instance.
              private readonly IDictionary<string, object> _propertyValues = new Dictionary<string, object>();
      
              // The property descriptor now takes an extra argument.
              public void AddProperty(string name, Type type)
              {
                  _propertyDescriptors.Add(new MyPropertyDescriptor(name, type));
              }
      
              public override PropertyDescriptorCollection GetProperties()
              {
                  return new PropertyDescriptorCollection(_propertyDescriptors.ToArray());
              }
      
              public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
              {
                  return GetProperties();
              }
      
              public override EventDescriptorCollection GetEvents()
              {
                  return null;
              }
      
              public override EventDescriptorCollection GetEvents(Attribute[] attributes)
              {
                  return null;
              }
      
              private class MyPropertyDescriptor : PropertyDescriptor
              {
                  // This data is here to indicate that different instances of the type
                  // object may have properties of the same name, but with different
                  // characteristics.
                  private readonly Type _type;
      
                  public MyPropertyDescriptor(string name, Type type)
                      : base(name, null)
                  {
                      _type = type;
                  }
      
                  public override bool CanResetValue(object component)
                  {
                      throw new NotImplementedException();
                  }
      
                  public override Type ComponentType
                  {
                      get { throw new NotImplementedException(); }
                  }
      
                  public override object GetValue(object component)
                  {
                      MyCustomType obj = (MyCustomType)component;
                      object value = null;
                      obj._propertyValues.TryGetValue(Name, out value);
                      return value;
                  }
      
                  public override bool IsReadOnly
                  {
                      get { return false; }
                  }
      
                  public override Type PropertyType
                  {
                      get { return _type; }
                  }
      
                  public override void ResetValue(object component)
                  {
                      throw new NotImplementedException();
                  }
      
                  public override void SetValue(object component, object value)
                  {
                      var oldValue = GetValue(component);
      
                      if (oldValue != value)
                      {
                          MyCustomType obj = (MyCustomType)component;
                          obj._propertyValues[Name] = value;
                          OnValueChanged(component, new PropertyChangedEventArgs(Name));
                      }
                  }
      
                  public override bool ShouldSerializeValue(object component)
                  {
                      throw new NotImplementedException();
                  }
      
                  public override void AddValueChanged(object component, EventHandler handler)
                  {
                      // set a breakpoint here to see WPF attaching a value changed handler
                      base.AddValueChanged(component, handler);
                  }
              }
          }
      }
      

      我希望我没有发出任何咆哮,因为这是我的第一篇文章!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-01
        • 2018-12-04
        • 1970-01-01
        • 2022-09-30
        • 2012-12-14
        相关资源
        最近更新 更多