我正在尝试在wpf中创建某种形状,它会自动调整内容(应该是文本)。不幸的是,拉伸属性不是正确的,因为我只希望调整Shape的宽度和没有边框(请在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行的未注释部分是我真正想要实现的:将Rectangle的Rect绑定到其他东西。不幸的是,Rect的宽度不是dp,这就是为什么我直接使用字符串格式化Binding到Rect本身。

正如预期的生活,这不起作用(没有任何可见的):D 我在这里做错了什么?

有帮助吗?

解决方案

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

当我读到它时,要么你想要控制拉伸,这个解决方案会提供,或者你想让笔画随着图像一起伸展,Sam的回答将会提供。

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

<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形状类与普通形状(PolylinePolygonPathLine)一样使用,除了额外的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,
    });
  }
}

享受!

其他提示

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

E.g。在 RectangleGeometry 标记中输入类似的内容:

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

其中 textBoxName 是文本框的名称。无法将自己称为文本 - 太混乱了。

您需要提供转换器以确保缩放正确 - 例如您可能希望根据示例代码返回宽度/ 150 之类的内容。

当Visual Studio设计器中矩形的宽度设置为Auto时,我看到了一些奇怪的行为 - 我认为这可能是设计师的怪癖。在运行时连接转换器时应该可以工作。

如何使用路径作为画笔?在下面的代码中,我使用DrawingBrush作为TextBox本身的背景或作为封闭边框的Background。只是一个暗示...... 希望这会有所帮助。

<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>
我正在做这样的事情。我想要在我调整窗口大小时自动调整大小的自定义形状。对于我的解决方案,我从形状派生并覆盖了definitionsGeometry属性。 对于我的形状的尺寸,我使用ActualWidth和ActualHeight属性,因为它们反映了真实的宽度和高度。我也像这样

覆盖了measureOverride()方法
        protected override Size MeasureOverride(Size constraint)
        {
            return this.DesiredSize;
        }

我在代码中使用(如前所述)generateWidth和actualHeight作为最大值生成形状。 希望这有帮助

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top