在ItemsControl中使用GeometryDrawing

时间:2014-11-03 15:43:12

标签: c# wpf performance data-binding itemscontrol

在我的WPF应用程序中,我使用包含ObservableCollection的类的PointCollection。此点集合用于为可观察集合中的每个项目绘制多边形。下面的代码显示了我如何使用模板绑定来绘制所有多边形:

<Grid>
<ItemsControl ItemsSource="{Binding GeoLines}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type vm:GeoPointsViewModel}">
                <Polygon Stroke="LightSkyBlue" StrokeThickness="0.5" Opacity="0.8">                      
                    <Polygon.Style>
                        <Style TargetType="{x:Type Polygon}">
                            <Setter Property="Points">
                                <Setter.Value>
                                    <MultiBinding Converter="{StaticResource pointMultiConverter}">
                                        <Binding Path="ActualWidth" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                                        <Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                                        <Binding Path="Points"/>
                                    </MultiBinding>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="Fill" Value="#FF0A0A10"/>                                
                        </Style>
                    </Polygon.Style>
                </Polygon>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

虽然效果很好,但性能并不高,因为每个多边形都是UIElement,带有所有的铃声和​​口哨声。为了提高性能,我想使用占地面积较小的GeometryDrawing。不幸的是,我不知道在这种情况下如何进行数据绑定。所以,我的问题是我如何使用GeometryDrawing实现代码(或者比Shapes重量轻的任何代码。)

2 个答案:

答案 0 :(得分:2)

我不确定使用GeometryDrawing是否可以显着改善性能,但是您可以通过以下方式实现它。结果可能看起来并不完全符合您的要求,因为StartPoint的默认PathFigure

<DataTemplate DataType="{x:Type vm:GeoPointsViewModel}">
   <Border>
      <Border.Background>
         <DrawingBrush>
                <DrawingBrush.Drawing>
                    <GeometryDrawing Brush="Red">
                        <GeometryDrawing.Geometry>
                            <PathGeometry>
                                <PathFigure IsClosed="True">
                                    <PolyLineSegment Points="{Binding Points}"/>
                                </PathFigure>
                            </PathGeometry>
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                </DrawingBrush.Drawing>
          </DrawingBrush>
      </Border.Background>
   </Border>
</DataTemplate>

我使用简单的Red画笔作为折线。如果需要,您还可以指定一些Pen。请注意,多边形的效果可能与原始代码产生的效果不完全相同。我想你可能需要做一些调整才能做到正确。

答案 1 :(得分:1)

我最终使用后面的代码直接绘制视觉效果。这绕过了很多UI层并显着提高了性能。任何其他改进此代码的建议仍然受到欢迎。

    private Brush geoBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#FF0A0A10"));
    private Pen geoPen = new Pen(Brushes.LightSkyBlue, 0.5);
    private DropShadowEffect geoDropShadow = new DropShadowEffect
    {
        Color = Brushes.LightSteelBlue.Color,
        BlurRadius = 8.0,
        ShadowDepth = 0.0
    };
    private DrawingVisual GeoVisual = null;
    private void UpdateGeoLines()
    {
        MapProjectionViewModel map = this.DataContext as MapProjectionViewModel;
        if (map != null)
        {
            DrawingVisual visual = new DrawingVisual();
            using (DrawingContext dc = visual.RenderOpen())
            {
                foreach (var item in map.GeoLines)
                {
                    if (item.Points.Count > 1)
                    {
                        List<Point> points = new List<Point>();
                        foreach (var p in item.Points)
                        {
                            Point point = new Point(
                            p.X * canvas.ActualWidth,
                            p.Y * canvas.ActualHeight);
                            points.Add(point);
                        }
                        StreamGeometry geom = new StreamGeometry();
                        using (StreamGeometryContext gc = geom.Open())
                        {
                            Point p1 = points[0];

                            // Start new object, filled=true, closed=true
                            gc.BeginFigure(p1, true, true);

                            // isStroked=true, isSmoothJoin=true
                            gc.PolyLineTo(points, true, false);
                        }
                        geom.Freeze();
                        dc.DrawGeometry(geoBrush, geoPen, geom);
                    }
                }
            }

            visual.Effect = geoDropShadow;
            visual.Opacity = 0.8;
            canvas.Visuals.Remove(GeoVisual);
            canvas.Visuals.Add(visual);
            GeoVisual = visual;
        }
    }