WPF 形状矩形绑定

发布于 2024-07-12 07:35:31 字数 2659 浏览 7 评论 0原文

我正在尝试在 wpf 中制作某种形状,它会根据内容(应该是文本)调整自身大小。 不幸的是,拉伸属性不是正确的,因为我只想调整形状的宽度,并且拉伸该形状的边框(请复制 xamlpad 中的底部示例以供自己查看)。 边框应保持原样,或者至少按统一比例缩放。 我尝试过很多想法。 在网格、堆栈面板或剪切面板等中使用不同的形状切片。 我的下一个方法将是以下方法:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Page.Resources>
<LinearGradientBrush StartPoint="0.0,1" EndPoint="0.0,0" x:Key="brushYellow">
  <LinearGradientBrush.GradientStops>
    <GradientStop Offset="0.000000" Color="#fffef4a6"/>
    <GradientStop Offset="0.175824" Color="#fffef9d6"/>
    <GradientStop Offset="0.800000" Color="#fffef9d6"/>
    <GradientStop Offset="1.000000" Color="#fffef4a6"/>
  </LinearGradientBrush.GradientStops>
</LinearGradientBrush></Page.Resources><Grid>
 <Path Stroke="#fffce90d" StrokeThickness="1" Fill="{StaticResource brushYellow}">
    <Path.Data>
      <CombinedGeometry GeometryCombineMode="Exclude">
        <CombinedGeometry.Geometry1>
          <RectangleGeometry RadiusX="15" RadiusY="15">
            <!--RectangleGeometry.Rect>
              <Binding StringFormat="{}{0 0 {0} 82}" ElementName="Text" Path="Width"/>
            </RectangleGeometry.Rect-->
            <RectangleGeometry.Rect>
              <Rect Width="150" Height="82"/>
            </RectangleGeometry.Rect>
          </RectangleGeometry>
        </CombinedGeometry.Geometry1>
        <CombinedGeometry.Geometry2>
          <PathGeometry>
            <PathGeometry.Figures>
              <PathFigureCollection>
                <PathFigure IsClosed="True" StartPoint="0,15">
                  <PathFigure.Segments>
                    <PathSegmentCollection>
                      <LineSegment Point="17,41" />
                      <LineSegment Point="0,67" />
                    </PathSegmentCollection>
                  </PathFigure.Segments>
                </PathFigure>
              </PathFigureCollection>
            </PathGeometry.Figures>
          </PathGeometry>
        </CombinedGeometry.Geometry2>
      </CombinedGeometry>
    </Path.Data>
  </Path>
  <TextBox Name="Text" Background="Transparent" BorderThickness="0" MinWidth="150" Margin="0"/>
</Grid></Page>

这将在 xamlpad 中开箱即用。 第19行未注释的部分是我真正想要实现的:将矩形的矩形绑定到其他东西。 不幸的是,矩形的宽度不是 dp,这就是为什么我直接使用字符串格式的绑定到矩形本身。

正如生活所预期的那样,这是行不通的(什么都看不见):D 我在这里做错了什么?

I'm trying to make some kind of shape in wpf, which does resize itself to the content (which should be text). Unfortunately, the stretch property isn't the right thing, since I want only the width of the Shape resized and without the borders (pls copy bottom example in xamlpad to see for yourself) of this shape stretched. The borders should stay the way they are, or at least scale in Uniform.
I've tried many ideas. With different slices of the shape in a grid, stackpanel, or with a clipped panel and etc.
My next approach would be the following one:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"><Page.Resources>
<LinearGradientBrush StartPoint="0.0,1" EndPoint="0.0,0" x:Key="brushYellow">
  <LinearGradientBrush.GradientStops>
    <GradientStop Offset="0.000000" Color="#fffef4a6"/>
    <GradientStop Offset="0.175824" Color="#fffef9d6"/>
    <GradientStop Offset="0.800000" Color="#fffef9d6"/>
    <GradientStop Offset="1.000000" Color="#fffef4a6"/>
  </LinearGradientBrush.GradientStops>
