【问题标题】:Why does ItemsControl is not updated?为什么 ItemsControl 没有更新?
【发布时间】:2017-05-19 20:07:22
【问题描述】:

代码使用Path 创建多边形。每次用户双击它都会关闭多边形并为第二个多边形添加另一个Path 对象,依此类推。我正在使用PointsToPathConverterPoints 转换为Path 想要的集合。

积分被添加到Areas 集合中,但由于某种原因OnPropertyChanged("Areas"); 没有更新ItemsControl。可能是什么原因?

XAML

<ItemsControl ItemsSource="{Binding Areas}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Path Data="{Binding Path=., Converter={StaticResource ResourceKey=PointsToPathConverter}}" Stroke="Black" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

视图模型

public class VM : INotifyPropertyChanged
{
    private ICommand _addPointCommand;
    public ICommand AddPointCommand
    {
        get
        {
            if (_addPointCommand == null)
            {
                _addPointCommand = new RelayCommand<MouseButtonEventArgs>(AddPoint);
            }

            return _addPointCommand;
        }
    }

    private ObservableCollection<List<Point>> _areas { get; set; }
    public ObservableCollection<List<Point>> Areas
    {
        get
        {
            if (_areas == null)
            {
                _areas = new ObservableCollection<List<Point>>();
            }
            return _areas;
        }
    }

    public VM()
    {
        Areas = new ObservableCollection<List<Point>>();
        Areas.Add(new List<Point>());
    }

    private void AddPoint(MouseButtonEventArgs e)
    {
        var curPoints = Areas[Areas.Count - 1];
        curPoints.Add(e.GetPosition((IInputElement)e.Source));

        if (e.ClickCount == 2 && curMaskPoints.Count > 0)
        {
            curMaskPoints.Add(curMaskPoints[0]);
            Areas.Add(new List<Point>());
        }

        OnPropertyChanged("Areas");
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

}

public class PointsToPathConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var points = (value as List<Point>);
            if (points.Count > 0)
            {
                Point start = points[0];
                List<LineSegment> segments = new List<LineSegment>();
                for (int i = 1; i < points.Count; i++)
                {
                    segments.Add(new LineSegment(points[i], true));
                }
                PathFigure figure = new PathFigure(start, segments, false); //true if closed
                PathGeometry geometry = new PathGeometry();
                geometry.Figures.Add(figure);
                return geometry;
            }
            else
            {
                return null;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotSupportedException();
        }

        #endregion
    }

b

【问题讨论】:

  • 您可能想在 Areas 属性发生变化时通知您,但这里 Areas 的值永远不会改变。 Areas 的值是对 ObservableCollection 实例的引用。
  • PropertyChanged 事件只会通知。不多不少。绑定控件决定如何处理该通知,有些控件会将旧值与新值进行比较,如果没有变化,它们将什么也不做。
  • @EdPlunkett,然后当用户单击时,它会向集合 var curPoints = Areas[Areas.Count - 1]; curPoints.Add(e.GetPosition((IInputElement)e.Source)); 添加一个点。然后我引发属性事件更改为刷新整个ItemsControl
  • 你需要类似 ObservableCollection>
  • 对不起,我们不能。我们可以用 ObservableCollection> ... 来做到这一点,但你也可以。只需将 List 更改为 ObservableCollection

标签: c# wpf data-binding observablecollection


【解决方案1】:

这里是修复。使嵌套集合可观察并没有帮助。您需要将嵌套集合替换为任一类型的新集合——包含相同的点集。无论您使用哪种类型的集合。只需要是一个不同的集合对象instance

private void AddPoint(MouseButtonEventArgs e)
{
    var curPoints = Areas[Areas.Count - 1];
    curPoints.Add(e.GetPosition((IInputElement)e.Source));

    //  ** fix ** 
    Areas[Areas.Count - 1] = new List<Point>(curPoints);
    //  ** end fix ** 

    if (e.ClickCount == 2 && curMaskPoints.Count > 0)
    {
        curMaskPoints.Add(curMaskPoints[0]);
        Areas.Add(new List<Point>());
    }
}

Rufo 爵士更受人尊敬的修复将出于同样的原因做同样的事情:它强制通知发生,只是通过不同的机制。

