代码之家  ›  专栏  ›  技术社区  ›  Derek

用于绘制图形的WPF折线相对点值和拉伸

  •  4
  • Derek  · 技术社区  · 16 年前

    我试图创建一个非常简单的图形组件,它由同一网格单元内表示图形线的一系列折线组成。我的策略是查看集合中的所有点,确定最小值和最大值,然后相应地计算一个0到1之间的数字,并使用Stretch=“Fill”拉伸每条折线以填充网格单元格。我想要的效果是,0、.5处的点将垂直地位于单元格的中心,但实际上,折线会垂直拉伸以填充整个单元格,具体取决于Y值的最小值和最大值。例如,如果折线中的.5是我的最大值,.7是我的最小值,那么.5在单元格的顶部是透明的,.7在底部也是透明的,而不是中心的.5和底部的.77/10。

    这是一个简单的例子,有两条折线和0到1之间的计算点。您会注意到红色折线直接位于蓝色折线的顶部,即使红色Y值更大。红色折线应与蓝色折线看起来相同,但在单元格中的方向略低。然而,它被拉伸以填充整个单元格,因此它直接位于蓝色的顶部。

    <Window x:Class="Test.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="100" Width="300">
    <Grid>
        <Polyline
            Stretch="Fill"
            Stroke="Blue"
            Points="0,0 0.2,0 0.2,0.363636363636364 0.4,0.363636363636364 0.4,0.636363636363636 0.6,0.636363636363636 0.6,0.0909090909090909 0.8,0.0909090909090909 0.8,0 1,0" />
        <Polyline
            Stretch="Fill"
            Stroke="Red"
            Points="0,0.363636363636364 0.2,0.363636363636364 0.2,0.727272727272727 0.4,0.727272727272727 0.4,1 0.6,1 0.6,0.454545454545455 0.8,0.454545454545455 0.8,0.363636363636364 1,0.363636363636364" />
    </Grid>
    

    2 回复  |  直到 16 年前
        1
  •  4
  •   repka    16 年前

    DrawingGroup GeometryDrawing

    <Window x:Class="Polyline.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="100" Width="300">
        <Grid>
            <Image>
                <Image.Source>
                    <DrawingImage>
                        <DrawingImage.Drawing>
                            <DrawingGroup>
                                <GeometryDrawing Brush="Transparent">
                                    <GeometryDrawing.Geometry>
                                        <RectangleGeometry Rect="0,0,1,1">
                                            <RectangleGeometry.Transform>
                                                <ScaleTransform ScaleX="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Grid}}"
                                                                ScaleY="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}"/>
                                            </RectangleGeometry.Transform>
                                        </RectangleGeometry>
                                    </GeometryDrawing.Geometry>
                                </GeometryDrawing>
                                <GeometryDrawing>
                                    <GeometryDrawing.Pen>
                                        <Pen Brush="Blue" Thickness="1"/>
                                    </GeometryDrawing.Pen>
                                    <GeometryDrawing.Geometry>
                                        <PathGeometry>
                                            <PathGeometry.Transform>
                                                <ScaleTransform ScaleX="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Grid}}"
                                                                ScaleY="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}"/>
                                            </PathGeometry.Transform>
                                            <PathGeometry.Figures>
                                                <PathFigure StartPoint="0,0">
                                                    <PathFigure.Segments>
                                                        <LineSegment Point="0.2,0"/>
                                                        <LineSegment Point="0.2,0.363636363636364"/>
                                                        <LineSegment Point="0.4,0.363636363636364"/>
                                                        <LineSegment Point="0.4,0.636363636363636"/>
                                                        <LineSegment Point="0.6,0.636363636363636"/>
                                                        <LineSegment Point="0.6,0.0909090909090909"/>
                                                        <LineSegment Point="0.8,0.0909090909090909"/>
                                                        <LineSegment Point="0.8,0"/>
                                                        <LineSegment Point="1,0"/>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                            </PathGeometry.Figures>
                                        </PathGeometry>
                                    </GeometryDrawing.Geometry>
                                </GeometryDrawing>
                                <GeometryDrawing>
                                    <GeometryDrawing.Pen>
                                        <Pen Brush="Red" Thickness="1"/>
                                    </GeometryDrawing.Pen>
                                    <GeometryDrawing.Geometry>
                                        <PathGeometry>
                                            <PathGeometry.Transform>
                                                <ScaleTransform ScaleX="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType=Grid}}"
                                                                ScaleY="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=Grid}}"/>
                                            </PathGeometry.Transform>
                                            <PathGeometry.Figures>
                                                <PathFigure StartPoint="0,0.363636363636364">
                                                    <PathFigure.Segments>
                                                        <LineSegment Point="0.2,0.363636363636364"/>
                                                        <LineSegment Point="0.2,0.727272727272727"/>
                                                        <LineSegment Point="0.4,0.727272727272727"/>
                                                        <LineSegment Point="0.4,1"/>
                                                        <LineSegment Point="0.6,1"/>
                                                        <LineSegment Point="0.6,0.454545454545455"/>
                                                        <LineSegment Point="0.8,0.454545454545455"/>
                                                        <LineSegment Point="0.8,0.363636363636364"/>
                                                        <LineSegment Point="1,0.363636363636364"/>
                                                    </PathFigure.Segments>
                                                </PathFigure>
                                            </PathGeometry.Figures>
                                        </PathGeometry>
                                    </GeometryDrawing.Geometry>
                                </GeometryDrawing>
                            </DrawingGroup>
                        </DrawingImage.Drawing>
                    </DrawingImage>
                </Image.Source>
            </Image>
        </Grid>
    
    </Window>
    

    RectangleGeometry

        2
  •  3
  •   Ray Burns    15 年前

    Path Line , Polyline ,以及 Polygon 除了它们让你很容易按照自己想要的方式伸展身体。

    ViewboxPath , ViewboxLine , ViewboxPolyline ViewboxPolygon

    <edf:ViewboxPolyline
        Viewbox="0 0 1 1"  <!-- Actually the default, can be omitted -->
        Stretch="Fill"     <!-- Also default, can be omitted -->
        Stroke="Blue"
        Points="0,0 0.2,0 0.2,0.3 0.4,0.3" />
    
    <edf:ViewboxPolygon
        Viewbox="0 0 10 10"
        Stroke="Blue"
        Points="5,0 10,5 5,10 0,5" />
    
    <edf:ViewboxPath
        Viewbox="0 0 10 10"
        Stroke="Blue"
        Data="M10,5 L4,4 L5,10" />
    

    就像普通形状一样使用( , , Viewbox Stretch="Fill" Fill , Uniform UniformToFill 设置,而不是使用 Geometry.GetBounds .

    这可以非常精确地控制拉伸,并使单独的形状很容易彼此精确对齐。

    ViewboxShape

    public abstract class ViewboxShape : Shape
    {
      Matrix _transform;
      Pen _strokePen;
      Geometry _definingGeometry;
      Geometry _renderGeometry;
    
      static ViewboxShape()
      {
        StretchProperty.OverrideMetadata(typeof(ViewboxShape), new FrameworkPropertyMetadata
        {
          AffectsRender = true,
          DefaultValue = Stretch.Fill,
        });
      }
    
      // The built-in shapes compute stretching using the actual bounds of the geometry.
      // ViewBoxShape and its subclasses use this Viewbox instead and ignore the actual bounds of the geometry.
      public Rect Viewbox { get { return (Rect)GetValue(ViewboxProperty); } set { SetValue(ViewboxProperty, value); } }
      public static readonly DependencyProperty ViewboxProperty = DependencyProperty.Register("Viewbox", typeof(Rect), typeof(ViewboxShape), new UIPropertyMetadata
      {
        DefaultValue = new Rect(0,0,1,1),
      });
    
      // If defined, replaces all the Stroke* properties with a single Pen
      public Pen Pen { get { return (Pen)GetValue(PenProperty); } set { SetValue(PenProperty, value); } }
      public static readonly DependencyProperty PenProperty = DependencyProperty.Register("Pen", typeof(Pen), typeof(ViewboxShape));
    
      // Subclasses override this to define geometry if caching is desired, or just override DefiningGeometry
      protected virtual Geometry ComputeDefiningGeometry()
      {
        return null;
      }
    
      // Subclasses can use this PropertyChangedCallback for properties that affect the defining geometry
      protected static void OnGeometryChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
      {
        var shape = sender as ViewboxShape;
        if(shape!=null)
        {
          shape._definingGeometry = null;
          shape._renderGeometry = null;
        }
      }
    
      // Compute viewport from box & constraint
      private Size ApplyStretch(Stretch stretch, Rect box, Size constraint)
      {
        double uniformScale;
        switch(stretch)
        {
          default:
            return new Size(box.Width, box.Height);
    
          case Stretch.Fill:
            return constraint;
    
          case Stretch.Uniform:
            uniformScale = Math.Min(constraint.Width / box.Width, constraint.Height / box.Height);
            break;
    
          case Stretch.UniformToFill:
            uniformScale = Math.Max(constraint.Width / box.Width, constraint.Height / box.Height);
            break;
        }
        return new Size(uniformScale * box.Width, uniformScale * box.Height);
      }
    
      protected override Size MeasureOverride(Size constraint)
      {
        // Clear pen cache if settings have changed
        if(_strokePen!=null)
          if(Pen!=null)
            _strokePen = null;
          else
            if(_strokePen.Thickness != StrokeThickness ||
               _strokePen.Brush != Stroke ||
               _strokePen.StartLineCap != StrokeStartLineCap ||
               _strokePen.EndLineCap != StrokeEndLineCap ||
               _strokePen.DashCap != StrokeDashCap ||
               _strokePen.LineJoin != StrokeLineJoin ||
               _strokePen.MiterLimit != StrokeMiterLimit ||
               _strokePen.DashStyle.Dashes != StrokeDashArray ||
               _strokePen.DashStyle.Offset != StrokeDashOffset)
              _strokePen = null;
    
        _definingGeometry = null;
        _renderGeometry = null;
    
        return ApplyStretch(Stretch, Viewbox, constraint);
      }
    
      protected override Size ArrangeOverride(Size availableSize)
      {
        Stretch stretch = Stretch;
        Size viewport;
        Matrix transform;
    
        // Compute new viewport and transform
        if(stretch==Stretch.None)
        {
          viewport = availableSize;
          transform = Matrix.Identity;
        }
        else
        {
          Rect box = Viewbox;
          viewport = ApplyStretch(stretch, box, availableSize);
    
          double scaleX = viewport.Width / box.Width;
          double scaleY = viewport.Height / box.Height;
          transform = new Matrix(scaleX, 0, 0, scaleY, -box.Left * scaleX, -box.Top * scaleY);
        }
    
        if(_transform!=transform)
        {
          _transform = transform;
          _renderGeometry = null;
          InvalidateArrange();
        }
        return viewport;
      }
    
      protected Pen PenOrStroke
      {
        get
        {
          if(Pen!=null)
            return Pen;
          if(_strokePen==null)
            _strokePen = new Pen
            {
              Thickness = StrokeThickness,
              Brush = Stroke,
              StartLineCap = StrokeStartLineCap,
              EndLineCap = StrokeEndLineCap,
              DashCap = StrokeDashCap,
              LineJoin = StrokeLineJoin,
              MiterLimit = StrokeMiterLimit,
              DashStyle =
                StrokeDashArray.Count==0 && StrokeDashOffset==0 ? DashStyles.Solid :
                new DashStyle(StrokeDashArray, StrokeDashOffset),
            };
          return _strokePen;
        }
      }
    
      protected Matrix Transform
      {
        get
        {
          return _transform;
        }
      }
    
      protected override Geometry DefiningGeometry
      {
        get
        {
          if(_definingGeometry==null)
            _definingGeometry = ComputeDefiningGeometry();
          return _definingGeometry;
        }
      }
    
      protected Geometry RenderGeometry
      {
        get
        {
          if(_renderGeometry==null)
          {
            Geometry defining = DefiningGeometry;
            if(_transform==Matrix.Identity || defining==Geometry.Empty)
              _renderGeometry = defining;
            else
            {
              Geometry geo = defining.CloneCurrentValue();
              if(object.ReferenceEquals(geo, defining)) geo = defining.Clone();
    
              geo.Transform = new MatrixTransform(
                geo.Transform==null ? _transform : geo.Transform.Value * _transform);
              _renderGeometry = geo;
            }
          }
          return _renderGeometry;
        }
      }
    
      protected override void OnRender(DrawingContext drawingContext)
      {
        drawingContext.DrawGeometry(Fill, PenOrStroke, RenderGeometry);
      }
    
    }
    
    [ContentProperty("Data")]
    public class ViewboxPath : ViewboxShape
    {
      public Geometry Data { get { return (Geometry)GetValue(DataProperty); } set { SetValue(DataProperty, value); } }
      public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(Geometry), typeof(ViewboxPath), new UIPropertyMetadata
      {
        DefaultValue = Geometry.Empty,
        PropertyChangedCallback = OnGeometryChanged,
      });
    
      protected override Geometry DefiningGeometry
      {
        get { return Data ?? Geometry.Empty; }
      }
    }
    
    public class ViewboxLine : ViewboxShape
    {
      public double X1 { get { return (double)GetValue(X1Property); } set { SetValue(X1Property, value); } }
      public double X2 { get { return (double)GetValue(X2Property); } set { SetValue(X2Property, value); } }
      public double Y1 { get { return (double)GetValue(Y1Property); } set { SetValue(Y1Property, value); } }
      public double Y2 { get { return (double)GetValue(Y2Property); } set { SetValue(Y2Property, value); } }
      public static readonly DependencyProperty X1Property = DependencyProperty.Register("X1", typeof(double), typeof(ViewboxLine), new FrameworkPropertyMetadata { PropertyChangedCallback = OnGeometryChanged, AffectsRender = true });
      public static readonly DependencyProperty X2Property = DependencyProperty.Register("X2", typeof(double), typeof(ViewboxLine), new FrameworkPropertyMetadata { PropertyChangedCallback = OnGeometryChanged, AffectsRender = true });
      public static readonly DependencyProperty Y1Property = DependencyProperty.Register("Y1", typeof(double), typeof(ViewboxLine), new FrameworkPropertyMetadata { PropertyChangedCallback = OnGeometryChanged, AffectsRender = true });
      public static readonly DependencyProperty Y2Property = DependencyProperty.Register("Y2", typeof(double), typeof(ViewboxLine), new FrameworkPropertyMetadata { PropertyChangedCallback = OnGeometryChanged, AffectsRender = true });
    
      protected override Geometry ComputeDefiningGeometry()
      {
        return new LineGeometry(new Point(X1, Y1), new Point(X2, Y2));
      }
    }
    
    [ContentProperty("Points")]
    public class ViewboxPolyline : ViewboxShape
    {
      public ViewboxPolyline()
      {
        Points = new PointCollection();
      }
    
      public PointCollection Points { get { return (PointCollection)GetValue(PointsProperty); } set { SetValue(PointsProperty, value); } }
      public static readonly DependencyProperty PointsProperty = DependencyProperty.Register("Points", typeof(PointCollection), typeof(ViewboxPolyline), new FrameworkPropertyMetadata
      {
        PropertyChangedCallback = OnGeometryChanged,
        AffectsRender = true,
      });
    
      public FillRule FillRule { get { return (FillRule)GetValue(FillRuleProperty); } set { SetValue(FillRuleProperty, value); } }
      public static readonly DependencyProperty FillRuleProperty = DependencyProperty.Register("FillRule", typeof(FillRule), typeof(ViewboxPolyline), new FrameworkPropertyMetadata
      {
        DefaultValue = FillRule.EvenOdd,
        PropertyChangedCallback = OnGeometryChanged,
        AffectsRender = true,
      });
    
      public bool CloseFigure { get { return (bool)GetValue(CloseFigureProperty); } set { SetValue(CloseFigureProperty, value); } }
      public static readonly DependencyProperty CloseFigureProperty = DependencyProperty.Register("CloseFigure", typeof(bool), typeof(ViewboxPolyline), new FrameworkPropertyMetadata
      {
        DefaultValue = false,
        PropertyChangedCallback = OnGeometryChanged,
        AffectsRender = true,
      });
    
      protected override Geometry  ComputeDefiningGeometry()
      {
        PointCollection points = Points;
        if(points.Count<2) return Geometry.Empty;
    
        var geometry = new StreamGeometry { FillRule = FillRule };
        using(var context = geometry.Open())
        {
          context.BeginFigure(Points[0], true, CloseFigure);
          context.PolyLineTo(Points.Skip(1).ToList(), true, true);
        }
        return geometry;
      }
    
    }
    
    public class ViewboxPolygon : ViewboxPolyline
    {
      static ViewboxPolygon()
      {
        CloseFigureProperty.OverrideMetadata(typeof(ViewboxPolygon), new FrameworkPropertyMetadata
        {
          DefaultValue = true,
        });
      }
    }
    

    享受!

    推荐文章