</LinearGradientBrush></Page.Resources><Grid>
 <Path Stroke="#fffce90d" StrokeThickness="1" Fill="{StaticResource brushYellow}">
    <Path.Data>
      <CombinedGeometry GeometryCombineMode="Exclude">
        <CombinedGeometry.Geometry1>
          <RectangleGeometry RadiusX="15" RadiusY="15">
            <!--RectangleGeometry.Rect>
              <Binding StringFormat="{}{0 0 {0} 82}" ElementName="Text" Path="Width"/>
            </RectangleGeometry.Rect-->
            <RectangleGeometry.Rect>
              <Rect Width="150" Height="82"/>
            </RectangleGeometry.Rect>
          </RectangleGeometry>
        </CombinedGeometry.Geometry1>
        <CombinedGeometry.Geometry2>
          <PathGeometry>
            <PathGeometry.Figures>
              <PathFigureCollection>
                <PathFigure IsClosed="True" StartPoint="0,15">
                  <PathFigure.Segments>
                    <PathSegmentCollection>
                      <LineSegment Point="17,41" />
                      <LineSegment Point="0,67" />
                    </PathSegmentCollection>
                  </PathFigure.Segments>
                </PathFigure>
              </PathFigureCollection>
            </PathGeometry.Figures>
          </PathGeometry>
        </CombinedGeometry.Geometry2>
      </CombinedGeometry>
    </Path.Data>
  </Path>
  <TextBox Name="Text" Background="Transparent" BorderThickness="0" MinWidth="150" Margin="0"/>
</Grid></Page>

This will work right out of the box in xamlpad. The uncommented part at line 19 is what I really want to achieve: Binding the Rect of the Rectangle to something else. Unfortunately, the Width of Rect isn't dp, that's why I'm using directly a stringformatted Binding to Rect itself.

As expected with life, that doesn't work (nothing is visible) :D
What am I doing wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

醉生梦死 2024-07-19 07:35:31

我使用一组名为 ViewboxPath、ViewboxLine、ViewboxPolyline 等的类,这些类将 Shape 的拉伸语义更改为更易于处理。 我不确定我是否理解你的问题,所以我不知道我的技术是否能解决你的问题。

当我读到它时,要么您想要控制拉伸(该解决方案将提供),要么您希望笔划与图像一起拉伸(山姆的答案将提供)。

无论如何,下面是这些类的代码,这就是您使用它们的方式:

<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形状类就像普通形状一样使用(PolylinePolygon、PathLine),除了额外的 Viewbox 参数,以及它们默认为 Stretch="Fill" 的事实。 Viewbox 参数指定在用于指定形状的坐标系中,应使用 FillUniformUniformToFill 拉伸的几何图形区域> 设置,而不是使用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(Pen), new UIPropertyMetadata
  {
    DefaultValue = null
  });

  // 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,
    });
  }
}

享受吧!

I use a set of classes named ViewboxPath, ViewboxLine, ViewboxPolyline, etc that change the stretch semantics of Shape to be a little more tractable. I'm not sure I understood your question, so I don't know if my technique will solve your problem or not.

As I read it, either you want control over the stretching, which this solution will provide, or you want to have the strokes stretch along with the image, which Sam's answer will provide.

Anyhow, below is the code for these classes and this is how you use them:

<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" />

My Viewbox shape classes are used just like normal shapes (Polyline, Polygon, Path and Line) except for the extra Viewbox parameter, and the fact that they default to Stretch="Fill". The Viewbox parameter specifies, in the coordinate system used to specify the shape, the area of the geometry that should be stretched using Fill, Uniform or UniformToFill settings, instead of using Geometry.GetBounds.

This gives very precise control over the stretching and makes it easy to have separate shapes align accurately with one another.

Here is the actual code for my Viewbox shape classes, including the abstract base class ViewboxShape that contains common functionality:

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(Pen), new UIPropertyMetadata
  {
    DefaultValue = null
  });

  // 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,
    });
  }
}

Enjoy!

荒芜了季节 2024-07-19 07:35:31

