4

I have a MultiColumn TreeView which uses a GridViewColumnCollection. I my situation, I don't know how many columns will be there, nor their header names. That's discovery at run time.

Hence I have need to create these columns in code and bind dynamically to them.

Okay - creation is simple enough:

GridViewColumn c = new GridViewColumn();
c.Header = "Next Column";
myTree.Columns.Add(c);

Now where I stumble - Suppose I wish to bind to my viewModel's "MyName" property:

Binding myBinding = new Binding(??);
myBinding.Source = ??
BindingOperations.SetBinding(myTree,GridViewColumn.????  , myBinding);

Now the template for this

 <DataTemplate x:Key="CellTemplate_Name">
        <DockPanel>
            <ToggleButton x:Name="Expander"
                          Margin="{Binding Level,
                                           Converter={StaticResource LevelToIndentConverter},
                                           RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}"
                          ClickMode="Press"
                          IsChecked="{Binding Path=IsExpanded,
                                              RelativeSource={RelativeSource AncestorType={x:Type l:TreeListViewItem}}}"
                          Style="{StaticResource ExpandCollapseToggleStyle}" />
            <TextBlock Text="{Binding Name}" />
.                
.
.
    <Style TargetType="{x:Type l:TreeListViewItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type l:TreeListViewItem}">
                    <StackPanel>
                        <Border Name="Bd"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Padding="{TemplateBinding Padding}">
                            <GridViewRowPresenter x:Name="PART_Header"
                                                  Columns="{Binding Path=Columns,
                                                                    RelativeSource={RelativeSource AncestorType={x:Type l:TreeListView}}}"
                                                  Content="{TemplateBinding Header}" />
                        </Border>
                        <ItemsPresenter x:Name="ItemsHost" />
                    </StackPanel>

Where column insertion and binding in XAML appears as:

    <l:TreeListView x:Name="myTree" ItemsSource="{Binding MySource}">
    <l:TreeListView.Columns>
        <GridViewColumn x:Name="GridViewColumn0" CellTemplate="{StaticResource CellTemplate_Name}" 
                        Header="Name" />
        <GridViewColumn Width="60"
                        DisplayMemberBinding="{Binding Description}"
                        Header="Description" />

Any help with SetBinding here would be greatly appreciated. I've searched till my fingers fell off.

Update: Excellent answer:

        GridViewColumn c = new GridViewColumn();
        c.Header = "Next Column";
        myTree.Columns.Add(c);

        Binding myBinding = new Binding("MyName"); 
        myBinding.Source = viewModel;
        BindingOperations.SetBinding(myTree.Columns[myTree.Columns.Count - 1], 
            GridViewColumn.HeaderProperty, 
            myBinding);

The binding now works against the Header perfectly - thank you so much.

yu yang Jian
  • 6,680
  • 7
  • 55
  • 80
  • `Binding myBinding = new Binding("MyName"); myBinding.Source = viewModel;` – Vlad Oct 29 '12 at 23:13
  • Sounds good! But what of BindingOperations.SetBinding(myTree,GridViewColumn.???? , myBinding); What would I use for the first two parameters of SetBinding in this case? GridViewColumn is not a DO and which of GridViewColumn's DP's would I use? – Walt Lounsbury Oct 31 '12 at 16:55
  • perhaps `BindingOperations.SetBinding(myTree.Columns[i], GridViewColumn.HeaderProperty, myBinding);` where `i` is the number of column? – Vlad Oct 31 '12 at 17:06
  • and BTW, `GridViewColumn` [is a DO](http://msdn.microsoft.com/en-us/library/system.windows.controls.gridviewcolumn.aspx) (well, otherwise it couldn't have DPs). – Vlad Oct 31 '12 at 17:08
  • Perfect! One more question - how to set the value binding for the Cell? In viewModel, Item exposes "Description" which is to be displayed in the cell. The ItemsSource is set to public ObservableCollection Items - where, in XAML, DisplayMemberBinding="{Binding Description}". How do I set the binding for the DisplayMemberBinding? – Walt Lounsbury Oct 31 '12 at 19:06
  • Well, I would try something like `myTree.Columns[i].DisplayMemberBinding = new Binding("Description");` – Vlad Oct 31 '12 at 19:10
  • Okay - "some" success - I did this: Binding nextBind = new Binding("Next"); nextBind.Source = viewModel.MySource; column.DisplayMemberBinding = nextBind; And it binds ONLY to the first row's "Next" value. So the binding "kinda" works, but its not honoring the ObservableCollection - All the rows are showing the value of the First element's "Next" value only :( – Walt Lounsbury Oct 31 '12 at 19:46
  • Alas - Success. I removed the nextBind.Source = from the "Next" item and it behaves great. Thanks tons - topic CLOSED! :) – Walt Lounsbury Oct 31 '12 at 19:55
  • You are welcome! BTW you can consider marking the answer if it helped :) – Vlad Nov 01 '12 at 11:39

2 Answers2

4

(Extracting an answer from comments:)

You can set the binding manually with the code like this:

Binding myBinding = new Binding("MyName");
myBinding.Source = viewModel;
BindingOperations.SetBinding(myTree.Columns[i], GridViewColumn.HeaderProperty, myBinding);

where i is the number of column.

For setting the DisplayMemberBinding, a simpler code can be used:

Binding descriptionBinding = new Binding("Description");
myTree.Columns[i].DisplayMemberBinding = descriptionBinding;
Vlad
  • 35,022
  • 6
  • 77
  • 199
0

I make it a method NewGridViewColumn, set the parameter header and bindingName to the method, and you will get a binding-completed GridViewColumn:

    private GridViewColumn NewGridViewColumn(string header, string bindingName)
    {
        GridViewColumn gvc = new GridViewColumn();
        gvc.Header = header;
        gvc.DisplayMemberBinding = new Binding(bindingName);            
        return gvc;
    }

.

Then in case of ListView xaml like this:

<Grid>
        <ListView Margin="5" Name="ListViewTest"></ListView>
</Grid>

You can easily add the GridViewColumn to ListView in Constructor using NewGridViewColumn:

    public MainWindow()
    {
        InitializeComponent();

        //Give the Source to ListViewTest
        //ProduceNames() makes a List<NameClass> that 
        //NameClass has 3 property Name1,Name2,Nmae3
        ListViewTest.ItemsSource = ProduceNames();

        GridView gview = new GridView();

        //add some GridViewColumns to the gview
        gview.Columns.Add(NewGridViewColumn("FirstName","Name1"));
        gview.Columns.Add(NewGridViewColumn("SecondName","Name2"));
        gview.Columns.Add(NewGridViewColumn("ThirdName","Name3"));

        //set the gview to the ListViewTest's View
        ListViewTest.View = gview;

    }

the result:

enter image description here

yu yang Jian
  • 6,680
  • 7
  • 55
  • 80