使用 MVVM 绑定 WPF DataGridComboBoxColumn

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5517789/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:44:15  来源:igfitidea点击:

Binding a WPF DataGridComboBoxColumn with MVVM

wpfmvvmdatagriddatagridcomboboxcolumn

提问by Rob

I've looked at the answers to variousquestions, but haven't managed to map the content in the answers to the problem I'm attempting to solve. I've reduced it down to the following code (representative of the outcome I'm trying to achieve), and basically want to be able to render the Person.TitleIdas its corresponding Title.TitleTextwhen the row isn't being edited, and have the drop-down bound correctly so that it displays the TitleTexts in the drop-down and writes the associated TitleIdback to the Personrecord when its changed.

我查看了各种问题的答案,但没有设法将答案中的内容映射到我试图解决的问题中。我已将其简化为以下代码(代表我试图实现的结果),并且基本上希望能够在未编辑行时将Person.TitleId其呈现为对应Title.TitleText的代码,并具有下拉菜单正确绑定,以便它TitleText在下拉列表中显示s 并在更改时将关联TitleIdPerson记录写回记录。

In short, what do I put in my <DataGridComboBoxColumn>to achieve this?

简而言之,<DataGridComboBoxColumn>为了实现这一目标,我需要投入什么?

App.xaml.cs

应用程序.xaml.cs

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    var viewModel = new ViewModels.MainWindowViewModel();
    var mainWindow = new MainWindow();
    mainWindow.DataContext = viewModel;
    mainWindow.ShowDialog();
}

MainWindow.xaml

主窗口.xaml

<Grid>
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Contacts}">
        <DataGrid.Columns>
            <DataGridComboBoxColumn Header="Title" SelectedItemBinding="{Binding Person}">
                <DataGridComboBoxColumn.ElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
                        <Setter Property="IsReadOnly" Value="True"/>
                    </Style>
                </DataGridComboBoxColumn.ElementStyle>
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
                        <Setter Property="DisplayMemberPath" Value="TitleText" />
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
            </DataGridComboBoxColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

Person.cs

人物.cs

public class Person
{
    public int TitleId { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

Title.cs

标题.cs

public struct Title
{
    public Title(int titleId, string titleText)
        : this()
    {
        TitleId = titleId;
        TitleText = titleText;
    }

    public string TitleText { get; private set; }
    public int TitleId { get; private set; }

    public static List<Title> GetAvailableTitles()
    {
        var titles = new List<Title>();

        titles.Add(new Title(1, "Mr"));
        titles.Add(new Title(2, "Miss"));
        titles.Add(new Title(3, "Mrs"));

        return titles;
    }
}

MainWindowViewModel.cs

主窗口视图模型.cs

public class MainWindowViewModel : ViewModelBase
{
    private ObservableCollection<Person> contacts;
    private List<Title> titles;

    public MainWindowViewModel()
    {
        titles = Title.GetAvailableTitles();

        Contacts = new ObservableCollection<Person>();
        Contacts.Add(new Person() { FirstName = "Jane", LastName = "Smith", TitleId = 2 });
    }

    public List<Title> Titles
    {
        get { return titles; }
    }

    public ObservableCollection<Person> Contacts
    {
        get { return contacts; }
        set
        {
            if (contacts != value)
            {
                contacts = value;
                this.OnPropertyChanged("Contacts");
            }
        }
    }
}

ViewModelBase.cs

视图模型库

public class ViewModelBase : INotifyPropertyChanged
{
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

回答by Snowbear

Here is a working code. The key point here was to use SelectedValueBindinginstead of SelecteItemBinding.

这是一个工作代码。这里的关键点是使用SelectedValueBinding而不是SelecteItemBinding.

<DataGridComboBoxColumn Header="Title" 
                        SelectedValueBinding="{Binding TitleId}"
                        SelectedValuePath="TitleId"
                        DisplayMemberPath="TitleText"
                        >
    <DataGridComboBoxColumn.ElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
        </Style>
    </DataGridComboBoxColumn.ElementStyle>
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Titles}"/>
        </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

回答by Paul Chavez

@SnowBear's answer worked well for me. But I want to clarify a detail of the binding.

@SnowBear 的回答对我很有效。但我想澄清绑定的一个细节。

In @Rob's example, both Title and Person classes use TitleID. Therefore, in @SnowBear's answer, in the binding:

在@Rob 的示例中,Title 和 Person 类都使用 TitleID。因此,在@SnowBear 的回答中,在绑定中:

SelectedValueBinding="{Binding TitleId}"

it wasn't immediately obvious to me which class and property was being bound.

对我来说,绑定的是哪个类和属性并不是很明显。

Because the SelectedValueBinding attribute appeared on the DataGridComboBoxColumn, it is binding to the ItemsSource of the containing DataGrid. In this case the Contacts collection of Person objects.

因为 SelectedValueBinding 属性出现在 DataGridComboBoxColumn 上,所以它绑定到包含 DataGrid 的 ItemsSource。在本例中是 Person 对象的 Contacts 集合。

In my case, the DataGrid's DataSource collection was attributed with a property that was named different from the ValuePath of the ComboBox's ItemSource collection. So my SelectedValueBinding's value was bound to a different property than the property named in the ComboBox's SelectedValuePath.

在我的例子中,DataGrid 的 DataSource 集合的属性与 ComboBox 的 ItemSource 集合的 ValuePath 命名不同。因此,我的 SelectedValueBinding 的值绑定到与 ComboBox 的 SelectedValuePath 中命名的属性不同的属性。