Vâng, điều này có lẽ ngây thơ hơn một chút so với bạn nơi mong đợi, nhưng nó có thể có thể cung cấp cho bạn một điểm khởi đầu. Nó có thể làm với một số tái cấu trúc, nhưng nó đã được thực hiện theo nghĩa đen trong 15 phút để mang nó cho những gì nó được, mà không được kiểm tra tốt hoặc sử dụng bất kỳ fancies WPF cho rằng vấn đề.
Lần đầu tiên một UserControl đơn giản mà chỉ tổ chức một 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>
Các mã sau này sẽ chỉ có một tính chất gọi là ObjectGraph
, điều này được thiết lập để thể hiện của đối tượng bạn muốn duyệt.
Cây chỉ được tải với cấp thuộc tính đầu tiên mỗi nút có định dạng PropertyName: Value hoặc PropertyName: Loại, nếu thuộc tính là kiểu nguyên thủy (xem hàm IsPrimitive), thì giá trị được hiển thị, nếu không thì chuỗi rỗng được thêm vào dưới dạng nút con. Việc thêm chuỗi trống cho biết người dùng có thể mở rộng nút.
Khi nút được thoát ra, kiểm tra nhanh được thực hiện để xem liệu đứa con đầu tiên có phải là một chuỗi rỗng hay không, nếu nút đó bị xóa và thuộc tính cho nút đó được tải vào cây.
Vì vậy, điều này về cơ bản xây dựng cây lên khi nút được mở rộng. Điều này làm như dễ dàng hơn vì hai lý do
1- Không cần phải thực hiện đệ quy
2- Không cần phải phát hiện tài liệu tham khảo theo chu kỳ đó sẽ mở rộng để vĩnh cửu hoặc một số tài nguyên bị cạn kiệt, trong đó bao giờ xảy ra trước.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Reflection;
namespace ObjectBrowser
{
public partial class PropertyTree : UserControl
{
public PropertyTree()
{
InitializeComponent();
}
private void treeView1_Expanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = e.OriginalSource as TreeViewItem;
if (item.Items.Count == 1 && item.Items[0].ToString() == string.Empty)
{
LoadGraph(item.Items, item.Tag);
}
}
public object ObjectGraph
{
get { return (object)GetValue(ObjectGraphProperty); }
set { SetValue(ObjectGraphProperty, value); }
}
public static readonly DependencyProperty ObjectGraphProperty =
DependencyProperty.Register("ObjectGraph", typeof(object), typeof(PropertyTree),
new UIPropertyMetadata(0, OnObjectGraphPropertyChanged));
private static void OnObjectGraphPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
PropertyTree control = source as PropertyTree;
if (control != null)
{
control.OnObjectGraphChanged(source, EventArgs.Empty);
}
}
protected virtual void OnObjectGraphChanged(object sender, EventArgs e)
{
LoadGraph(treeView1.Items, ObjectGraph);
}
private void LoadGraph(ItemCollection nodeItems, object instance)
{
nodeItems.Clear();
if (instance == null) return;
Type instanceType = instance.GetType();
foreach (PropertyInfo pi in instanceType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
object propertyValue =pi.GetValue(instance, null);
TreeViewItem item = new TreeViewItem();
item.Header = BuildItemText(instance, pi, propertyValue);
if (!IsPrimitive(pi) && propertyValue != null)
{
item.Items.Add(string.Empty);
item.Tag = propertyValue;
}
nodeItems.Add(item);
}
}
private string BuildItemText(object instance, PropertyInfo pi, object value)
{
string s = string.Empty;
if (value == null)
{
s = "<null>";
}
else if (IsPrimitive(pi))
{
s = value.ToString();
}
else
{
s = pi.PropertyType.Name;
}
return pi.Name + " : " + s;
}
private bool IsPrimitive(PropertyInfo pi)
{
return pi.PropertyType.IsPrimitive || typeof(string) == pi.PropertyType;
}
}
}
Sử dụng điều khiển khá đơn giản. Ở đây tôi sẽ chỉ đặt điều khiển trên Form và sau đó đặt ObjectGraph thành một thể hiện của một đối tượng, tôi tự ý chọn 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>
Mã đằng sau
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;
}
}
}
Tất nhiên điều này vẫn sẽ cần rất nhiều công việc, xử lý đặc biệt với nhiều loại như mảng có thể là một cơ chế để xử lý quan điểm tùy chỉnh đến các loại đặc biệt, v.v.
Bạn không biết mình tiết kiệm được bao nhiêu thời gian! Tôi biết ý kiến không phải là "Cảm ơn" - nhưng dành 8 phút để sao chép và điều chỉnh thay vì phát triển trong 80 phút ... Bạn và Chris xứng đáng nhận được lời cảm ơn lớn! –
@ G.Y nhận xét như vậy là lý do tại sao tôi tiếp tục cố gắng trả lời các câu hỏi về SO. Cảm ơn ** bạn ** –
Zachary - Great work. Nó thực sự giúp tôi tiết kiệm thời gian. Vì lợi ích của tất cả tôi đã tải lên một dự án làm việc để codeplex có thể được tìm thấy ở đây: https://wpfobjecttreeview.codeplex.com/ –