Xceed PropertyGrid的WPF展示为扩大收集项目 [英] Xceed WPF propertyGrid show item for expanded collection

查看:1178
本文介绍了Xceed PropertyGrid的WPF展示为扩大收集项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

怎么样,我显示一个的ObservableCollection<> 在Xceed WPF PropertyGrid的自定义对象,其中每个项目可以展开,以显示自定义对象的属性。 (即:



---- ----- PropertyGrid的



CoreClass




  • (+/-)的ObservableCollection< CustomClass>




    • (+/-)CustomClass.Object1




      • Property1:值


      • Property2:值


      • ...


      • propertyN中:值



    • (+/-)CustomClass.Object2




      • Property1:值


      • Property2:值


      • ...


      • propertyN中:值






如果我用 [ExpandableObject] 的ObservableCollection<> 它只显示计数属性。



编辑:(添加的代码)



MainWindow.xaml:

 <窗口x:类=PropGridExample.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:xctk =http://schemas.xceed.com/ WPF / XAML /工具包
的xmlns:MC =http://schemas.openxmlformats.org/markup-compatibility/2006
的xmlns:地方=CLR的命名空间:PropGridExample
MC:可忽略=D
标题=主窗口HEIGHT =350WIDTH =525>
<网格和GT;
< xctk:PropertyGrid的X:名称=PropertyGrid的SelectedObject ={结合BindingItem}>< / xctk:PropertyGrid的>
< /网格和GT;
< /窗GT;



MainWindow.xaml.cs

 公共部分类主窗口:窗口
{
公共主窗口()
{
MainWindowViewModel mwvm =新MainWindowViewModel();
this.DataContext = mwvm;
的InitializeComponent();
}
}



MainWindowViewModel.cs

 公共类MainWindowViewModel 
{
公共项目BindingItem {搞定;组; }

公共MainWindowViewModel()
{
BindingItem =新项目();
}

公共类项目
{
公众诠释ID {搞定;组; }
[ExpandableObject()]
公众的ObservableCollection< CustomClass> {类搞定;组; }

公共项目()
{
ID = 1;
类=新的ObservableCollection< CustomClass>();
Classes.Add(新CustomClass(){名称=CustomFoo});
}
}

公共类CustomClass
{
公共字符串名称{;组; }
[ExpandableObject()]
公众的ObservableCollection<&型GT;类型{搞定;组; }

公共CustomClass()
{
型=新的ObservableCollection<&型GT;();
Types.Add(新类型(){名称=富,值=酒吧});
Types.Add(新类型(){名称=酒吧,值=富});
}
}

公共类类型
{
公共字符串名称{;组; }
公共字符串值{获得;组; }
}
}


解决方案

请注意,大多数这个想法来自于 CodeProject上项目,您链接到的。这篇文章让你大多数的方式存在,但你注意,它不会收集的WPF PropertyGrid的拓展中的每个项目。为了做到这一点,每一个项目需要有一个 ExpandableObjectAttribute



为了让未来的计算器读者了解,我打算从从头开始。



从一开始



所以,从开始这个例子:

 公共类MainWindowViewModel 
{
///<总结>这个目的,我们希望能够在数据网格进行编辑。 < /总结>
公共ComplexObject BindingComplexObject {搞定;组; }

公共MainWindowViewModel()
{
BindingComplexObject =新ComplexObject();
}
}

公共类ComplexObject
{
公众诠释ID {搞定;组; }

公众的ObservableCollection< ComplexSubObject> {类搞定;组; }

公共ComplexObject()
{
ID = 1;
类=新的ObservableCollection< ComplexSubObject>();
Classes.Add(新ComplexSubObject(){名称=CustomFoo});
Classes.Add(新ComplexSubObject(){名称=我的其他富});
}
}

公共类ComplexSubObject
{
公共字符串名称{;组; }

公众的ObservableCollection< SimpleValues>类型{搞定;组; }

公共ComplexSubObject()
{
型=新的ObservableCollection< SimpleValues>();
Types.Add(新SimpleValues(){名称=富,值=酒吧});
Types.Add(新SimpleValues(){名称=酒吧,值=富});
}
}

公共类SimpleValues
{
公共字符串名称{;组; }
公共字符串值{获得;组; }
}

为了为WPF PropertyGrid的,以便能够在编辑每个项的ObservableCollection,我们需要为集合类型描述符,并返回的项目,如可编辑的集合属性。因为我们不能静态地确定从收集的物品(如每个集合都有不同数量的元素),这意味着集合本身必须是TypeDescriptor,这意味着实施 ICustomTypeDescriptor



(请注意,只有的GetProperties 是我们的目的,剩下的只是代表重要的 TypeDescriptor ):

 公共类ExpandableObservableCollection< T> :的ObservableCollection< T> ;, 
ICustomTypeDescriptor
{
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
//创建一个集合对象持有物业的描述符
PropertyDescriptorCollection PDS =新PropertyDescriptorCollection(NULL);

的for(int i = 0; I<计数;我++)
{
pds.Add(新ItemPropertyDescriptor< T>(这一点,我));
}

返回PDS;
}

#地区使用默认TypeDescriptor东西

AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
返回TypeDescriptor.GetAttributes(这一点,noCustomTypeDesc :真正的);
}