【讨论】:

  • ... 并且内存压力较小(Point 结构的复制):o)
  • @SirRufo Bah。没有人点击那么多:)
  • @EdPlunkett,我实际上与您的解决方案类似。我只是删除并重新添加集合。 Masks.RemoveAt(Masks.Count - 1); Masks.Add(curMaskPoints); 并且有效。
  • @theateist 相同的区别。
  • 但是,我不明白为什么OnPropertyChanged("Areas"); 不强制ItemsControl 更新?
【解决方案2】:

问题的根源就在这里

<Path Data="{Binding Path=., Converter={StaticResource ResourceKey=PointsToPathConverter}}" Stroke="Black" />

对于Path.Data 的更新,必须有一个通知。

修改你的代码

public class ObservableArea : GalaSoft.MvvmLight.ObservableObject
{
    public ObservableArea()
    {
        Points = new ObservableCollection<Point>();
        Points.CollectionChanged += ( s, e ) => RaisePropertyChanged( nameof( Points ) );
    }
    public ObservableCollection<Point> Points { get; }
}

public class VM : INotifyPropertyChanged
{
    private ICommand _addPointCommand;
    public ICommand AddPointCommand
    {
        get
        {
            if (_addPointCommand == null)
            {
                _addPointCommand = new RelayCommand<MouseButtonEventArgs>(AddPoint);
            }

            return _addPointCommand;
        }
    }

    private ObservableCollection<ObservableArea> _areas { get; set; }
    public ObservableCollection<ObservableArea> Areas
    {
        get
        {
            if (_areas == null)
            {
                _areas = new ObservableCollection<ObservableArea>();
            }
            return _areas;
        }
    }

    public VM()
    {
        Areas = new ObservableCollection<ObservableArea>();
        Areas.Add(new ObservableArea());
    }

    private void AddPoint(MouseButtonEventArgs e)
    {
        var curPoints = Areas[Areas.Count - 1];
        curPoints.Points.Add(e.GetPosition((IInputElement)e.Source));

        if (e.ClickCount == 2 && curMaskPoints.Count > 0)
        {
            curMaskPoints.Add(curMaskPoints[0]);
            Areas.Add(new ObservableArea());
        }
        // useless and can be removed
        // OnPropertyChanged("Areas");
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

}

和 XAML 到

<ItemsControl ItemsSource="{Binding Areas}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Path Data="{Binding Path=Points, Converter={StaticResource ResourceKey=PointsToPathConverter}}" Stroke="Black" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

插件

作为一个证明,为什么在值未更改的属性上引发 PropertyChanged 事件是无用的:

Areas 绑定到 ItemsControl.ItemsSource 属性,这是一个 DependencyProperty (have a look at the Reference Source) 并且控件表示的更改只会发生在值的真正更改时,因为 DP 逻辑本身会比较新旧值,如果不相等,它将调用 PropertyChangedCallback(参见参考源中的ItemsControl.OnItemsSourceChanged)。

每当引发PropertyChanged 事件时,绑定都会分配值,但如果值与旧值相同,则不会有更多操作。

using System;
using System.Windows;

public class Program
{
    public static void Main()
    {
        var foo = new Foo();

        Console.WriteLine( "Set foo.Bar to 1" );
        foo.Bar = 1;
        Console.WriteLine( "Set foo.Bar to 1 (assigning the same value)" );
        foo.Bar = 1;
        Console.WriteLine( "Set foo.Bar to 2" );
        foo.Bar = 2;
    }
}

public class Foo : DependencyObject
{
    public int Bar
    {
        get { return (int) GetValue( BarProperty ); }
        set { SetValue( BarProperty, value ); }
    }

    public static readonly DependencyProperty BarProperty =
        DependencyProperty.Register(
            "Bar",
            typeof( int ),
            typeof( Foo ),
            new PropertyMetadata(
                defaultValue: 0,
                propertyChangedCallback: new PropertyChangedCallback( OnBarChanged ) ) );

    private static void OnBarChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
    {
        Console.WriteLine( "OnBarChanged: Property has changed from '{0}' to '{1}'", e.OldValue, e.NewValue );
    }
}

Run it live on .netfiddle

【讨论】:

  • 我确实收到了更改通知。它在OnPropertyChanged("Areas");
  • @theateist 正如我告诉你的那样,该通知是浪费时间。它没有效果。我们都解决了你的问题。我们都在这样做时删除了那个毫无意义的通知。
  • 好吧,试试看,你会发现你的区域通知是没用的。我只是在这里评论一下
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-12
  • 2021-08-04
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多