为什么ItemsControl没有更新?

时间:2017-05-19 20:07:23

标签: c# wpf data-binding observablecollection

代码使用Path创建多边形。每次用户双击它时,它会关闭多边形并为第二个多边形添加另一个Path对象,依此类推。我正在使用PointsToPathConverterPoint转换为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'/ P>

2 个答案:

答案 0 :(得分:1)

这是解决方法。使嵌套集合可观察无济于事。您需要将嵌套集合替换为任一类型的新集合 - 包含相同的点集。您使用哪种类型的集合并不重要。只需要是一个不同的集合对象实例

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先生更受尊敬的解决方案会做同样的事情:它只是通过不同的机制强制通知发生。

答案 1 :(得分:1)

问题的原因在这里

<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属性DependencyPropertyhave 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