串ICustomTypeDescriptor.GetClassName()
{
返回TypeDescriptor.GetClassName(这一点,noCustomTypeDesc:真正的);
}

串ICustomTypeDescriptor.GetComponentName()
{
返回TypeDescriptor.GetComponentName(这一点,noCustomTypeDesc:真正的);
}

类型转换器ICustomTypeDescriptor.GetConverter()
{
返回TypeDescriptor.GetConverter(这一点,noCustomTypeDesc:真正的);
}

EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
返回TypeDescriptor.GetDefaultEvent(这一点,noCustomTypeDesc:真正的);
}

的PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
返回TypeDescriptor.GetDefaultProperty(这一点,noCustomTypeDesc:真正的);
}

对象ICustomTypeDescriptor.GetEditor(类型editorBaseType)
{
返回TypeDescriptor.GetEditor(这一点,editorBaseType,noCustomTypeDesc:真正的);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
返回TypeDescriptor.GetEvents(这一点,noCustomTypeDesc:真正的);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents(属性[]属性)
{
返回TypeDescriptor.GetEvents(这一点,属性,noCustomTypeDesc:真正的);
}

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(属性[]属性)
{
返回TypeDescriptor.GetProperties(这一点,属性,noCustomTypeDesc:真正的);
}

对象ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor的PD)
{
返回这一点;
}

#endregion
}

