如何使用数据绑定在画布上绘制数据点集合?

时间:2016-04-20 12:03:15

标签: c# wpf

我有一组数据点,它们存储X和Y值以及两个坐标:点本身和下一个点的像素位置。

然后我有一个ItemsControl,它绑定到集合并绘制一条线,将当前点连接到下一个点,形成存储在集合中的所有数据点的折线图。

  <ItemsControl x:Name="GraphCanvas"
            ItemsSource="{Binding LineChartData}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Canvas>
                            <Canvas.Resources>
                                <local:BindingProxy x:Key="proxy" Data="{Binding}" />
                            </Canvas.Resources>
                            <Path Stroke="Black" StrokeThickness="1">
                                <Path.Data>
                                    <GeometryGroup>
                                        <PathGeometry>
                                            <PathFigure StartPoint="{Binding Source={StaticResource proxy}, Path=Data.CurrentPoint}">
                                                <PathFigure.Segments>
                                                    <LineSegment Point="{Binding Source={StaticResource proxy}, Path=Data.NextPoint}"/>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                        </PathGeometry>
                                    </GeometryGroup>
                                </Path.Data>
                            </Path>
                        </Canvas>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>

一切正常但是我想知道是否有更好的方法来做到这一点,因为当我必须调整我的控制时,我必须遍历每个数据点并重新计算它们的当前点和下一个点,如下所示:

    Point currentPoint;
    Point nextPoint = getCanvasPoint(lineChartData[0]);
    for (int i = 1; i < lineChartData.Count; i++)
    {
        var dataPoint = lineChartData[i];
        currentPoint = nextPoint;
        nextPoint = getCanvasPoint(dataPoint);

        dataPoint.CurrentPoint = currentPoint;
        dataPoint.NextPoint = nextPoint;
    }

这是一个相当缓慢的过程,并使调整大小非常紧张。我想知道是否有更好的方法来绑定X&amp;列表Y值为itemscontrol,以便我可以在屏幕上绘制它们。

它的外观如下: enter image description here

2 个答案:

答案 0 :(得分:2)

我一直在使用MultiBinding。

在视图中,我绑定到:

<Grid x:Name="root" RenderTransformOrigin="0.5 0.5">
    <Grid.RenderTransform>
        <ScaleTransform ScaleY="-1"/>
    </Grid.RenderTransform>
    <Path Stroke="Brown" StrokeThickness="0.5"
          Stretch="Fill"
          StrokeEndLineCap="Round" StrokeLineJoin="Round">
        <Path.Data>
            <MultiBinding Converter="{StaticResource SinalToGeometryConverter}">
                <Binding ElementName="root" Path="ActualWidth"/>
                <Binding ElementName="root" Path="ActualHeight"/>
                <Binding Path="Samples"/>
            </MultiBinding>
        </Path.Data>
    </Path>
</Grid>

然后在转换器中我生成一个或多或少像这样的路径:

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Any(v => v == null) &&
            values.Any(v => v == DependencyProperty.UnsetValue))
            return null;

        double viewportWidth = (double)values[0];
        double viewportHeight = (double)values[1];

        IEnumerable<int> samples= values[2] as IEnumerable<int>;

        var sb = new StringBuilder("M");
        int x_coord = 0; // could be taken from sample if available
        foreach (var y_coord in samples)
            sb.AppendFormat(" {0} {1}", x_coord++, y_coord);

        var result = Geometry.Parse(sb.ToString());

        return result;
    }

然后,每当其中一个绑定的东西发生变化时,由于数据源更改或视口更改,您将获得一个全新的路径。如果数据点数少于plotterControl.ActualWidth,则速度相对较快。

答案 1 :(得分:0)

我使用Clemens评论来构建我的解决方案就是我如何做到的:

我将PolyLineSegment的Points属性绑定到ViewModel中的点集合。然后我使用变换组来缩放和平移路径以适应轴。

以下是代码:

   <Canvas Background="Transparent" Grid.Row="1" Grid.Column="2" x:Name="GraphCanvas">
        <Path Stroke="Black" StrokeThickness="1">
            <Path.Data>
                <PathGeometry>
                    <PathGeometry.Transform>
                        <TransformGroup>
                            <ScaleTransform ScaleX="{Binding xScale}" ScaleY="{Binding yScale}"/>
                            <TranslateTransform Y="{Binding yAxisTranslation}"/>
                        </TransformGroup>
                    </PathGeometry.Transform>
                    <PathGeometry.Figures>
                        <PathFigureCollection>
                            <PathFigure StartPoint="{Binding FirstPoint}">
                                <PathFigure.Segments>
                                    <PathSegmentCollection>
                                        <PolyLineSegment Points="{Binding Points}"/>
                                    </PathSegmentCollection>
                                </PathFigure.Segments>
                            </PathFigure>
                        </PathFigureCollection>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>
        <Path Stroke="Black" StrokeThickness="1">
            <Path.Data>
                <PathGeometry>
                    <PathGeometry.Figures>
                        <PathFigureCollection>
                            <PathFigure StartPoint="0,0">
                                <LineSegment Point="{Binding OriginPoint}"/>
                                <LineSegment Point="{Binding xAxisEndPoint}"/>
                            </PathFigure>
                        </PathFigureCollection>
                    </PathGeometry.Figures>
                </PathGeometry>
            </Path.Data>
        </Path>
    </Canvas>

我真的很喜欢这个解决方案,它似乎比原始代码快得多,特别是在调整大小时。