C# 正在查找WPF的对象图树视图控件
我试图找到一段代码或一个预打包控件,它接受一个对象图,并在树视图中(递归地)显示公共属性和属性值。即使是一个简单的实现也可以,我只是需要一些东西开始C# 正在查找WPF的对象图树视图控件,c#,reflection,treeview,wpf-controls,C#,Reflection,Treeview,Wpf Controls,我试图找到一段代码或一个预打包控件,它接受一个对象图,并在树视图中(递归地)显示公共属性和属性值。即使是一个简单的实现也可以,我只是需要一些东西开始 解决方案必须是WPF,而不是winforms或com等等。好吧,这可能比您希望的要幼稚一些,但它可能会给您一个起点。它可以做一些重构,但实际上它是在15分钟内完成的,所以就拿它来说吧,它没有经过很好的测试,也没有使用任何WPF的想法 首先是一个简单的UserControl,它只托管一个TreeView <UserControl x:Class
解决方案必须是WPF,而不是winforms或com等等。好吧,这可能比您希望的要幼稚一些,但它可能会给您一个起点。它可以做一些重构,但实际上它是在15分钟内完成的,所以就拿它来说吧,它没有经过很好的测试,也没有使用任何WPF的想法 首先是一个简单的UserControl,它只托管一个TreeView
<UserControl x:Class="ObjectBrowser.PropertyTree"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TreeView Name="treeView1" TreeViewItem.Expanded="treeView1_Expanded" />
</Grid>
</UserControl>
使用该控件非常简单。在这里,我将把控件放在表单上,然后将ObjectGraph设置为一个对象的实例,我任意选择了XmlDataProvider
XAML
<Window x:Class="ObjectBrowser.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" xmlns:my="clr-namespace:ObjectBrowser" Loaded="Window_Loaded">
<Grid>
<my:PropertyTree x:Name="propertyTree1" />
</Grid>
</Window>
隐藏的代码
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace ObjectBrowser
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var o = new XmlDataProvider();
o.Source = new Uri("http://www.stackoverflow.com");
propertyTree1.ObjectGraph = o;
}
}
}
使用系统;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
命名空间对象浏览器
{
///
///MainWindow.xaml的交互逻辑
///
公共部分类主窗口:窗口
{
公共主窗口()
{
初始化组件();
}
已加载私有无效窗口(对象发送器、路由目标)
{
var o=新的XmlDataProvider();
o、 Source=新Uri(“http://www.stackoverflow.com");
propertyTree1.ObjectGraph=o;
}
}
}
当然,这仍然需要大量工作,对数组等类型的特殊处理可能是将自定义视图处理为特殊类型等的机制。因此,我从Chris Taylor的示例和的结构中选取了部分内容,并将它们合并到以下内容中: TreeView xaml:
<TreeView Name="tvObjectGraph" ItemsSource="{Binding FirstGeneration}" Margin="12,41,12,12" FontSize="13" FontFamily="Consolas">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" Grid.Row="0" Padding="2,0" />
<TextBlock Text="{Binding Type}" Grid.Column="1" Grid.Row="0" Padding="2,0" />
<TextBlock Text="{Binding Value}" Grid.Column="2" Grid.Row="0" Padding="2,0" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public class ObjectViewModel : INotifyPropertyChanged
{
ReadOnlyCollection<ObjectViewModel> _children;
readonly ObjectViewModel _parent;
readonly object _object;
readonly PropertyInfo _info;
readonly Type _type;
bool _isExpanded;
bool _isSelected;
public ObjectViewModel(object obj)
: this(obj, null, null)
{
}
ObjectViewModel(object obj, PropertyInfo info, ObjectViewModel parent)
{
_object = obj;
_info = info;
if (_object != null)
{
_type = obj.GetType();
if (!IsPrintableType(_type))
{
// load the _children object with an empty collection to allow the + expander to be shown
_children = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { new ObjectViewModel(null) });
}
}
_parent = parent;
}
public void LoadChildren()
{
if (_object != null)
{
// exclude value types and strings from listing child members
if (!IsPrintableType(_type))
{
// the public properties of this object are its children
var children = _type.GetProperties()
.Where(p => !p.GetIndexParameters().Any()) // exclude indexed parameters for now
.Select(p => new ObjectViewModel(p.GetValue(_object, null), p, this))
.ToList();
// if this is a collection type, add the contained items to the children
var collection = _object as IEnumerable;
if (collection != null)
{
foreach (var item in collection)
{
children.Add(new ObjectViewModel(item, null, this)); // todo: add something to view the index value
}
}
_children = new ReadOnlyCollection<ObjectViewModel>(children);
this.OnPropertyChanged("Children");
}
}
}
/// <summary>
/// Gets a value indicating if the object graph can display this type without enumerating its children
/// </summary>
static bool IsPrintableType(Type type)
{
return type != null && (
type.IsPrimitive ||
type.IsAssignableFrom(typeof(string)) ||
type.IsEnum);
}
public ObjectViewModel Parent
{
get { return _parent; }
}
public PropertyInfo Info
{
get { return _info; }
}
public ReadOnlyCollection<ObjectViewModel> Children
{
get { return _children; }
}
public string Type
{
get
{
var type = string.Empty;
if (_object != null)
{
type = string.Format("({0})", _type.Name);
}
else
{
if (_info != null)
{
type = string.Format("({0})", _info.PropertyType.Name);
}
}
return type;
}
}
public string Name
{
get
{
var name = string.Empty;
if (_info != null)
{
name = _info.Name;
}
return name;
}
}
public string Value
{
get
{
var value = string.Empty;
if (_object != null)
{
if (IsPrintableType(_type))
{
value = _object.ToString();
}
}
else
{
value = "<null>";
}
return value;
}
}
#region Presentation Members
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (_isExpanded != value)
{
_isExpanded = value;
if (_isExpanded)
{
LoadChildren();
}
this.OnPropertyChanged("IsExpanded");
}
// Expand all the way up to the root.
if (_isExpanded && _parent != null)
{
_parent.IsExpanded = true;
}
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
}
public bool NameContains(string text)
{
if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Name))
{
return false;
}
return Name.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
}
public bool ValueContains(string text)
{
if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Value))
{
return false;
}
return Value.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class ObjectViewModelHierarchy
{
readonly ReadOnlyCollection<ObjectViewModel> _firstGeneration;
readonly ObjectViewModel _rootObject;
public ObjectViewModelHierarchy(object rootObject)
{
_rootObject = new ObjectViewModel(rootObject);
_firstGeneration = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { _rootObject });
}
public ReadOnlyCollection<ObjectViewModel> FirstGeneration
{
get { return _firstGeneration; }
}
}
ObjectViewModel.cs:
<TreeView Name="tvObjectGraph" ItemsSource="{Binding FirstGeneration}" Margin="12,41,12,12" FontSize="13" FontFamily="Consolas">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Name}" Grid.Column="0" Grid.Row="0" Padding="2,0" />
<TextBlock Text="{Binding Type}" Grid.Column="1" Grid.Row="0" Padding="2,0" />
<TextBlock Text="{Binding Value}" Grid.Column="2" Grid.Row="0" Padding="2,0" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
public class ObjectViewModel : INotifyPropertyChanged
{
ReadOnlyCollection<ObjectViewModel> _children;
readonly ObjectViewModel _parent;
readonly object _object;
readonly PropertyInfo _info;
readonly Type _type;
bool _isExpanded;
bool _isSelected;
public ObjectViewModel(object obj)
: this(obj, null, null)
{
}
ObjectViewModel(object obj, PropertyInfo info, ObjectViewModel parent)
{
_object = obj;
_info = info;
if (_object != null)
{
_type = obj.GetType();
if (!IsPrintableType(_type))
{
// load the _children object with an empty collection to allow the + expander to be shown
_children = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { new ObjectViewModel(null) });
}
}
_parent = parent;
}
public void LoadChildren()
{
if (_object != null)
{
// exclude value types and strings from listing child members
if (!IsPrintableType(_type))
{
// the public properties of this object are its children
var children = _type.GetProperties()
.Where(p => !p.GetIndexParameters().Any()) // exclude indexed parameters for now
.Select(p => new ObjectViewModel(p.GetValue(_object, null), p, this))
.ToList();
// if this is a collection type, add the contained items to the children
var collection = _object as IEnumerable;
if (collection != null)
{
foreach (var item in collection)
{
children.Add(new ObjectViewModel(item, null, this)); // todo: add something to view the index value
}
}
_children = new ReadOnlyCollection<ObjectViewModel>(children);
this.OnPropertyChanged("Children");
}
}
}
/// <summary>
/// Gets a value indicating if the object graph can display this type without enumerating its children
/// </summary>
static bool IsPrintableType(Type type)
{
return type != null && (
type.IsPrimitive ||
type.IsAssignableFrom(typeof(string)) ||
type.IsEnum);
}
public ObjectViewModel Parent
{
get { return _parent; }
}
public PropertyInfo Info
{
get { return _info; }
}
public ReadOnlyCollection<ObjectViewModel> Children
{
get { return _children; }
}
public string Type
{
get
{
var type = string.Empty;
if (_object != null)
{
type = string.Format("({0})", _type.Name);
}
else
{
if (_info != null)
{
type = string.Format("({0})", _info.PropertyType.Name);
}
}
return type;
}
}
public string Name
{
get
{
var name = string.Empty;
if (_info != null)
{
name = _info.Name;
}
return name;
}
}
public string Value
{
get
{
var value = string.Empty;
if (_object != null)
{
if (IsPrintableType(_type))
{
value = _object.ToString();
}
}
else
{
value = "<null>";
}
return value;
}
}
#region Presentation Members
public bool IsExpanded
{
get { return _isExpanded; }
set
{
if (_isExpanded != value)
{
_isExpanded = value;
if (_isExpanded)
{
LoadChildren();
}
this.OnPropertyChanged("IsExpanded");
}
// Expand all the way up to the root.
if (_isExpanded && _parent != null)
{
_parent.IsExpanded = true;
}
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
this.OnPropertyChanged("IsSelected");
}
}
}
public bool NameContains(string text)
{
if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Name))
{
return false;
}
return Name.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
}
public bool ValueContains(string text)
{
if (String.IsNullOrEmpty(text) || String.IsNullOrEmpty(Value))
{
return false;
}
return Value.IndexOf(text, StringComparison.InvariantCultureIgnoreCase) > -1;
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class ObjectViewModelHierarchy
{
readonly ReadOnlyCollection<ObjectViewModel> _firstGeneration;
readonly ObjectViewModel _rootObject;
public ObjectViewModelHierarchy(object rootObject)
{
_rootObject = new ObjectViewModel(rootObject);
_firstGeneration = new ReadOnlyCollection<ObjectViewModel>(new ObjectViewModel[] { _rootObject });
}
public ReadOnlyCollection<ObjectViewModel> FirstGeneration
{
get { return _firstGeneration; }
}
}
公共类ObjectViewModel:INotifyPropertyChanged
{
只读集合儿童;
只读ObjectViewModel\u父对象;
只读对象_对象;
只读属性信息;
只读类型_类型;
布尔(u)被扩张;;
布尔乌当选;
公共对象视图模型(对象对象对象)
:此(对象、空、空)
{
}
ObjectViewModel(对象对象对象、属性信息、ObjectViewModel父对象)
{
_对象=obj;
_信息=信息;
如果(_object!=null)
{
_type=obj.GetType();
如果(!IsPrintableType(_type))
{
//加载带有空集合的_children对象,以允许显示+扩展器
_children=new ReadOnlyCollection(new ObjectViewModel[]{new ObjectViewModel(null)});
}
}
_父母=父母;
}
公共儿童
{
如果(_object!=null)
{
//从列出的子成员中排除值类型和字符串
如果(!IsPrintableType(_type))
{
//此对象的公共属性是其子对象
var children=\u type.GetProperties()
.Where(p=>!p.GetIndexParameters().Any())//暂时排除索引参数
.Select(p=>newObjectViewModel(p.GetValue(_object,null),p,this))
.ToList();
//如果这是集合类型,请将包含的项添加到子项
变量集合=_对象作为IEnumerable;
if(集合!=null)
{
foreach(集合中的var项)
{
Add(newObjectViewModel(item,null,this));//todo:添加一些内容以查看索引值
}
}
_children=新的只读集合(children);
本协议项下的不动产变更(“子女”);
}
}
}
///
///获取一个值,该值指示对象图是否可以在不枚举其子对象的情况下显示此类型
///
静态布尔iPrintableType(类型)
{
返回类型!=null&&(
type.IsPrimitive||
type.IsAssignableFrom(typeof(string))||
类型(IsEnum);
}
公共对象视图模型父对象
{
获取{return\u parent;}
}
公共财产信息
{
获取{return\u info;}
}
公共只读集合子项
{
获取{return\u children;}
}
公共字符串类型
{
得到
{
var type=string.Empty;
如果(_object!=null)
{
type=string.Format(“({0})”,_type.Name);
}
其他的
{
如果(_info!=null)
{
type=string.Format(“({0})”,_info.PropertyType.Name);
}
}
返回类型;
}
}
公共字符串名
{
得到
{
var name=string.Empty;
如果(_info!=null)
{
name=_info.name;
}
返回名称;
}
}
公共字符串值
{
得到
{
var值=string.Empty;
如果(_object!=null)
{
if(IsPrintableType(_-type))
{
value=_object.ToString();
}
}
其他的
{
value=“”;
}
返回值;
}
}
#区域演示文稿成员
公共图书馆被扩展了
{
获取{return\u isExpanded;}
设置
{
如果(_isExpanded!=值)
{
_isExpanded=值;
如果(_i扩展)
{
LoadChildren();
}
本协议项下的不动产变更(“IsExpanded”);
}
//一直展开到根。
if(_isExpanded&&u parent!=null)
{