数据网格中的双向绑定:源未更新

本文关键字:绑定 更新 数据网 网格 数据 | 更新日期: 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