使用DataTemplate跨单元绑定在DataGrid中级联组合框 [英] Cascading Combobox in DataGrid using DataTemplate Cross-Cell Binding

查看:86
本文介绍了使用DataTemplate跨单元绑定在DataGrid中级联组合框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我将级联组合框放在WPF DataGrid单元的同一DataTemplate内,则绑定正常(通过ElementName ).但是,从UI角度来看,我希望组合框物理上驻留在不同的单元格中,而不是在同一datagrid单元中.如何使用DataGridTemplateColumns使跨单元绑定工作(在DataTemplates之间的 )?似乎问题在于,当组合框位于不同的DataTemplate列中时,第二个组合框的ItemsSource找不到绑定的ElementName.

这可行....

<DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
                <DataGrid.Columns>        
                    <DataGridTemplateColumn Header="Car Make / Model" Width="150">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="SelectionChanged">
                                            <mvvm:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.SelectCarMake}" PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </ComboBox>                            
                                <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ElementName=cbCarMake, Path=Tag.CarModels, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
     </DataGrid>

这不是....

 <DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
            <DataGrid.Columns>        
                <DataGridTemplateColumn Header="Car Make" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">
                                <i:Interaction.Triggers>
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="SelectionChanged">
                                            <mvvm:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.SelectCarMake}" PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </i:Interaction.Triggers>
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Car Model" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ElementName=cbCarMake, Path=Tag.CarModels, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
 </DataGrid>

解决方案

跨数据模板绑定的窍门是使用ViewModel作为中间对象.当视图模型的属性发生变化(在这种情况下为一个ObservableCollection )时,您可以向依赖的侦听器(侦听PropertyChanged事件的元素)触发此通知.

对我来说,问题在于视图模型没有收到应有的PropertyChanged通知.我假设mvvm-light ObservableObject为所有属性自动触发了PropertyChanged事件.

您必须使用mvvminpcset代码段,该代码段显式引发ObservableCollection中各项的属性更改事件.一旦PropertyChanged事件开始触发,ItemSource绑定就会按如下所示工作.

<DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
            <DataGrid.Columns>        
                <DataGridTemplateColumn Header="Car Make" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">                                
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Car Model" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding CarMake.CarModels}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
 </DataGrid>

If I place my cascading comboboxes inside the same DataTemplate in a WPF DataGrid cell - the binding works properly (via ElementName). However, from a UI perspective I want my comboboxes to physically reside in different cells, not the same datagrid cell. How do you make cross-cell binding work (between DataTemplates) using DataGridTemplateColumns? It seems the issue is that the second comboboxes' ItemsSource cannot find the ElementName for binding when the comboboxes exist in different DataTemplate columns.

This works....

<DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
                <DataGrid.Columns>        
                    <DataGridTemplateColumn Header="Car Make / Model" Width="150">
                        <DataGridTemplateColumn.CellTemplate>
                            <DataTemplate>
                                <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="SelectionChanged">
                                            <mvvm:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.SelectCarMake}" PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </ComboBox>                            
                                <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ElementName=cbCarMake, Path=Tag.CarModels, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                            </DataTemplate>
                        </DataGridTemplateColumn.CellTemplate>
                    </DataGridTemplateColumn>
                </DataGrid.Columns>
     </DataGrid>

This doesn't....

 <DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
            <DataGrid.Columns>        
                <DataGridTemplateColumn Header="Car Make" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">
                                <i:Interaction.Triggers>
                                    <i:Interaction.Triggers>
                                        <i:EventTrigger EventName="SelectionChanged">
                                            <mvvm:EventToCommand Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.SelectCarMake}" PassEventArgsToCommand="True"/>
                                        </i:EventTrigger>
                                    </i:Interaction.Triggers>
                                </i:Interaction.Triggers>
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Car Model" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ElementName=cbCarMake, Path=Tag.CarModels, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
 </DataGrid>

解决方案

The trick with cross-data template binding is to use the ViewModel as a go-between. When properties change on the view model (an ObservableCollection in this case), you can trigger this notification to the dependent listeners (elements listening to PropertyChanged events).

The issue for me was that the View Model wasn't receiving the PropertyChanged notifications it should have been. I assumed that the mvvm-light ObservableObject automatically triggered PropertyChanged events for all properties.

You must use the mvvminpcset code snippet which explicitly raises property change events for the items in your ObservableCollection. Once the PropertyChanged events started firing, the ItemSource binding worked as seen below.

<DataGrid x:Name="grdItems" AutoGenerateColumns="false" ItemsSource="{Binding Model}">          
            <DataGrid.Columns>        
                <DataGridTemplateColumn Header="Car Make" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox x:Name="cbCarMake" SelectedItem="{Binding CarMake, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.CarMakes, Mode=TwoWay}" DisplayMemberPath="Name" SelectedValuePath="ID">                                
                            </ComboBox>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="Car Model" Width="150">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox SelectedItem="{Binding CarModel, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding CarMake.CarModels}" DisplayMemberPath="Name" SelectedValuePath="ID"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
 </DataGrid>

这篇关于使用DataTemplate跨单元绑定在DataGrid中级联组合框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