Sunday, March 6, 2011

How could I implement this strange WPF TreeListDataGridView?

As you can see in the image below I have a tree datamodel consisting of groups that can contain other groups plus an arbitary number of items wich again can hold Parameters. The Parameters itself are defined globally and just reoccur in the items. Only the parameter's actual value may differ from parameter usage to parameter usage in the different items.

The image below is an ordinary WPF treeview control with a custom control template and datatemplates for the items.

Now my goal is to remove the parameter names above the textboxes and stack them vertically in a separate column at the very left of the treeview and just leave the textboxes there but also stacked vertically so they the correspond with their parameter names in the first column.

Is there a way I can solve this with control templates and data templates and databinding to the view model ? (Yes I use MVVM)

treeview image image link

The problem is a general layout problem that must work well with databinding. generally I want to bind the object graph to a view that somewhat looks like this (cutout mockup):

treelayout

Note that the ParamX headers are not really part of the treelayout anymore. But the values still are. Now the values must keep a connection (i.e. the are on the same row) with them. Also if none of the items in the tree contain for example Param1 the Param1 header and the corresponding row must completely dissappear.

From stackoverflow
  • You could try just what you suppose in the title of the question - create a TreeListDataGridView. It will be a custom control made up of a TreeView for the top part and a DataGrid for the bottom part - or maybe just an ordinary Grid, depending on the desired effect. That way you'll have your look-and-feel and you'll preserve all the advantages of data binding, control templates, etc.

  • I'm not a tree view expert, but it's easy to build something like that without a tree view.

    Start with an empty VS2008 Wpf Application named WpfTreeGridWhatever

    First, let's define our model:

    using System;
    using System.Collections.Generic;
    
    namespace WpfTreeGridWhatever
    {
        public class ItemBase
        {
        }
        public class Group : ItemBase
        {
            public string Name { get; set; }
            public IList<ItemBase> Items { get; set; }
        }
        public class Item : ItemBase
        {
            public string Name { get; set; }
            public IList<Parameter> Parameters { get; set; }
        }
        public class Parameter
        {
            public string Name { get; set; }
            public string Value { get; set; }
        }
    }
    

    now, in the Window1 constructor create our objects (just so we have some data to bind to):

       public Window1()
        {
            DataContext = new Group[]
            {
                new Group()
                {
                    Name="Group A",
                    Items = new List<ItemBase>()
                    {
                        new Item()
                        {
                            Name="Item",
                            Parameters=new List<Parameter>()
                            {
                                new Parameter(){Name="Param 1",Value="12"},
                                new Parameter(){Name="Param 2",Value="true"},
                                new Parameter(){Name="Param 3",Value="0.0"},
                                new Parameter(){Name="Param 4",Value="off"},
                            }
                        },
                        new Item()
                        {
                            Name="Item",
                            Parameters=new List<Parameter>()
                            {
                                new Parameter(){Name="Param 1",Value="12"},
                                new Parameter(){Name="Param 2",Value="true"}
                            }
                        },
                        new Group()
                        {
                            Name="Group B",
                            Items = new List<ItemBase>()
                            {
                                new Item()
                                {
                                    Name="Item",
                                    Parameters=new List<Parameter>()
                                    {
                                        new Parameter(){Name="Param 1",Value="12"},
                                        new Parameter(){Name="Param 2",Value="true"},
                                        new Parameter(){Name="Param 3",Value="0.0"},
                                        new Parameter(){Name="Param 4",Value="off"},
                                    }
                                },
                                new Item()
                                {
                                    Name="Item",
                                    Parameters=new List<Parameter>()
                                    {
                                        new Parameter(){Name="Param 1",Value="12"},
                                        new Parameter(){Name="Param 2",Value="true"},
                                        new Parameter(){Name="Param 3",Value="0.0"},
                                        new Parameter(){Name="Param 4",Value="off"},
                                        new Parameter(){Name="Param 5",Value="2000"},
                                    }
                                },
                                new Item()
                                {
                                    Name="Item",
                                    Parameters=new List<Parameter>()
                                    {
                                        new Parameter(){Name="Param 1",Value="12"},
                                        new Parameter(){Name="Param 2",Value="true"},
                                    }
                                },
                                new Group()
                                {
                                    Name="Group C",
                                    Items = new List<ItemBase>()
                                    {
                                        new Item()
                                        {
                                            Name="Item",
                                            Parameters=new List<Parameter>()
                                            {
                                                new Parameter(){Name="Param 1",Value="12"},
                                                new Parameter(){Name="Param 2",Value="true"},
                                                new Parameter(){Name="Param 3",Value="0.0"},
                                                new Parameter(){Name="Param 4",Value="off"},
                                            }
                                        },
                                        new Item()
                                        {
                                            Name="Item",
                                            Parameters=new List<Parameter>()
                                            {
                                                new Parameter(){Name="Param 1",Value="12"},
                                                new Parameter(){Name="Param 2",Value="true"},
                                                new Parameter(){Name="Param 3",Value="0.0"},
                                                new Parameter(){Name="Param 4",Value="off"},
                                                new Parameter(){Name="Param 5",Value="2000"},
                                            }
                                        },
                                        new Item()
                                        {
                                            Name="Item",
                                            Parameters=new List<Parameter>()
                                            {
                                                new Parameter(){Name="Param 1",Value="12"},
                                                new Parameter(){Name="Param 2",Value="true"},
                                            }
                                        },
                                    }
                                }
                            }
                        }
                    }
                }
            };
    
            InitializeComponent();
        }
    

    And now, the magic - use this code in Window1.xaml

    <Window x:Class="WpfTreeGridWhatever.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:WpfTreeGridWhatever"
        Title="Window1" Height="300" Width="300">
        <Window.Resources>
            <LinearGradientBrush x:Key="Bk" StartPoint="0,0" EndPoint="0,1" >
                <GradientStop Offset="0" Color="DarkGray"/>
                <GradientStop Offset="1" Color="White"/>
            </LinearGradientBrush>
            <DataTemplate DataType="{x:Type l:Parameter}">
                <Border CornerRadius="5" Background="{StaticResource Bk}"
                        BorderThickness="1" BorderBrush="Gray" Margin="2" >
                    <StackPanel Margin="5">
                        <TextBlock Height="12" Text="{Binding Name}"/>
                        <TextBox Height="22" Text="{Binding Value}"/>
                    </StackPanel>
                </Border>
            </DataTemplate>
            <DataTemplate DataType="{x:Type l:Item}" >
                <StackPanel>
                    <Border CornerRadius="5" Background="{StaticResource Bk}"
                        BorderThickness="1" BorderBrush="Gray" Height="25" Margin="3">
                        <TextBlock Height="12" Text="{Binding Name}" VerticalAlignment="Center" Margin="3,0"/>
                    </Border>
                    <ItemsControl ItemsSource="{Binding Parameters}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </StackPanel>
            </DataTemplate>
            <DataTemplate DataType="{x:Type l:Group}">
                <StackPanel>
                    <Border CornerRadius="5" Background="{StaticResource Bk}"
                        BorderThickness="1" BorderBrush="Gray" Height="25" Margin="3">
                        <TextBlock Height="12" Text="{Binding Name}" VerticalAlignment="Center" Margin="3,0"/>
                    </Border>
                    <ItemsControl ItemsSource="{Binding Items}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel Orientation="Horizontal"/>
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>
                </StackPanel>
            </DataTemplate>
    
        </Window.Resources>
        <Grid>
            <ItemsControl ItemsSource="{Binding}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
            </ItemsControl>
        </Grid>
    </Window>
    

    This should get you started

    bitbonk : Thank you for your detailed answer. Its great to know how simple it is how to build hierarical visualization even without a treeview. But my main problem lies somewhere else. It is more of a general layout problem combined with dynamic databinding. See my answer below.
    bitbonk : Sorry - instead of an answer I modified my initial question and added some additional information.

0 comments:

Post a Comment