WPF MVVM 将 ComboBox 绑定到 Datagrid 选定项

Posted

技术标签:

【中文标题】WPF MVVM 将 ComboBox 绑定到 Datagrid 选定项【英文标题】:WPF MVVM Binding ComboBox to Datagrid Selected Item 【发布时间】:2013-01-03 17:45:23 【问题描述】:

有没有人将 ComboBox 绑定到 DataGrid 的选定项的示例?我已经尝试了很多方法,但我似乎无法让它发挥作用。我对 MVVM 相当陌生,所以我做错了。任何帮助,将不胜感激。我将组合框所在的网格的数据上下文设置为数据网格,但是当我从数据网格中选择一行时,组合框不会改变。所有文本框都使用来自数据网格的数据填充,但组合框不会更改。有问题的组合框是 cmbRoles。

谢谢,

RG

这是 XAML:

<UserControl x:Class="Compliance.Views.UserAdministrationView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:Compliance"
         xmlns:views="clr-namespace:Compliance.Views"
         xmlns:helpers="clr-namespace:Compliance.Helpers"
         xmlns:vm="clr-namespace:Compliance.ViewModels"
         xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
         mc:Ignorable="d" 
         d:DesignHeight="1000" d:DesignWidth="800">
<UserControl.Resources>
    <helpers:ActiveStatusConverter x:Key="ActiveStatusConverter"/>
</UserControl.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" Margin="15">
        <Label Content="User" Height="25" FontSize="14" HorizontalContentAlignment="Center" />
        <Grid HorizontalAlignment="Center" VerticalAlignment="Top" DataContext="Binding ElementName=usersDG, Path=SelectedItem">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" MinHeight="35" />
            </Grid.RowDefinitions>
            <telerik:Label Content="User Name: " Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="0" MinHeight="23" HorizontalAlignment="Left" VerticalAlignment="Center" MinWidth="180" MaxWidth="180" >
                <TextBox.Text>
                    <Binding Path="UserName" Mode="TwoWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True"/>
                </TextBox.Text>                    
            </TextBox> 
            <telerik:Label Content="First Name: " Grid.Column="2" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <TextBox Grid.Column="3" Grid.Row="0" MinHeight="23" HorizontalAlignment="Left" VerticalAlignment="Center" MinWidth="180" MaxWidth="180">
            <TextBox.Text>
                <Binding Path="FirstName" Mode="TwoWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True"/>
            </TextBox.Text>
            </TextBox>
            <telerik:Label Content="Last Name: " Grid.Column="4" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <TextBox Grid.Column="5" Grid.Row="0" MinHeight="23" HorizontalAlignment="Left" VerticalAlignment="Center" MinWidth="180" MaxWidth="180">
            <TextBox.Text>
                <Binding Path="LastName" Mode="TwoWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True"/>
            </TextBox.Text>
            </TextBox>
            <telerik:Label Content="Email: " Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <TextBox Grid.Column="1" Grid.Row="1" MinHeight="23" HorizontalAlignment="Left" VerticalAlignment="Center" MinWidth="180" MaxWidth="180">
                <TextBox.Text>
                    <Binding Path="Email" Mode="TwoWay" ValidatesOnDataErrors="True" NotifyOnValidationError="True"/>
                </TextBox.Text>
            </TextBox>
            <telerik:Label Content="Active Status: " Grid.Column="2" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <telerik:RadComboBox x:Name="comBoxActiveStatus" Grid.Column="3" Grid.Row="1" MinHeight="23" MinWidth="180" HorizontalAlignment="Left" VerticalAlignment="Center"
                    SelectedItem="Binding Path=ActiveStatus, 
                                    Converter=StaticResource ResourceKey=ActiveStatusConverter, 
                                    Mode=TwoWay, 
                                    ValidatesOnExceptions=True, 
                                    NotifyOnValidationError=True">
            </telerik:RadComboBox>
            <telerik:Label Content="Role: " Grid.Column="4" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center" />
            <telerik:RadComboBox x:Name="cmbRoles" DisplayMemberPath="RoleName" 
                        Grid.Column="5" 
                        Grid.ColumnSpan="3"
                        Grid.Row="1" 
                        MinHeight="23" 
                        HorizontalAlignment="Left" 
                        ItemsSource="Binding" 
                        Margin="5" 
                        VerticalAlignment="Center" 
                        MinWidth="180" 
                        SelectedItem="Binding RoleName"
                        IsSynchronizedWithCurrentItem="True">
            </telerik:RadComboBox>
            <Button Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="3" Content="Save User" Width="100"  />
            <Button Grid.Column="4" Grid.Row="2" Grid.ColumnSpan="3" Content="Add User"  Width="100"  />
        </Grid>
    </StackPanel>
    <Border CornerRadius="10" BorderThickness="5" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center">
        <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Content="Users" Grid.Row="0" Height="25" FontSize="14" HorizontalContentAlignment="Center" />
            <telerik:RadGridView x:Name="usersDG" ItemsSource="Binding Users" AutoGenerateColumns="False" ShowGroupPanel="False" IsReadOnly="True">
                <telerik:RadGridView.Columns>
                    <telerik:GridViewDataColumn DataMemberBinding="Binding UserName" Header="User Name" />
                    <telerik:GridViewDataColumn DataMemberBinding="Binding FirstName" Header="First Name" />
                    <telerik:GridViewDataColumn DataMemberBinding="Binding LastName" Header="Last Name" />
                    <telerik:GridViewDataColumn DataMemberBinding="Binding Email" Header="Email" />
                    <telerik:GridViewDataColumn DataMemberBinding="Binding Role.RoleName" Header="Role Name" />
                    <telerik:GridViewDataColumn DataMemberBinding="Binding ActiveStatus, Converter=StaticResource ActiveStatusConverter" Header="Active Status" />
                </telerik:RadGridView.Columns>
            </telerik:RadGridView>
        </Grid>
    </Border>
