【问题标题】:Binding not updated when NotifyPropertyChange invoked despite ValueConverter being called尽管调用了 ValueConverter,但调用 NotifyPropertyChange 时绑定未更新
【发布时间】:2016-10-08 01:23:33
【问题描述】:

花了几天的谷歌搜索来解决这个问题,我希望你们中的一个人能够提供帮助。

我在 Grid 控件上有一个自定义依赖属性。首次创建 Observable Collection 时,它会正确更新。但是,当更新集合(在我的简化示例中按下更新按钮)并且调用 NotifyPropertyChanged 时,不会调用依赖属性的更新方法。正在调用绑定的值转换器并显示更新的数据(暗示绑定尚未被覆盖)

请注意,这是 Windows Universal,因此某些 WPF 绑定跟踪信息似乎不可用。

谁能说明为什么依赖属性的更新方法只在第一次调用而不是在调用 NotifyPropertyChanged 时调用?

罗伯特

<Page
    x:Class="CustomBindingTest.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomBindingTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d" Loaded="Page_Loaded">

    <Page.Resources>
        <local:DebugConverter x:Key="debug"/>
    </Page.Resources>

    <StackPanel Orientation="Vertical">
        <Button Height="50" Click="Button_Click">Update</Button>
        <Grid local:GridHelpers.Cells="{Binding Path=Cells, Mode=TwoWay, Converter={StaticResource debug}}" Height="500">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
        </Grid>
    </StackPanel>
</Page>

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace CustomBindingTest
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public ObservableCollection<Cell> Cells { get; set; }

        public MainPage()
        {
            this.InitializeComponent();
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            Cells = new ObservableCollection<Cell>();
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());

            DataContext = this;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());
            Cells.Add(new Cell());

            NotifyPropertyChanged("Cells");
        }

        #region NotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion

    }
}

<UserControl
    x:Class="CustomBindingTest.Cell"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CustomBindingTest"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <Grid Background="Green" Margin="5">

    </Grid>
</UserControl>

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Text;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace CustomBindingTest
{
    public class GridHelpers
    {
        #region Cells Property

        /// <summary>
        /// Adds the Cell objectss to Grid. 
        /// </summary>
        public static readonly DependencyProperty CellsProperty =
            DependencyProperty.RegisterAttached(
                "Cells", typeof(ObservableCollection<Cell>), typeof(GridHelpers),
                new PropertyMetadata(null, CellsChanged));

        // Get
        public static ObservableCollection<Cell> GetCells(DependencyObject obj)
        {
            return (ObservableCollection<Cell>)obj.GetValue(CellsProperty);
        }

        // Set
        public static void SetCells(DependencyObject obj, ObservableCollection<Cell> value)
        {
            obj.SetValue(CellsProperty, value);
        }

        // Change Event - Add the Cells
        public static void CellsChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || e.NewValue == null)
                return;

            Grid grid = (Grid)obj;

            List<Cell> cells = ((ObservableCollection<Cell>)e.NewValue).ToList();

            if (cells != null)
            {
                if (cells.Count() == 0)
                {
                    ShowEmpty(grid);
                }
                else
                {
                    int rowCount = grid.RowDefinitions.Count();
                    int colCount = grid.ColumnDefinitions.Count();

                    for (int row = 0; row < rowCount; row++)
                    {
                        for (int col = 0; col < colCount; col++)
                        {
                            int index = (row * colCount) + col;
                            if (index < cells.Count())
                            {
                                Cell cell = cells[index];
                                Debug.WriteLine("Row {0} Col {1} Cell {2}", row, col, index);
                                cell.SetValue(Grid.ColumnProperty, col);
                                cell.SetValue(Grid.RowProperty, row);
                                grid.Children.Add(cell);
                            }
                        }
                    }
                }
            }
        }

        private static void ShowEmpty(Grid grid)
        {
            TextBlock text = new TextBlock();
            text.Text = "[Empty Grid]";
            text.FontSize = 24.0;
            text.Foreground = new SolidColorBrush(Colors.Red);
            text.FontWeight = FontWeights.Bold;
            text.SetValue(Grid.ColumnProperty, 0);
            text.SetValue(Grid.RowProperty, 0);
            grid.Children.Add(text);
        }

        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Data;

namespace CustomBindingTest
{
    class DebugConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}

【问题讨论】:

  • 您可以直接编辑您的帖子,而不是在 cmets 中进行更新。这将使问题在未来更具可读性。

标签: xaml binding uwp


【解决方案1】:

发生这种情况是因为您实际上并未更改 Cells 集合实例,而只是向其中添加项目。

最初,Dependency 属性值为 null。 在 Page_Loaded 事件中,您正在创建 ObservableCollection() 的新实例,当您设置 DataContext 时,它会更新绑定并将依赖属性值设置为该实例。

在 Button_Click 事件中,您只是在集合中添加新项目,但这不会更改 Cells 实例,因此 Dependency 属性不会引发属性更改事件 - 即使您引发事件,框架也会注意到您实际上并没有更改实例,因此它忽略了调用。

我不完全确定您要实现什么目标,但我确实注意到您并没有真正将 Cells 用作 ObservableCollection - 因为您在 Dependency ChangedCells 事件中将其转换为 List。作为一种解决方法,您可以尝试将 Cells 对象更改为 List 并在每次点击更新按钮时重新创建它。

【讨论】:

  • 感谢 Alex - 非常感谢。我原以为 NotifyPropertyChange 会强制更新视图,即使集合就绑定而言根本没有改变。这显然是错误的!此外,我提供的代码是说明问题的简化示例,因此 Cells 对象在实际项目中的实际使用方式不同。再次感谢您的帮助。
  • 我刚刚查看了位于msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx 的文档,其中指出:表示一个动态数据集合,在添加、删除或刷新整个列表时提供通知。我>
  • 很高兴听到它帮助了罗伯特。如果您正在寻找答案,请不要忘记将帖子标记为答案!
猜你喜欢
  • 1970-01-01
  • 2021-11-27
  • 2020-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多