嵌套 DataGrid 的 SelectedValue 绑定不起作用,而 ItemsSource 起作用

SelectedValue binding of a nested DataGrid doesn't work while ItemsSource does

我在 UserControl 中有一个 DataGrid,而 UserControl 又在另一个 UserControl 中。这是由于项目的其他需要,我无法更改这种嵌套架构。我将 Person class 的列表绑定到此 DataGrid。这是一个没有使用 VM 的简化版本,但在我的真实项目中我使用的是 VM.

我的 UserControlDataGrid:

<Grid>
    <DataGrid x:Name="MyDg"
        ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"
        MouseDoubleClick="MyDg_MouseDoubleClick"
        SelectedValue="{Binding SelectedValue, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

代码隐藏:

public partial class UCDataGrid : UserControl
{
    public event RoutedEventHandler RoutedDataGridDoubleClick;

    public UCDataGrid()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
    public object ItemsSource
    {
        get { return GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }

    public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
    public object SelectedValue
    {
        get { return GetValue(SelectedValueProperty); }
        set { SetValue(SelectedValueProperty, value); }
    }

    private void MyDg_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        RoutedDataGridDoubleClick?.Invoke(this, new RoutedEventArgs());
    }
}

第 2 个 UserControl 包含上述控件:

<Grid>
    <ContentControl Content="{Binding MyDataGrid, ElementName=ucDisplay}"/>
</Grid>

ucDisplay 只是这个 UserControl.

Name 属性 值

代码隐藏:

没什么特别的。

public partial class UCDisplay : UserControl
{
    public UCDisplay()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty MyDataGridProperty = DependencyProperty.Register("MyDataGrid", typeof(object), typeof(UCDisplay), new PropertyMetadata(null));
    public object MyDataGrid
    {
        get { return GetValue(MyDataGridProperty); }
        set { SetValue(MyDataGridProperty, value); }
    }
}

主要Window

在我的 Main Window 中,我绑定了我的 People 列表以及 SelectedPerson 实例,如下所示:

<Grid>
    <local:UCDisplay>
        <local:UCDisplay.MyDataGrid>
            <local:UCDataGrid ItemsSource="{Binding People}"
                              SelectedValue="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged}"
                              RoutedDataGridDoubleClick="UCDataGrid_RoutedDataGridDoubleClick"/>
        </local:UCDisplay.MyDataGrid>
    </local:UCDisplay>
</Grid>

代码隐藏:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    private List<Person> people;
    public List<Person> People
    {
        get => people;
        set => SetField(ref people, value);
    }

    private Person selectedPerson;
    public Person SelectedPerson
    {
        get => selectedPerson;
        set => SetField(ref selectedPerson, value);
    }

    public MainWindow()
    {
        InitializeComponent();
        People = GetPeople();
        DataContext = this;
    }

    private void UCDataGrid_RoutedDataGridDoubleClick(object sender, RoutedEventArgs e)
    {

    }

    private List<Person> GetPeople()
    {
        return new List<Person>
        {
            new Person() { Name = "A" },
            new Person() { Name = "B" },
            new Person() { Name = "C" }
        };
    }

    public class Person
    {
        public string Name { get; set; }
    }
}

同样,实际上我使用的是 VM,这只是为了简单起见。

现在,当我 运行 这时,我可以很好地显示我的列表内容。但是当我双击我的 DataGrid 中的一个项目时,在我后面的 Main Window 代码中对应的项目中,SelectedPerson 仍然是 null,尽管它的绑定与People 列表。我通过在后面的主要代码中使用断点来确认这一点:

但是如果我调试并看到我最里面 UserControl 后面的代码中的值,您会看到那里的 SelectedValue 具有正确的所选项目值。

那么我做错了什么?为什么我似乎不能绑定 SelectedValue 虽然我做的和我的 ItemsSource 绑定完全一样,但后者有效?

SelectedValue应该和SelectedValuePath一起使用。您应该改用 SelectedItem

除此之外,您还缺少 TwoWay 绑定。显式声明 SelectedItem Binding TwoWay

<DataGrid x:Name="MyDg"
    ItemsSource="{Binding ItemsSource,
        RelativeSource={RelativeSource AncestorType=UserControl}}"
    SelectedItem="{Binding SelectedItem,
        RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}"/>

或者注册属性默认绑定TwoWay:

public static readonly DependencyProperty SelectedItemProperty =
    DependencyProperty.Register(
        nameof(SelectedItem), typeof(object), typeof(UCDataGrid),
        new FrameworkPropertyMetadata(
            null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public object SelectedItem
{
    get { return GetValue(SelectedItemProperty); }
    set { SetValue(SelectedItemProperty, value); }
}

另请注意,设置 UpdateSourceTrigger=PropertyChanged 在您的所有绑定中都毫无意义。