- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
如何显示 ObservableCollection<>
Xceed WPF PropertyGrid 中的自定义对象,其中每个列表项都可以展开以显示自定义对象属性。 (即:
----属性网格-----
核心类
(+/-) ObservableCollection< CustomClass >
(+/-) CustomClass.Object1
属性 1:值
属性 2:值(value)
…
属性N:值(value)
(+/-) CustomClass.Object2
属性 1:值
属性 2:值(value)
…
属性N:值(value)
如果我使用 [ExpandableObject]
在 ObservableCollection<>
上它只显示 Counts 属性。
编辑:(添加代码)
主窗口.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>
主窗口.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; }
}
}
最佳答案
请注意,这个想法大部分来自 CodeProject project you linked to .这篇文章向您介绍了大部分内容,但正如您所注意到的,它并未展开 WPF PropertyGrid 集合中的每个项目。为此,每个“项目”都需要有一个 ExpandableObjectAttribute
。
为了让 future 的 StackOverflow 读者能够理解,我将从头开始。
所以,从这个例子开始:
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; }
}
为了让 WPF PropertyGrid 能够编辑 ObservableCollection 中的每个项目,我们需要为集合提供一个类型描述符,它将项目作为该集合的“属性”返回,以便可以编辑它们。因为我们不能静态地确定集合中的项目(因为每个集合都有不同数量的元素),这意味着集合本身必须是 TypeDescriptor,这意味着实现 ICustomTypeDescriptor
。
(注意只有 GetProperties
对我们的目的很重要,其余的只是委托(delegate)给 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
}
此外,我们需要一个ItemPropertyDescriptor
的实现,我在这里提供:
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();
}
在大多数情况下,只需设置合理的默认值,您可以对其进行调整以满足您的需求。
需要注意的一件事是,您可以根据您的用例以不同方式实现 Attributes
属性。如果您不执行“如果不存在则将其添加到属性集合”,那么您需要将属性添加到要扩展的类/类型;如果您确实保留了该代码,那么无论类/类型是否具有属性,您都可以展开集合中的每个项目。
然后就变成了使用 ExpandableObservableCollection
代替 ObservableCollection
的问题。这种情况很糟糕,因为它意味着您的 ViewModel
中有类似 View 的东西,但是 ¯\_(ツ)_/¯
。
此外,您需要将 ExpandableObjectAttribute
添加到属于 ExpandableObservableCollection
的每个属性。
如果您在家跟随,则可以使用以下对话代码来运行该示例:
<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;
}
}
}
下面是完整的 ViewModel 实现:
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();
}
}
关于c# - Xceed WPF propertyGrid 显示扩展集合的项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36286530/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!