WPF路径绑定到PointCollection不更新UI

时间:2014-02-07 02:16:31

标签: c# wpf xaml mvvm binding

我正在尝试学习MVVM样式更新。不是那么游泳。

我不得不更新一个基于对Point Collection的更改而绘制的简单矩形。 在初始化时,UI更新,但在对Point Collection进行简单更改后,UI中的路径不会更新。

我添加了一些TextBlock来确保Change事件被触发,但此时我有点迷失。

任何帮助表示赞赏:

XAML:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ExampleGreg" x:Class="ExampleGreg.MainWindow"
        Title="MainWindow" Height="161.614" Width="324.087">
    <Grid x:Name="gridUser" MouseDown="click_MouseDown" >
        <Canvas x:Name="MeterCanvas" Margin="14,7,30,0" Background="#FFAFAFAF" Height="35" VerticalAlignment="Top">
            <Path Stroke="Black" StrokeThickness="2">
                <Path.Data>
                    <PathGeometry x:Name="geometry"/>
                </Path.Data>
            </Path>
            <Path Stroke="Black" StrokeThickness="2">
                <Path.Data>
                    <PathGeometry x:Name="polylinePwr">
                        <PathGeometry.Transform>
                            <ScaleTransform ScaleX="{Binding ActualWidth, ElementName=MeterCanvas}" ScaleY="{Binding ActualHeight, ElementName=MeterCanvas}" />
                        </PathGeometry.Transform>
                        <PathGeometry.Figures>
                            <PathFigure IsClosed ="True" StartPoint="{Binding Path=thePoints[0]}">
                                <PathFigure.Segments>
                                    <PathSegmentCollection>
                                        <PolyLineSegment Points="{Binding thePoints, UpdateSourceTrigger=PropertyChanged}" />
                                    </PathSegmentCollection>
                                </PathFigure.Segments>
                            </PathFigure>
                        </PathGeometry.Figures>
                    </PathGeometry>
                </Path.Data>
            </Path>
        </Canvas>
        <Label Content="{Binding thePoints[0]}" Margin="14,58,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
        <Label Content="{Binding thePoints[1]}" Margin="14,88,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
        <Label Content="{Binding thePoints[2]}" Margin="165,58,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
        <Label Content="{Binding thePoints[3]}" Margin="165,93,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
    </Grid>
</Window>

主窗口:

    public partial class MainWindow : Window
{
    private MainViewModel mvm;
    public MainWindow()
    {
        InitializeComponent();
        mvm = new MainViewModel();
        this.DataContext = mvm;

    }

    private void click_MouseDown(object sender, MouseButtonEventArgs e)
    {
        mvm.theText = mvm.theText + ".";
        mvm.ChangePoint(.4);
    }
}

模型视图:

    class MainViewModel : INotifyPropertyChanged
{
    private string _theText = "Initial";
    private PointCollection _points = new PointCollection();
    private PolyLineSegment segment;

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(String info)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(info));
        }
    }
    public MainViewModel()
    {
        ChangePoint(0.9);
    }

    public string theText
    {
        get { return _theText; }
        set
        {
            if (_theText != value)
            {
                _theText = value;
                OnPropertyChanged("theText");
            }
        }
    }
    public PointCollection thePoints
    {
        get
        { return _points; }
    }

    public void ChangePoint(double x)
    {
        _points.Clear();
        AddPoint(new Point(0.2, 0.2));
        AddPoint(new Point(0.2, 0.8));
        AddPoint(new Point(x, 0.8));
        AddPoint(new Point(x, 0.2));

        OnPropertyChanged("thePoints");
        _theText = _theText + "!";
        OnPropertyChanged("theText");


    }

    public void AddPoint(Point p)
    {
        _points.Add(p);
    }
}

感谢您的任何建议(工作:))

- 根据以下答案进行编辑 - 我添加了一个IValueConverter类:

    public class PointCollectionConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value.GetType() == typeof(ObservableCollection<Point>) && targetType == typeof(PointCollection))
        {
            var pointCollection = new PointCollection();
            foreach (var point in value as ObservableCollection<Point>)
                pointCollection.Add(point);
            return pointCollection;
        }
        return null;

    }

    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return null; //not needed
    }

    #endregion
}

