开发者

Bind Object to WPF TreeView

I would like to know how to bind a custom data type to a TreeView.

The data type is basically an arraylist of objects that contain other arraylists. Access would look something like this:

foreach (DeviceGroup dg in开发者_如何学JAVA system.deviceGroups)
    {
        foreach (DeviceType dt in dg.deviceTypes)
        {
            foreach (DeviceInstance di in dt.deviceInstances)
            {

            }
        }
    }

I would like the TreeView to look something like this:

DeviceGroup1

 --> DeviceType1
      --DeviceInstance1
      --DeviceInstance2
 --> DeviceType2
      --DeviceInstance1

DeviceGroup2

 --> DeviceType1
      --DeviceInstance1
 --> DeviceType2


Ok this is where the HierarchicalDataTemplate will save you. The trick is you will need to use two different hierarchical templates, since you have a three-level hierarchy here. I have constructed a simple UserControl to illustrate. First, here is some code-behind creating model data similar to what you have:

public partial class ThreeLevelTreeView : UserControl
{
    public ArrayList DeviceGroups { get; private set; }

    public ThreeLevelTreeView()
    {
        DeviceInstance inst1 = new DeviceInstance() { Name = "Instance1" };
        DeviceInstance inst2 = new DeviceInstance() { Name = "Instance2" };
        DeviceInstance inst3 = new DeviceInstance() { Name = "Instance3" };
        DeviceInstance inst4 = new DeviceInstance() { Name = "Instance4" };

        DeviceType type1 = new DeviceType() { Name = "Type1", DeviceInstances = new ArrayList() { inst1, inst2 } };
        DeviceType type2 = new DeviceType() { Name = "Type2", DeviceInstances = new ArrayList() { inst3 } };
        DeviceType type3 = new DeviceType() { Name = "Type3", DeviceInstances = new ArrayList() { inst4 } };
        DeviceType type4 = new DeviceType() { Name = "Type4" };

        DeviceGroup group1 = new DeviceGroup() { Name = "Group1", DeviceTypes = new ArrayList() { type1, type2 } };
        DeviceGroup group2 = new DeviceGroup() { Name = "Group2", DeviceTypes = new ArrayList() { type3, type4 } };

        DeviceGroups = new ArrayList() { group1, group2 };

        InitializeComponent();
    }
}

public class DeviceGroup
{
    public string Name { get; set; }
    public ArrayList DeviceTypes { get; set; }
}

public class DeviceType
{
    public string Name { get; set; }
    public ArrayList DeviceInstances { get; set; }
}

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

Nothing difficult here, but note that you should use ObservableCollection instead of ArrayList if you want to add and remove from your collections dynamically. Now let's look at the XAML for this control:

<UserControl x:Class="TestWpfApplication.ThreeLevelTreeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TreeView ItemsSource="{Binding DeviceGroups}">
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate ItemsSource="{Binding DeviceTypes}">
            <HierarchicalDataTemplate.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding DeviceInstances}">
                    <TextBlock Text="{Binding Name}"/>
                </HierarchicalDataTemplate>
            </HierarchicalDataTemplate.ItemTemplate>
            <TextBlock Text="{Binding Name}"/>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
</TreeView>

And here is the result:

alt text http://img684.imageshack.us/img684/6281/threeleveltreeview.png


I lately had to deal with a similar issue and after much research was able to get to a good generic solution. My problem was a bit more generic: Visualize a .NET object's properties in a tree view. So given this class ```

class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public List<Person> Children { get; set; }
}

I should see a tree like for an example instance:

- root
  - FirstName: John
  - LastName: Smith
  - Children:
    - [0]
      - FirstName: Ann
      - LastName: Smith

``` It's rather difficult to use reflection and go over an object's properties and such. Luckily, we already have a library that does that - Newtonsoft.Json. My trick is to serialize the object with Newtonsoft.Json, then deserialize with System.Web.Script.Serialization.JavaScriptSerializer.

JavaScriptSerializer returns ArrayLists and Dictionaries, which are pretty simple to go over and add to the tree.

Thanks to this article which gave me some of the concept ideas.

Anyway, here's the entire code:

In XAML:

<TreeView ItemsSource="{Binding TreeItemsSource}">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type local:TreeNode}" ItemsSource="{Binding Path=Children}">
            <TreeViewItem>
                <TreeViewItem.Header>
                    <StackPanel Orientation="Horizontal" Margin="-10,0,0,0">
                        <TextBlock Text="{Binding Path=Name}"/>
                        <TextBlock Text=" : "/>
                        <TextBox Text="{Binding Path=Value}" IsReadOnly="True"/>
                    </StackPanel>
                </TreeViewItem.Header>
            </TreeViewItem>
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

In ViewModel:

public IEnumerable<TreeNode> TreeItemsSource
{
    get
    {
        TreeNode tree = TreeNode.CreateTree(SelectedSession);
        return new List<TreeNode>() { tree };
    }
}

And the TreeNode class

public class TreeNode
{
    public string Name { get; set; }
    public string Value { get; set; }
    public List<TreeNode> Children { get; set; } = new List<TreeNode>();

    public static TreeNode CreateTree(object obj)
    {
        JavaScriptSerializer jss = new JavaScriptSerializer();
        var serialized = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
        Dictionary<string, object> dic = jss.Deserialize<Dictionary<string, object>>(serialized);
        var root = new TreeNode();
        root.Name = "session";
        BuildTree2(dic, root);
        return root;
    }

    private static void BuildTree2(object item, TreeNode node)
    {
        if (item is KeyValuePair<string, object>)
        {
            KeyValuePair<string, object> kv = (KeyValuePair<string, object>)item;
            TreeNode keyValueNode = new TreeNode();
            keyValueNode.Name = kv.Key;
            keyValueNode.Value = GetValueAsString(kv.Value);
            node.Children.Add(keyValueNode);
            BuildTree2(kv.Value, keyValueNode);
        }
        else if (item is ArrayList)
        {
            ArrayList list = (ArrayList)item;
            int index = 0;
            foreach (object value in list)
            {
                TreeNode arrayItem = new TreeNode();
                arrayItem.Name = $"[{index}]";
                arrayItem.Value = "";
                node.Children.Add(arrayItem);
                BuildTree2(value, arrayItem);
                index++;
            }
        }
        else if (item is Dictionary<string, object>)
        {
            Dictionary<string, object> dictionary = (Dictionary<string, object>)item;
            foreach (KeyValuePair<string, object> d in dictionary)
            {
                BuildTree2(d, node);
            }
        }
    }

    private static string GetValueAsString(object value)
    {
        if (value == null)
            return "null";
        var type = value.GetType();
        if (type.IsArray)
        {
            return "[]";
        }

        if (value is ArrayList)
        {
            var arr = value as ArrayList;
            return $"[{arr.Count}]";
        }

        if (type.IsGenericType)
        {
            return "{}";
        }

        return value.ToString();
    }

}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