嵌套 DataGrid 的 SelectedValue 绑定不起作用,而 ItemsSource 起作用
SelectedValue binding of a nested DataGrid doesn't work while ItemsSource does
我在 UserControl
中有一个 DataGrid
,而 UserControl
又在另一个 UserControl
中。这是由于项目的其他需要,我无法更改这种嵌套架构。我将 Person
class 的列表绑定到此 DataGrid
。这是一个没有使用 VM
的简化版本,但在我的真实项目中我使用的是 VM
.
我的 UserControl
和 DataGrid
:
<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
在您的所有绑定中都毫无意义。
我在 UserControl
中有一个 DataGrid
,而 UserControl
又在另一个 UserControl
中。这是由于项目的其他需要,我无法更改这种嵌套架构。我将 Person
class 的列表绑定到此 DataGrid
。这是一个没有使用 VM
的简化版本,但在我的真实项目中我使用的是 VM
.
我的 UserControl
和 DataGrid
:
<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
在您的所有绑定中都毫无意义。