您可以尝试使用变换来更改矩形的大小,而不是直接绑定矩形的宽度。 我认为这应该有效。

例如,在您的 RectangleGeometry 标记中放入类似的内容:

<RectangleGeometry.Transform>
    <ScaleTransform ScaleX="{Binding ElementName=textBoxName, Path=Width, 
        Converter=MyScaleWidthConverter}" />
</RectangleGeometry.Transform>

其中 textBoxName 是文本框的名称。 无法让自己称其为“文本”——太混乱了。

您将需要提供一个转换器以确保缩放正确 - 例如,根据示例代码,您可能希望返回类似 Width / 150 的内容。

当在 Visual Studio 设计器中将矩形的宽度设置为“自动”时,我看到稍微奇怪的行为 - 我认为这可能是设计器的怪癖。 一旦转换器在运行时连接起来就应该可以工作。

You could try using a transform to change the size of the rectangle rather than binding the rectangle's width directly. I think this should work.

E.g. put something like this in your RectangleGeometry tag:

<RectangleGeometry.Transform>
    <ScaleTransform ScaleX="{Binding ElementName=textBoxName, Path=Width, 
        Converter=MyScaleWidthConverter}" />
</RectangleGeometry.Transform>

Where textBoxName is the name of your textbox. Couldn't bring myself to call it Text - too confusing.

You will need to supply a converter to ensure the scaling is correct - e.g. You'll probably want to return something like Width / 150 given your sample code.

I am seeing slightly odd behaviour when the rectangle's width is set to Auto in the Visual Studio designer - I think this is probably a designer quirk. Should work once the converter is hooked up at run time.

余生共白头 2024-07-19 07:35:31

使用你的路径作为画笔怎么样? 在下面的代码中,我使用 DrawingBrush 作为 TextBox 本身的背景或作为封闭边框的背景。 只是一个提示...
希望这可以帮助。

<Window x:Class="MarkupWpf.BrushTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="BrushTest" Height="300" Width="300">
    <Window.Resources>
        <LinearGradientBrush StartPoint="0.0,1" EndPoint="0.0,0" x:Key="brushYellow">
            <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0.000000" Color="#fffef4a6"/>
                <GradientStop Offset="0.175824" Color="#fffef9d6"/>
                <GradientStop Offset="0.800000" Color="#fffef9d6"/>
                <GradientStop Offset="1.000000" Color="#fffef4a6"/>
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
        <DrawingBrush x:Key="FabBrush">
            <DrawingBrush.Drawing>
                <GeometryDrawing Brush="{StaticResource brushYellow}">
                    <GeometryDrawing.Pen>
                        <Pen Thickness="1" Brush="#fffce90d" />
                    </GeometryDrawing.Pen>
                    <GeometryDrawing.Geometry>
                        <CombinedGeometry GeometryCombineMode="Exclude">
                            <CombinedGeometry.Geometry1>
                                <RectangleGeometry RadiusX="15" RadiusY="15">
                                    <RectangleGeometry.Rect>
                                        <Rect Width="150" Height="82"/>
                                    </RectangleGeometry.Rect>
                                </RectangleGeometry>
                            </CombinedGeometry.Geometry1>
                            <CombinedGeometry.Geometry2>
                                <PathGeometry>
                                    <PathGeometry.Figures>
                                        <PathFigureCollection>
                                            <PathFigure IsClosed="True" StartPoint="0,15">
                                                <PathFigure.Segments>
                                                    <PathSegmentCollection>
                                                        <LineSegment Point="17,41" />
                                                        <LineSegment Point="0,67" />
                                                    </PathSegmentCollection>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                        </PathFigureCollection>
                                    </PathGeometry.Figures>
                                </PathGeometry>
                            </CombinedGeometry.Geometry2>
                        </CombinedGeometry>
                    </GeometryDrawing.Geometry>
                </GeometryDrawing>
            </DrawingBrush.Drawing>
        </DrawingBrush>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Background="{StaticResource FabBrush}" BorderThickness="0" MinWidth="150" Margin="0"/>
        <Grid Grid.Row="1">
            <Border Background="{StaticResource FabBrush}">
                <TextBox Grid.Row="0" BorderThickness="0" MinWidth="150" Margin="20" />
            </Border>
        </Grid>
    </Grid>