此外我们需要 ItemPropertyDescriptor 的实现,这是我在这里提供:

 公共类ItemPropertyDescriptor< T> :PropertyDescriptor的
{
私人只读的ObservableCollection< T> _所有者;
私人只读INT _index;

公共ItemPropertyDescriptor(的ObservableCollection< T>所有者,INT指数)
:基地(#+指数,NULL)
{
_owner =所有者;
_index =指数;
}

公众覆盖AttributeCollection属性
{
得到
{
VAR属性= TypeDescriptor.GetAttributes(的GetValue(空),FALSE) ;
如果(attributes.OfType<!ExpandableObjectAttribute方式>()任何())
{
//复制所有属性加一个额外的一个(
// ExpandableObjectAttribute)
//这确保即使对象本身的类型不具有
// ExpandableObjectAttribute,它仍然会膨胀。
变种newAttributes =新的属性[attributes.Count + 1];
attributes.CopyTo(newAttributes,newAttributes.Length - 1);
newAttributes [newAttributes.Length - 1] =新ExpandableObjectAttribute();

//覆盖数组
属性=新AttributeCollection(newAttributes);
}

返回属性;
}
}

公众覆盖布尔CanResetValue(对象部分)
{
返回FALSE;
}

公众覆盖对象的GetValue(对象部分)
{
返回值;
}

私人吨价
=> _owner [_index]

公共覆盖无效ResetValue(对象部分)
{
抛出新NotImplementedException();
}

公共覆盖无效的SetValue(对象组件,对象的值)
{
_owner [_index] =(T)值;
}

公众覆盖布尔ShouldSerializeValue(对象部分)
{
返回FALSE;
}

公众覆盖类型COMPONENTTYPE
=> _owner.GetType();

公众覆盖BOOL IsReadOnly
=>假;

公众覆盖类型属性类型
=>值.GetType()?;
}



这对于大多数情况下,仅设置了合理的默认值,你可以调整以满足您的需求。



有一点需要注意的是,你可以不同的方式实现属性属性,这取决于你用例。如果你不这样做将它添加到收藏属性,如果它不存在,那么你需要的属性添加到您想要扩展的类/类型;如果你在不断的代码,那么你就能够扩大每个项目集合中不管类/类型具有的属性或没有。



据然后就在的ObservableCollection 使用 ExpandableObservableCollection 的问题。这种吮吸,因为它意味着你的视图模型中有视图东西十岁上下的东西,但¯\_(ツ)_ /¯



此外,你需要将 ExpandableObjectAttribute 添加到每个那就是属性一个 ExpandableObservableCollection



代码转储



如果您再沿着在家之后,你可以使用下面的对话框代码运行的例子:

 <窗口x:CLASS = WpfDemo.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:地方=CLR的命名空间:WpfDemo
的xmlns:xctk =http://schemas.xceed.com/wpf/xaml/toolkit
MC :可忽略=D
标题=主窗口HEIGHT =350WIDTH =525>
<网格和GT;
< xctk:PropertyGrid的X:名称=它/>
< /网格和GT;
< /窗GT;



-

 使用系统; 
使用System.Collections.Generic;
使用System.Linq的;使用System.Threading.Tasks
;使用System.Windows
;

命名空间WpfDemo
{
///<总结> $ B $为MainWindow.xaml
///< b ///交互逻辑; /总结>
公共部分类主窗口:窗口
{
公共主窗口()
{
的InitializeComponent();

It.SelectedObject =新MainWindowViewModel()BindingComplexObject。
}
}
}

和这里的完整视图模型的实现

 使用系统; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.ComponentModel;
使用System.Linq的;使用System.Threading.Tasks
;使用Xceed.Wpf.Toolkit.PropertyGrid.Attributes
;

命名空间WpfDemo
{
公共类MainWindowViewModel
{
///<总结>这个目的,我们希望能够在数据网格进行编辑。 < /总结>
公共ComplexObject BindingComplexObject {搞定;组; }

公共MainWindowViewModel()
{
BindingComplexObject =新ComplexObject();
}
}

[ExpandableObject]
公共类ComplexObject
{
公众诠释ID {搞定;组; }

[ExpandableObject]
公共ExpandableObservableCollection< ComplexSubObject> {类搞定;组; }

公共ComplexObject()
{
ID = 1;
类=新ExpandableObservableCollection< ComplexSubObject>();
Classes.Add(新ComplexSubObject(){名称=CustomFoo});
Classes.Add(新ComplexSubObject(){名称=我的其他富});
}
}

[ExpandableObject]
公共类ComplexSubObject
{
公共字符串名称{;组; }

[ExpandableObject]
公共ExpandableObservableCollection< SimpleValues>类型{搞定;组; }

公共ComplexSubObject()
{
型=新ExpandableObservableCollection< SimpleValues>();
Types.Add(新SimpleValues(){名称=富,值=酒吧});
Types.Add(新SimpleValues(){名称=酒吧,值=富});
}
}

公共类SimpleValues
{
公共字符串名称{;组; }
公共字符串值{获得;组; }
}

公共类ExpandableObservableCollection< T> :的ObservableCollection< T> ;,
ICustomTypeDescriptor
{
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
//创建一个集合对象持有物业的描述符
PropertyDescriptorCollection PDS =新PropertyDescriptorCollection(NULL);

的for(int i = 0; I<计数;我++)
{
pds.Add(新ItemPropertyDescriptor< T>(这一点,我));
}

返回PDS;
}

#地区使用默认TypeDescriptor东西

AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
返回TypeDescriptor.GetAttributes(这一点,noCustomTypeDesc :真正的);
}

串ICustomTypeDescriptor.GetClassName()
{
返回TypeDescriptor.GetClassName(这一点,noCustomTypeDesc:真正的);
}

串ICustomTypeDescriptor.GetComponentName()
{
返回TypeDescriptor.GetComponentName(这一点,noCustomTypeDesc:真正的);
}

类型转换器ICustomTypeDescriptor.GetConverter()
{
返回TypeDescriptor.GetConverter(这一点,noCustomTypeDesc:真正的);
}

EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
返回TypeDescriptor.GetDefaultEvent(这一点,noCustomTypeDesc:真正的);
}

的PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
返回TypeDescriptor.GetDefaultProperty(这一点,noCustomTypeDesc:真正的);
}

对象ICustomTypeDescriptor.GetEditor(类型editorBaseType)
{
返回TypeDescriptor.GetEditor(这一点,editorBaseType,noCustomTypeDesc:真正的);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
返回TypeDescriptor.GetEvents(这一点,noCustomTypeDesc:真正的);
}

EventDescriptorCollection ICustomTypeDescriptor.GetEvents(属性[]属性)
{
返回TypeDescriptor.GetEvents(这一点,属性,noCustomTypeDesc:真正的);
}

PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(属性[]属性)
{
返回TypeDescriptor.GetProperties(这一点,属性,noCustomTypeDesc:真正的);
}

对象ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor的PD)
{
返回这一点;
}

#endregion
}

公共类ItemPropertyDescriptor< T> :PropertyDescriptor的
{
私人只读的ObservableCollection< T> _所有者;
私人只读INT _index;

公共ItemPropertyDescriptor(的ObservableCollection< T>所有者,INT指数)
:基地(#+指数,NULL)
{
_owner =所有者;
_index =指数;
}

公众覆盖AttributeCollection属性
{
得到
{
VAR属性= TypeDescriptor.GetAttributes(的GetValue(空),FALSE) ;


如果(attributes.OfType<!ExpandableObjectAttribute方式>()任何())
{
//复制所有属性加一个额外的一个(
// ExpandableObjectAttribute)
//这确保即使对象本身的类型不具有
// ExpandableObjectAttribute,它仍然会膨胀。
变种newAttributes =新的属性[attributes.Count + 1];
attributes.CopyTo(newAttributes,newAttributes.Length - 1);
newAttributes [newAttributes.Length - 1] =新ExpandableObjectAttribute();

//覆盖原来的
属性=新AttributeCollection(newAttributes);
}

返回属性;
}
}

公众覆盖布尔CanResetValue(对象部分)
{
返回FALSE;
}

公众覆盖对象的GetValue(对象部分)
{
返回值;
}

私人吨价
=> _owner [_index]

公共覆盖无效ResetValue(对象部分)
{
抛出新NotImplementedException();
}

公共覆盖无效的SetValue(对象组件,对象的值)
{
_owner [_index] =(T)值;
}

公众覆盖布尔ShouldSerializeValue(对象部分)
{
返回FALSE;
}

公众覆盖类型COMPONENTTYPE
=> _owner.GetType();

公众覆盖BOOL IsReadOnly
=>假;

公众覆盖类型属性类型
=>值.GetType()?;
}
}


How, do I display a ObservableCollection<> of custom objects in the Xceed WPF PropertyGrid in which each List Item can be expanded to display the custom objects properties. (ie:

----PropertyGrid-----

CoreClass

  • (+/-) ObservableCollection< CustomClass >

    • (+/-) CustomClass.Object1

      • Property1: Value

      • Property2: Value

      • PropertyN: Value

    • (+/-) CustomClass.Object2

      • Property1: Value

      • Property2: Value

      • PropertyN: Value

If I use [ExpandableObject] on the ObservableCollection<> it only shows the Counts property.

Edit:(Added code)

MainWindow.xaml:

<Window x:Class="PropGridExample.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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:PropGridExample"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <xctk:PropertyGrid x:Name="PropertyGrid" SelectedObject="{Binding BindingItem}"></xctk:PropertyGrid>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        MainWindowViewModel mwvm = new MainWindowViewModel();
        this.DataContext = mwvm;
        InitializeComponent();
    }
}

MainWindowViewModel.cs

public class MainWindowViewModel
{
    public Item BindingItem { get; set; }

