WPF polyline القيم النسبية النقاط والتمدد لرسم الرسم البياني

StackOverflow https://stackoverflow.com/questions/1709945

  •  19-09-2019
  •  | 
  •  

سؤال

أحاول إنشاء مكون رسومي بسيط جدا يتكون من سلسلة من الأليفة داخل خلية الشبكة نفسها تمثل خطوط الرسم البياني. تتمثل إستراتيجيتي في إلقاء نظرة على جميع النقاط في مجموعتي، حدد MIN و MAX، ثم احسب عدد بين 0 إلى 1 وفقا لذلك واستخدام تمتد = "املأ" لتمتد كل polyline لملء خلية الشبكة. سيكون التأثير المرغوب فيه أن نقطة في 0، 0.5 سيكون رأسيا في وسط الخلية، ولكن في الواقع يمتد polyline عموديا لملء الخلية بأكملها اعتمادا على ما قيمة مين وماكس ص قيمة. على سبيل المثال إذا .5 هو My Max و .7 هو Min Min في Polyline، ثم .5 سيكون واضحا في الجزء العلوي من الخلية و .7 واضح في الأسفل، بدلا من .5 في المركز و .7 7/10 إلى أسفل.

إليك مثال بسيط مع اثنين من بوليلين ونقاط محسوبة بين 0 و 1. ستلاحظ أن Pololline الأحمر موجود مباشرة فوق اللون الأزرق، على الرغم من أن قيم 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>

السبب في أنني أستخدمه من 0 إلى 1 قيم هو أنني أريد عرض وارتفاع خلية الشبكة للتغيير بسهولة، على سبيل المثال عبر شريط تمرير أو شيء ما لضبط ارتفاع الرسم البياني، أو سحب النافذة أوسع لضبط العرض وبعد لذلك حاولت استخدام هذه الإستراتيجية التمدد لتحقيق ذلك بدلا من حساب قيم البكسل W / خارج تمتد.

أي نصيحة حول كيفية تحقيق هذا؟

شكرًا.

هل كانت مفيدة؟

المحلول

كان لدي مشكلة مماثلة لأنني لم أستطع إيجاد طريقة سهلة لتوسيع نطاق أشكال متعددة. انتهى الأمر باستخدام 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 إذا لم تكن بحاجة إلى الرسوم البيانية دائما مقياس بين 0 و 1.

نصائح أخرى

ركضت في هذه المشكلة لفترة طويلة. في ذلك الوقت، وجدت الحل ريبكا المقترح ولكنه غير راض عن ذلك لأنه كان معقدا نسبيا وليس فعالا كما كنت قد أحببت.

أنا حل المشكلة عن طريق الترميز مجموعة من بسيطة فصول شاشات Viewbox هذا العمل تماما مثل المدمج في 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 تستخدم تماما مثل الأشكال العادية (Polyline, Polygon, Path و Line) باستثناء اضافية Viewbox المعلمة، وحقيقة أنها الافتراضية ل Stretch="Fill". وبعد تحدد معلمة Viewbox، في نظام الإحداثيات المستخدم لتحديد الشكل، ومنطقة الهندسة التي يجب امتدادها باستخدام Fill, Uniform أو UniformToFill الإعدادات، بدلا من استخدام Geometry.GetBounds.

هذا يعطي سيطرة دقيقة للغاية على التمدد ويجعل من السهل أن يكون لديك أشكال منفصلة محاذاة بدقة مع بعضها البعض.

هنا هو الرمز الفعلي لبلدي فصول شاشات Viewbox, ، بما في ذلك فئة قاعدة مجردة 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,
    });
  }
}

يتمتع!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top