数据网格中的双向绑定:源未更新
本文关键字:绑定 更新 数据网 网格 数据 | 更新日期: 2023-09-27 18:35:44
我在双向绑定上遇到了问题,该绑定从源到目标工作正常,但目标上的更新永远不会传播到源。
我在 DataGrid 中显示一个自定义用户控件,该控件显示带有星号的评级:
* View.xaml
<DataGrid x:Name="Datagrid" Style="{StaticResource ResourceKey=DataGridStyle}" Grid.Row="1" AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding Path=GameList}" SelectedItem="{Binding Path=SelectedGame}" SelectionChanged="datagrid_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTemplateColumn Header="Rating" SortMemberPath="Rating">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<controls:RatingControl NumberOfStars="{Binding Path=Rating}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
* RatingControl.xaml.cs
public partial class RatingControl : UserControl
{
#region Public Dependency properties
public int NumberOfStars
{
get { return (int)GetValue(NumberOfStarsProperty); }
set { if ((int)GetValue(NumberOfStarsProperty) != value) { SetValue(NumberOfStarsProperty, value); } }
}
public static readonly DependencyProperty NumberOfStarsProperty =
DependencyProperty.Register("NumberOfStars", typeof(int), typeof(RatingControl),
new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnRatingChanged));
#endregion
[...]
private void Border_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
NumberOfStars = tempRating;
}
* 游戏.cs
public class Game : INotifyPropertyChanged
{
private int _rating;
public int Rating
{
get { return _rating; }
set { _rating = value; RaiseChangedEvent("Rating"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaiseChangedEvent(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
当我在 RatingControl 上单击一颗星时,我更新了 NumberOfStars 依赖项属性,但我的模型的 Rating 属性不会更新。我错过了什么?
这似乎是使用单元格模板等时 DataGrid 中的一个错误。 无论出于何种原因,除非显式设置 UpdateSourceTrigger,否则根本不会更新源。 奇怪的是,UpdateSourceTrigger将被设置为"默认",但就像它被设置为"显式"一样。
为了演示错误或意外行为,我以您的示例为例,并对其进行了一些调整。 您可以单击各种用户控件,并看到默认的双向绑定适用于所有内容,网格中的那些项除外。
注意:
此答案的其余部分只是一个代码转储,因此如果您对此答案感到满意,请不要再阅读了。
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<DataGrid x:Name="grid" Grid.Row="1" AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding Path=GameList}" MinHeight="100" >
<DataGrid.Columns>
<DataGridTextColumn Header="Title" Binding="{Binding Title}" />
<DataGridTemplateColumn Header="Rating">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<local:MyControl Number="{Binding Path=Rating}" Loaded="TemplateControlLoaded" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<ListBox Name="lst" ItemsSource="{Binding GameList}">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<local:MyControl Number="{Binding Rating}" />
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<local:MyControl x:Name="ctl" Loaded="MyControl_Loaded" Number="{Binding Path=Rating}" />
</StackPanel>
</Window>
MainWindow.xaml.cs
// ============================================================================================================================
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
grid.DataContext = new MyClassContainer();
lst.DataContext = new MyClassContainer();
ctl.DataContext = new MyClass();
}
private void TemplateControlLoaded(object sender, RoutedEventArgs e)
{
BindingExpression be = (sender as FrameworkElement).GetBindingExpression(MyControl.NumberProperty);
Console.WriteLine("Template trigger = " + be.ParentBinding.UpdateSourceTrigger.ToString());
}
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
BindingExpression be = (sender as FrameworkElement).GetBindingExpression(MyControl.NumberProperty);
Console.WriteLine("Instance trigger = " + be.ParentBinding.UpdateSourceTrigger.ToString());
}
}
// ============================================================================================================================
public class MyClassContainer
{
public List<MyClass> GameList { get; set; }
public MyClassContainer()
{
GameList = new List<MyClass>();
GameList.Add(new MyClass()
{
Rating = 150
});
}
}
// ============================================================================================================================
public class MyClass : INotifyPropertyChanged
{
private int _Rating;
public int Rating
{
get { return _Rating; }
set
{
_Rating = value; NotifyChange("Rating");
Console.WriteLine("changed!");
}
}
private void NotifyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
MyControl.xaml
<UserControl x:Class="WpfApplication1.MyControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="this">
<Grid Background="Red" PreviewMouseDown="Grid_PreviewMouseDown">
<TextBlock Text="{Binding ElementName=this, Path=Number}" FontWeight="Bold" />
</Grid>
</UserControl>
MyControl.xaml.cs
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public static readonly DependencyProperty NumberProperty = DependencyProperty.Register("Number", typeof(int), typeof(MyControl), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public int Number
{
get { return (int)GetValue(NumberProperty); }
set { SetValue(NumberProperty, value); }
}
private void Grid_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
this.Number = 1000;
}
}
将 StaticResource 更改为 DynamicResource。