    public MainWindowViewModel()
    {
        BindingItem = new Item();
    }

    public class Item
    {
        public int ID { get; set; }
        [ExpandableObject()]
        public ObservableCollection<CustomClass> Classes { get; set; }

        public Item()
        {
            ID = 1;
            Classes = new ObservableCollection<CustomClass>();
            Classes.Add(new CustomClass() { Name = "CustomFoo" });
        }
    }

    public class CustomClass
    {
        public string Name { get; set; }
        [ExpandableObject()]
        public ObservableCollection<type> Types { get; set; }

        public CustomClass()
        {
            Types = new ObservableCollection<type>();
            Types.Add(new type() { name = "foo", value = "bar" });
            Types.Add(new type() { name = "bar", value = "foo" });
        }
    }

    public class type
    {
        public string name { get; set; }
        public string value { get; set; }
    }
}

解决方案

Note that most of this idea comes from the CodeProject project you linked to. The article gets you most of the way there, but as you note, it does not expand each item in the collection for the WPF PropertyGrid. In order to do that, each "item" needs to have an ExpandableObjectAttribute.

In order to allow future StackOverflow readers to understand, I'm going to start from beginning.

From the beginning

So, starting from this example:

public class MainWindowViewModel
{
  /// <summary> This the object we want to be able to edit in the data grid. </summary>
  public ComplexObject BindingComplexObject { get; set; }

  public MainWindowViewModel()
  {
    BindingComplexObject = new ComplexObject();
  }
}

public class ComplexObject
{
  public int ID { get; set; }

  public ObservableCollection<ComplexSubObject> Classes { get; set; }

  public ComplexObject()
  {
    ID = 1;
    Classes = new ObservableCollection<ComplexSubObject>();
    Classes.Add(new ComplexSubObject() { Name = "CustomFoo" });
    Classes.Add(new ComplexSubObject() { Name = "My Other Foo" });
  }
}

public class ComplexSubObject
{
  public string Name { get; set; }

  public ObservableCollection<SimpleValues> Types { get; set; }

  public ComplexSubObject()
  {
    Types = new ObservableCollection<SimpleValues>();
    Types.Add(new SimpleValues() { name = "foo", value = "bar" });
    Types.Add(new SimpleValues() { name = "bar", value = "foo" });
  }
}

public class SimpleValues
{
  public string name { get; set; }
  public string value { get; set; }
}

In order for the WPF PropertyGrid to be able to edit each item in the ObservableCollection, we need to provide a type descriptor for collection, and return the items as "Properties" of the collection that can be edited. Because we cannot statically determine the items from a collection (as each collection has different number of elements), it means that the collection itself must be the TypeDescriptor, which means implementing ICustomTypeDescriptor.

(note that only GetProperties is important for our purposes, the rest just delegates to TypeDescriptor):

public class ExpandableObservableCollection<T> : ObservableCollection<T>,
                                                 ICustomTypeDescriptor
{
  PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
  {
    // Create a collection object to hold property descriptors
    PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

    for (int i = 0; i < Count; i++)
    {
      pds.Add(new ItemPropertyDescriptor<T>(this, i));
    }

    return pds;
  }

  #region Use default TypeDescriptor stuff

  AttributeCollection ICustomTypeDescriptor.GetAttributes()
  {
    return TypeDescriptor.GetAttributes(this, noCustomTypeDesc: true);
  }

  string ICustomTypeDescriptor.GetClassName()
  {
    return TypeDescriptor.GetClassName(this, noCustomTypeDesc: true);
  }

  string ICustomTypeDescriptor.GetComponentName()
  {
    return TypeDescriptor.GetComponentName(this, noCustomTypeDesc: true);
  }

  TypeConverter ICustomTypeDescriptor.GetConverter()
  {
    return TypeDescriptor.GetConverter(this, noCustomTypeDesc: true);
  }

  EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
  {
    return TypeDescriptor.GetDefaultEvent(this, noCustomTypeDesc: true);
  }

  PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
  {
    return TypeDescriptor.GetDefaultProperty(this, noCustomTypeDesc: true);
  }

  object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
  {
    return TypeDescriptor.GetEditor(this, editorBaseType, noCustomTypeDesc: true);
  }

  EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
  {
    return TypeDescriptor.GetEvents(this, noCustomTypeDesc: true);
  }

  EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
  {
    return TypeDescriptor.GetEvents(this, attributes, noCustomTypeDesc: true);
  }

  PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
  {
    return TypeDescriptor.GetProperties(this, attributes, noCustomTypeDesc: true);
  }

  object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
  {
    return this;
  }

  #endregion
}