</Window>

What about using your path as Brush? In the following code I use a DrawingBrush as Background for the TextBox itself or as Background for an enclosing Border. Just an hint...
Hope this helps.

<Window x:Class="MarkupWpf.BrushTest"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="BrushTest" Height="300" Width="300">
    <Window.Resources>
        <LinearGradientBrush StartPoint="0.0,1" EndPoint="0.0,0" x:Key="brushYellow">
            <LinearGradientBrush.GradientStops>
                <GradientStop Offset="0.000000" Color="#fffef4a6"/>
                <GradientStop Offset="0.175824" Color="#fffef9d6"/>
                <GradientStop Offset="0.800000" Color="#fffef9d6"/>
                <GradientStop Offset="1.000000" Color="#fffef4a6"/>
            </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
        <DrawingBrush x:Key="FabBrush">
            <DrawingBrush.Drawing>
                <GeometryDrawing Brush="{StaticResource brushYellow}">
                    <GeometryDrawing.Pen>
                        <Pen Thickness="1" Brush="#fffce90d" />
                    </GeometryDrawing.Pen>
                    <GeometryDrawing.Geometry>
                        <CombinedGeometry GeometryCombineMode="Exclude">
                            <CombinedGeometry.Geometry1>
                                <RectangleGeometry RadiusX="15" RadiusY="15">
                                    <RectangleGeometry.Rect>
                                        <Rect Width="150" Height="82"/>
                                    </RectangleGeometry.Rect>
                                </RectangleGeometry>
                            </CombinedGeometry.Geometry1>
                            <CombinedGeometry.Geometry2>
                                <PathGeometry>
                                    <PathGeometry.Figures>
                                        <PathFigureCollection>
                                            <PathFigure IsClosed="True" StartPoint="0,15">
                                                <PathFigure.Segments>
                                                    <PathSegmentCollection>
                                                        <LineSegment Point="17,41" />
                                                        <LineSegment Point="0,67" />
                                                    </PathSegmentCollection>
                                                </PathFigure.Segments>
                                            </PathFigure>
                                        </PathFigureCollection>
                                    </PathGeometry.Figures>
                                </PathGeometry>
                            </CombinedGeometry.Geometry2>
                        </CombinedGeometry>
                    </GeometryDrawing.Geometry>
                </GeometryDrawing>
            </DrawingBrush.Drawing>
        </DrawingBrush>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Background="{StaticResource FabBrush}" BorderThickness="0" MinWidth="150" Margin="0"/>
        <Grid Grid.Row="1">
            <Border Background="{StaticResource FabBrush}">
                <TextBox Grid.Row="0" BorderThickness="0" MinWidth="150" Margin="20" />
            </Border>
        </Grid>
    </Grid>
</Window>
蛮可爱 2024-07-19 07:35:31

我正在做这样的事情。 我想要自定义形状,当我调整窗口大小时自动调整大小。 对于我的解决方案,我从形状派生并覆盖定义几何属性。
对于形状的尺寸,我使用 ActualWidth 和 ActualHeight 属性,因为它们反映了真实的宽度和高度。 我还像这样覆盖了measureOverride()方法,

        protected override Size MeasureOverride(Size constraint)
        {
            return this.DesiredSize;
        }

我使用(就像我之前说的)actualWidth和actualHeight作为最大值在代码中生成形状。
希望这可以帮助

i'm doing something like this. i want custom shapes that automatically re size when i resize the window. for my solution i derived from shape and overridden the definingGeometry property.
For the dimensions of my shape i use the ActualWidth and ActualHeight properties as these reflect the true width and height. i'm also overiding the measureOverride() method like this

        protected override Size MeasureOverride(Size constraint)
        {
            return this.DesiredSize;
        }

I'm generating the shape in code using (like i said before) actualWidth and actualHeight as maximum values.
hope this helps

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文