</Grid>

【问题讨论】:

组合框中的日期应该是什么?它与 DataGrid 中的 SelectedItem 有何关系? 组合框中的数据是用户对应用程序的角色。这是一个用户管理屏幕。他们将能够通过从数据网格中选择他们并编辑他们的属性来添加新用户或编辑现有用户。我以前用另一个 WPF 应用程序做过这个,但我决定学习 MVVM 设计模式,我正在努力坚持这种模式。 您是否为您的组合框尝试过 SelectedItem="Binding Role.RoleName"? 是的。组合框没有变化,输出窗口也没有错误。 【参考方案1】:

扩展并对上面的 Hannish 的回答采取不同的看法,我也假设您使用的是 Master Details 类型排列,您希望将 Details DataContext 设置为 DataGrid Selected Row。

我在这个设置中也遇到了很多关于 ComboBox Binding 的问题。

我的应用程序不使用 Telerik 控件,但我倾向于为 DataGrid 选定行创建一个属性,并将我的详细信息绑定到此。

DataGrid XAML 类似于;

<DataGrid x:Name="grdResults" 
    DataContext="Binding DataGridDataContextCollection" 
    ItemsSource="Binding"
    SelectedItem="Binding DataContext.SelectedRow, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay">

我是这样做的,因为我已经为运行 DataGrid DataContext 中的项目的行设置了行为。

然后我也有一个选定组合框项目的属性,并将组合框绑定到这个。

对于 ComboBox ItemSource,问题在于,您需要将 ComboBox 绑定到的项目的 ObservableCollection,但这些当然不会是您的 DataGrid 选定行的成员,而反过来您的详细信息 DataContext .

所以绑定ComboBox ItemSource 和SelectedItem,必须引用View 的DataContext,而不是Details。

如果您在 DataGrid DataContext 中提供了它,还可以将 ComboBox Text 属性绑定到 DataGrid SelectedRow Name 值,假设两者相同!这有助于更新 DataGrid 也已更新。

我用于 ComboBox 的 XAML 类似于;

<ComboBox x:Name="cmbRoles" 
    SelectedItem="Binding DataContext.SelectedRole, Mode=TwoWay, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True" 
    SelectedValuePath="Role_ID" 
    SelectedValue="Binding Role_ID, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True"
    ItemsSource="Binding DataContext.RoleItems, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type UserControl" 
    Text="Binding RoleName, Mode=TwoWay"
    DisplayMemberPath="RoleName" 
    IsSynchronizedWithCurrentItem="True" 
    HorizontalAlignment="Left"/>

为了 100% 确定,然后我通过在 ComboBox SelectedItem Setter 中设置 DataGrid SelectedRow ID 值将 ComboBox SelectedItem 同步到 DataGrid SelectedRow。这当然不是绝对必要的。

我希望这会有所帮助!

【讨论】:

我所要做的就是将组合框 XAML 更改为以下内容,然后我就可以正常工作了... DisplayMemberPath="RoleName" SelectedValue="Binding ElementName=usersDG, Path=SelectedItem.RoleID, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged" SelectedValuePath="RoleID"【参考方案2】:

如果我理解正确,您有一个包含所有用户的 RadGridView,以及某种详细信息表单,您可以在其中编辑所选行的值。

所以,你必须修改几件事:

1:组合框中的ItemsSource设置为Binding,所以它只会绑定到其父元素的datacontext来获取项目列表。在这种情况下,父级是 Grid,其 DataContext 设置(正确)为 RadGrid 的选定项(此处:DataContext="Binding ElementName=usersDG, Path=SelectedItem",没关系)。

问题是您必须使用可能的角色列表填充 ComboBox 的 ItemsSource。我通常对此类列表使用静态 ObservableCollection(因此我确保它们在整个应用程序中都是相同的)。像这样的:

ItemsSource="x:Static local:Lists.RolesList"

您可能有一个枚举或其他东西,但重要的是您使用所有可能选择的选项填充 ItemsSource 属性。

2:由于您将使用 Role 对象填充组合框中的列表,因此 SelectedItem 必须绑定到此类对象,该对象必须作为公共属性(使用 INPC 或 DependecyProperty)存在于 User 对象中。你需要这样设置:

SelectedItem="Binding Role, Mode=TwoWay"

请记住,您的组合的数据上下文是一个用户对象,如在 RadGrid 中选择的那样。用户类必须有一个属性 Role。

我认为这样就可以了,如果您还有其他问题,请告诉我。最好的问候!

【讨论】:

这听起来完全符合我的需要。你有什么可以分享的例子,或者显示这个的链接吗? 具体是什么的例子?您可以在代码中使用这些修改。关于 ItemsSource,您还可以在 ViewModel 中公开角色集合并绑定到它,但在这种情况下绑定表达式并不那么简单(类似于 PGallaher 的答案)。告诉我您需要什么,我会尽力提供帮助。

以上是关于WPF MVVM 将 ComboBox 绑定到 Datagrid 选定项的主要内容,如果未能解决你的问题,请参考以下文章

简单的 WPF + MVVM 绑定

WPF中ComboBox控件的SelectedItem和SelectedValue的MVVM绑定

WPF - MVVM:SelectionChanged 后的 ComboBox 值

MVVM WPF ComboBox SelectedItem 绑定未在数据网格内激活

WPF,mvvm,在combobox中嵌套了checkbox控件,数据绑定完成后,无法实现双向绑定

WPF MVVM:组合框 SelectedValue 绑定