Additionally, we need an implementation of ItemPropertyDescriptor, which I provide here:

public class ItemPropertyDescriptor<T> : PropertyDescriptor
{
  private readonly ObservableCollection<T> _owner;
  private readonly int _index;

  public ItemPropertyDescriptor(ObservableCollection<T> owner, int index)
    : base("#" + index, null)
  {
    _owner = owner;
    _index = index;
  }

  public override AttributeCollection Attributes
  {
    get
    {
      var attributes = TypeDescriptor.GetAttributes(GetValue(null), false);
      if (!attributes.OfType<ExpandableObjectAttribute>().Any())
      {
        // copy all the attributes plus an extra one (the
        // ExpandableObjectAttribute)
        // this ensures that even if the type of the object itself doesn't have the
        // ExpandableObjectAttribute, it will still be expandable. 
        var newAttributes = new Attribute[attributes.Count + 1];
        attributes.CopyTo(newAttributes, newAttributes.Length - 1);
        newAttributes[newAttributes.Length - 1] = new ExpandableObjectAttribute();

        // overwrite the array
        attributes = new AttributeCollection(newAttributes);
      }

      return attributes;
    }
  }

  public override bool CanResetValue(object component)
  {
    return false;
  }

  public override object GetValue(object component)
  {
    return Value;
  }

  private T Value
    => _owner[_index];

  public override void ResetValue(object component)
  {
    throw new NotImplementedException();
  }

  public override void SetValue(object component, object value)
  {
    _owner[_index] = (T)value;
  }

  public override bool ShouldSerializeValue(object component)
  {
    return false;
  }

  public override Type ComponentType
    => _owner.GetType();

  public override bool IsReadOnly
    => false;

  public override Type PropertyType
    => Value?.GetType();
}

Which for the most part, just sets up reasonable defaults, which you can tweak to serve your needs.

One thing to note is that you may implement the Attributes property differently, depending on your use case. If you don't do the "add it to the attribute collection if it's not there", then you need to add the attribute to the classes/types that you want to expand; if you do keep that code in, then you'll be able to expand every item in the collection no matter if the class/type has the attribute or not.

It then becomes a matter of using ExpandableObservableCollection in place of ObservableCollection. This kind of sucks as it means your ViewModel has view-stuff-ish stuff in it, but ¯\_(ツ)_/¯.

Additionally, you need to add the ExpandableObjectAttribute to each of the properties that is a ExpandableObservableCollection.

Code Dump

If you're following along at home, you can use the following dialog code to run the example:

<Window x:Class="WpfDemo.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:WpfDemo"
        xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
      <xctk:PropertyGrid x:Name="It" />
    </Grid>
</Window>

-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace WpfDemo
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();

      It.SelectedObject = new MainWindowViewModel().BindingComplexObject;
    }
  }
}

And here's the complete ViewModel implementation:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;

namespace WpfDemo
{
  public class MainWindowViewModel
  {
    /// <summary> This the object we want to be able to edit in the data grid. </summary>
    public ComplexObject BindingComplexObject { get; set; }

    public MainWindowViewModel()
    {
      BindingComplexObject = new ComplexObject();
    }
  }

  [ExpandableObject]
  public class ComplexObject
  {
    public int ID { get; set; }

    [ExpandableObject]
    public ExpandableObservableCollection<ComplexSubObject> Classes { get; set; }

    public ComplexObject()
    {
      ID = 1;
      Classes = new ExpandableObservableCollection<ComplexSubObject>();
      Classes.Add(new ComplexSubObject() { Name = "CustomFoo" });
      Classes.Add(new ComplexSubObject() { Name = "My Other Foo" });
    }
  }

