Eh bien, cela est probablement un peu plus naïf que vous où en espérant, mais il pourrait peut-être vous donner un point de départ. Cela pourrait être fait avec un refactoring, mais cela a été fait littéralement en 15 minutes alors prenez-le pour ce qu'il est, ce qui n'est pas bien testé ou en utilisant des fantaisies WPF d'ailleurs.
D'abord simple UserControl qui accueille juste un 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>
Le code derrière pour cela aura juste une propriété appelée ObjectGraph
, ce paramètre est réglé à l'instance de l'objet que vous souhaitez parcourir.
L'arborescence est uniquement chargée avec le premier niveau de propriétés chaque nœud a le format PropertyName: Value ou PropertyName: Type, si la propriété est un type primitif (voir la fonction IsPrimitive), la valeur est affichée, sinon un Une chaîne vide est ajoutée en tant que noeud enfant. L'ajout de la chaîne vide indique à l'utilisateur que le noeud peut être développé. Lorsque le noeud est expiré, une vérification rapide est effectuée pour voir si le premier enfant est une chaîne vide, si c'est le cas, le noeud est effacé et les propriétés de ce noeud sont chargées dans l'arbre. Donc, cela construit fondamentalement l'arborescence au fur et à mesure que les nœuds sont développés. Cela rend plus facile comme pour deux raisons
1- Pas besoin d'effectuer récursion
2- Pas besoin de détecter des références cycliques qui étendront à l'éternité ou une ressource appauvrie, qui vient toujours en premier.
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;
}
}
}
L'utilisation du contrôle est assez simple. Ici, je vais juste mettre le contrôle sur Form et ensuite définir le ObjectGraph à une instance d'un objet, j'ai arbitrairement choisi 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>
Le code derrière
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;
}
}
}
Bien sûr, cela aurait encore besoin de beaucoup de travail, un traitement spécial pour les types comme des tableaux peut-être un mécanisme pour gérer la coutume vue à des types spéciaux etc.
Vous n'avez aucune idée de combien vous m'avez sauvé du temps! Je sais que les commentaires ne sont pas pour "Merci" - mais passer 8 minutes pour copier et ajuster au lieu de développer pour 80 minutes ... Vous et Chris méritent un grand merci! –
@ G.Y commentaires comme ça sont pourquoi je continue à essayer de répondre aux questions sur SO. Merci ** vous ** –
Zachary - Excellent travail. Cela m'a vraiment fait gagner du temps. Pour le bénéfice de tous j'ai téléchargé un projet de codage qui peut être trouvé ici: https://wpfobjecttreeview.codeplex.com/ –