并将ModelView更改为使用Observable Collection ... 更新了ModelView:

    class MainViewModel : INotifyPropertyChanged
{
    private string _theText = "Initial";
    private ObservableCollection<Point> _points;
    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(String info)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(info));
        }
    }

    public MainViewModel()
    {
        _points = new ObservableCollection<Point>();
        _points.Add(new Point(0.2, 0.2));
        _points.Add(new Point(0.2, 0.8));
    }

    public string theText
    {
        get { return _theText; }
        set
        {
            if (_theText != value)
            {
                _theText = value;
                OnPropertyChanged("theText");
            }
        }
    }
    public ObservableCollection<Point> thePoints
    {
        get
        { return _points; }
    }

    double xAdder = 0;
    double y = 0.0;
    public void ChangePoint(double x)
    {
        y = y + .1;
        if (y > .9) { y = .1; xAdder += .1; }
        _points.Add(new Point(x+xAdder, y));
        OnPropertyChanged("thePoints");

        _theText = _theText + "!";
        OnPropertyChanged("theText");

    }

}

更新了XAML:

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ExampleGreg"  x:Class="ExampleGreg.MainWindow"
    Title="MainWindow" Height="161.614" Width="324.087">
<Window.Resources>
    <local:PointCollectionConverter x:Key="pointCollectionConverter"/>
</Window.Resources>
<Grid x:Name="gridUser" MouseDown="click_MouseDown" >
    <Canvas x:Name="MeterCanvas" Margin="14,7,30,0" Background="#FFAFAFAF" Height="35" VerticalAlignment="Top">
        <Path Stroke="Black" StrokeThickness="2">
            <Path.Data>
                <PathGeometry x:Name="geometry"/>
            </Path.Data>
        </Path>
        <Path Stroke="Black" StrokeThickness="2">
            <Path.Data>
                <PathGeometry x:Name="polylinePwr">
                    <PathGeometry.Transform>
                        <ScaleTransform ScaleX="{Binding ActualWidth, ElementName=MeterCanvas}" ScaleY="{Binding ActualHeight, ElementName=MeterCanvas}" />
                    </PathGeometry.Transform>
                    <PathGeometry.Figures>
                        <PathFigure StartPoint="{Binding Path=thePoints[0], Converter={StaticResource pointCollectionConverter}}">
                            <PathFigure.Segments>
                                <PathSegmentCollection>
                                    <PolyLineSegment Points="{Binding thePoints, Converter={StaticResource pointCollectionConverter}}" />
                                </PathSegmentCollection>
                            </PathFigure.Segments>
                        </PathFigure>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>
    </Canvas>
    <Label Content="{Binding thePoints[0]}" Margin="14,58,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
    <Label Content="{Binding thePoints[1]}" Margin="14,88,199.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
    <Label Content="{Binding thePoints[2]}" Margin="165,58,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
    <Label Content="{Binding thePoints[3]}" Margin="165,93,29.6,0" VerticalAlignment="Top" Height="30" BorderThickness="1"/>
</Grid>

我不确定我是否会获得任何性能提升,因为IValueConverter创建了一个新的PointCollection每个ObservableCollection都被更改。

感谢大家帮助我完成这项工作。我希望将完整的代码粘贴为有效,如果有其他代码遇到同样的事情。

编辑#2 - 将Observable w / IValueConverter与复制PointCollection基准测试到新的PointCollection后,Point Collection看起来更快:

    // Option 2 - WAY Faster.
    // Just Use a Points Collection, copy it, and add a point.
    public void AddPoint2(Point pt)
    {
        PointCollection pc = new PointCollection(_points2);
        pc.Add(new Point(pt.X, pt.Y));
        _points2 = pc;
        OnPropertyChanged("thePoints2");
    }

来自

            // 5000 point sinwave for testing
            foreach (Point pt in sinWave) mvm.AddPoint2(pt);
            x++;

如果有更好的方式,评论表示赞赏。

1 个答案:

答案 0 :(得分:1)

尝试替换属性,而不是清除+重新填充它。

public void ChangePoint(double x)
{
    var newPoints = new PointCollection();
    newPoints.Add(new Point(0.2, 0.2));
    newPoints.Add(new Point(0.2, 0.8));
    newPoints.Add(new Point(x, 0.8));
    newPoints.Add(new Point(x, 0.2));

    OnPropertyChanged("thePoints");
    _theText = _theText + "!";
    OnPropertyChanged("theText");
}

似乎绑定有时会忽略&#34; PropertyChanged&#34;如果对象引用相同,则为event。