Doctor
Doctor

Reputation: 812

WPF DataGridColumn MultiConverter Parameters are UnSetValue

My problem is always Converter's second parameter which is DependencyProperty.UnSetValue. I cannot fix that. I try many things and suggested solution in similar topics in forums but again I failed. If I use itemscontrol instead of datagrid, there is no any problem and code works but I have to use data grid.

<ItemsControl ItemsSource="{Binding Definitions}" DataContext="{Binding Test}">
  <ItemsControl.ItemTemplate>
    <DataTemplate DataType="{x:Type definitions:Definition}">
      <Expander Header="{Binding Name}" x:Name="expander" Width="700" VerticalAlignment="Top" HorizontalAlignment="Left" DataContext="{Binding}" IsExpanded="True">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
          </Grid.RowDefinitions>

          <Grid Grid.Row="0">
          </Grid>

          <Grid Grid.Row="1" Margin="0,5,0,0">
            <DataGrid DataContext="{Binding Path=DataContext, ElementName=expander}" ItemsSource="{Binding Items}" CanUserAddRows="True" AutoGenerateColumns="False">
              <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="{Binding Path=AutoNamed,Mode=TwoWay}" />
                <DataGridComboBoxColumn DisplayMemberPath="ID" SelectedValuePath="Number" Header="-" SelectedValueBinding="{Binding Path=Start,Mode=TwoWay}">
                  <DataGridComboBoxColumn.ItemsSource>
                    <MultiBinding Converter="{StaticResource Converter}">
                      <Binding Path="Test.Types" Source="{x:Static viewmodels:ApplicationVM.Instance}" />
                      <Binding Path="DataContext.Name" ElementName="expander" />
                    </MultiBinding>
                  </DataGridComboBoxColumn.ItemsSource>
                </DataGridComboBoxColumn>
                <DataGridComboBoxColumn Header="+" DisplayMemberPath="ID" SelectedValuePath="Number" SelectedValueBinding="{Binding Path=End,Mode=TwoWay}">
                  <DataGridComboBoxColumn.ItemsSource>
                    <MultiBinding Converter="{StaticResource Converter}">
                      <Binding Path="Test.Types" Source="{x:Static viewmodels:ApplicationVM.Instance}" />
                      <Binding Path="Header" ElementName="expander" />
                    </MultiBinding>
                  </DataGridComboBoxColumn.ItemsSource>
                </DataGridComboBoxColumn>
                <DataGridTextColumn Header="Low" Binding="{Binding Path=Low,Mode=TwoWay}" />
                <DataGridTextColumn Header="High" Binding="{Binding Path=High,Mode=TwoWay}" />
                <DataGridCheckBoxColumn Header="Auto Name" Binding="{Binding Path=AutoNamed,Mode=TwoWay}" />
              </DataGrid.Columns>
            </DataGrid>
          </Grid>
        </Grid>
      </Expander>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Firstly, I thought it could be the binding issue. So, I added two new column to test.

<DataGridTextColumn Header="Name2" Binding="{Binding Path=Header, ElementName=expander}"/>
<DataGridTextColumn Header="Name3" Binding="{Binding Path=DataContext.Name, ElementName=expander}"/>

These two columns worked. I can see it is not binding data issue, but what I send as parameter instead of view model property is UnSetValue in Converter. How can it be possible? It is also inherited from ItemsControl. I can not bind parent datacontext to datagrid. How can I solve this problem? I am glad to get help about it. Thanks in advance.

Upvotes: 0

Views: 516

Answers (3)

Doctor
Doctor

Reputation: 812

Thanks to Ramin. I solved the problem, but I have to some changes.

According to Artical, I can bind view-model to proxy like below

<DataGrid.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>

And after that,

<DataGridComboBoxColumn Header="+" DisplayMemberPath="ID" SelectedValuePath="Number" SelectedValueBinding="{Binding Path=End,Mode=TwoWay}">
    <DataGridComboBoxColumn.ItemsSource>
        <MultiBinding Converter="{StaticResource Converter}">
            <Binding Path="Test.Types" Source="{x:Static viewmodels:ApplicationVM.Instance}" />
          <Binding Path="Data.Name" Source="{StaticResource proxy}"  />
         </MultiBinding>
    </DataGridComboBoxColumn.ItemsSource>
</DataGridComboBoxColumn>

I can reach my view-model over the proxy, so parameters is not UnSetValue anymore.

Upvotes: 0

mm8
mm8

Reputation: 169360

You should set the ItemsSource property of the ComboBox in the ElementStyle and EditingElementStyle of the DataGridComboBoxColumn. You can then use a RelativeSource to bind to the Expander:

<DataGrid DataContext="{Binding Path=DataContext, ElementName=expander}" ItemsSource="{Binding Items}" 
                              CanUserAddRows="True" AutoGenerateColumns="False">
    <DataGrid.Resources>
        <Style x:Key="PlusColumnStyle" TargetType="ComboBox">
            <Setter Property="ItemsSource">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource Converter}">
                        <!--<Binding Path="Test.Types" Source="{x:Static viewmodels:ApplicationVM.Instance}" />-->
                        <Binding Path="Header" RelativeSource="{RelativeSource AncestorType=Expander}" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridComboBoxColumn Header="+" DisplayMemberPath="ID" SelectedValuePath="Number" 
                                                    SelectedValueBinding="{Binding Path=End,Mode=TwoWay}"
                                                    ElementStyle="{StaticResource PlusColumnStyle}" EditingElementStyle="{StaticResource PlusColumnStyle}"/>
        <!-- + the other columns...-->
    </DataGrid.Columns>
</DataGrid>

Don't set the ItemsSource property of the DataGridComboBoxColumn itself. This won't work because a DataGridColumn is not a visual UIElement that gets added to the visual tree so it won't be able to find the Expander.

Upvotes: 1

rmojab63
rmojab63

Reputation: 3631

My solution is based on this article. Create a class that inherits Freezable and declares a Data dependency property:

public class BindingProxy : Freezable
{
    protected override Freezable CreateInstanceCore() { return new BindingProxy(); }
    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    } 
    public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), 
            typeof(BindingProxy), new UIPropertyMetadata(null));
} 

declare an instance of this class as the Expanders resources:

<Expander.Resources>
      <local:BindingProxy x:Key="proxy" Data="{Binding ElementName=expander}"/>
</Expander.Resources>

Note the "ElementName=expander" part. Use it in declaring the Bindings. The following gives the Expander as the first element.

<MultiBinding Converter="{StaticResource Converter}">
      <Binding Source="{StaticResource proxy}"  />
</MultiBinding>

Upvotes: 1

Related Questions