  [ExpandableObject]
  public class ComplexSubObject
  {
    public string Name { get; set; }

    [ExpandableObject]
    public ExpandableObservableCollection<SimpleValues> Types { get; set; }

    public ComplexSubObject()
    {
      Types = new ExpandableObservableCollection<SimpleValues>();
      Types.Add(new SimpleValues() { name = "foo", value = "bar" });
      Types.Add(new SimpleValues() { name = "bar", value = "foo" });
    }
  }

  public class SimpleValues
  {
    public string name { get; set; }
    public string value { get; set; }
  }

  public class ExpandableObservableCollection<T> : ObservableCollection<T>,
                                                   ICustomTypeDescriptor
  {
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
    {
      // Create a collection object to hold property descriptors
      PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);

      for (int i = 0; i < Count; i++)
      {
        pds.Add(new ItemPropertyDescriptor<T>(this, i));
      }

      return pds;
    }

    #region Use default TypeDescriptor stuff

    AttributeCollection ICustomTypeDescriptor.GetAttributes()
    {
      return TypeDescriptor.GetAttributes(this, noCustomTypeDesc: true);
    }

    string ICustomTypeDescriptor.GetClassName()
    {
      return TypeDescriptor.GetClassName(this, noCustomTypeDesc: true);
    }

    string ICustomTypeDescriptor.GetComponentName()
    {
      return TypeDescriptor.GetComponentName(this, noCustomTypeDesc: true);
    }

    TypeConverter ICustomTypeDescriptor.GetConverter()
    {
      return TypeDescriptor.GetConverter(this, noCustomTypeDesc: true);
    }

    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
    {
      return TypeDescriptor.GetDefaultEvent(this, noCustomTypeDesc: true);
    }

    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
    {
      return TypeDescriptor.GetDefaultProperty(this, noCustomTypeDesc: true);
    }

    object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
    {
      return TypeDescriptor.GetEditor(this, editorBaseType, noCustomTypeDesc: true);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
      return TypeDescriptor.GetEvents(this, noCustomTypeDesc: true);
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
    {
      return TypeDescriptor.GetEvents(this, attributes, noCustomTypeDesc: true);
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
    {
      return TypeDescriptor.GetProperties(this, attributes, noCustomTypeDesc: true);
    }

    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
    {
      return this;
    }

    #endregion
  }

  public class ItemPropertyDescriptor<T> : PropertyDescriptor
  {
    private readonly ObservableCollection<T> _owner;
    private readonly int _index;

    public ItemPropertyDescriptor(ObservableCollection<T> owner, int index)
      : base("#" + index, null)
    {
      _owner = owner;
      _index = index;
    }

    public override AttributeCollection Attributes
    {
      get
      {
        var attributes = TypeDescriptor.GetAttributes(GetValue(null), false);


        if (!attributes.OfType<ExpandableObjectAttribute>().Any())
        {
          // copy all the attributes plus an extra one (the
          // ExpandableObjectAttribute)
          // this ensures that even if the type of the object itself doesn't have the
          // ExpandableObjectAttribute, it will still be expandable. 
          var newAttributes = new Attribute[attributes.Count + 1];
          attributes.CopyTo(newAttributes, newAttributes.Length - 1);
          newAttributes[newAttributes.Length - 1] = new ExpandableObjectAttribute();

          // overwrite the original
          attributes = new AttributeCollection(newAttributes);
        }

        return attributes;
      }
    }

    public override bool CanResetValue(object component)
    {
      return false;
    }

    public override object GetValue(object component)
    {
      return Value;
    }

    private T Value
      => _owner[_index];

    public override void ResetValue(object component)
    {
      throw new NotImplementedException();
    }

    public override void SetValue(object component, object value)
    {
      _owner[_index] = (T)value;
    }

    public override bool ShouldSerializeValue(object component)
    {
      return false;
    }

    public override Type ComponentType
      => _owner.GetType();

    public override bool IsReadOnly
      => false;

    public override Type PropertyType
      => Value?.GetType();
  }
}

这篇关于Xceed PropertyGrid的WPF展示为扩大收集项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