【问题标题】:WPF Shape Rectangle BindingWPF 形状矩形绑定
【发布时间】:2009-01-12 08:50:07
【问题描述】:

我正在尝试在 wpf 中制作某种形状,它会根据内容(应该是文本)调整自身大小。不幸的是,stretch 属性不是正确的,因为我只想要调整形状的宽度并且没有 拉伸这个形状的边框(请复制 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 我在这里做错了什么?

【问题讨论】:

    标签: wpf binding shape rect


    【解决方案1】:

    我使用了一组名为 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,
        });
      }
    }
    

    享受吧!

    【讨论】:

      【解决方案2】:

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

      例如在你的 RectangleGeometry 标签中加入这样的东西:

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

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

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

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

      【讨论】:

        【解决方案3】:

        使用你的路径作为画笔怎么样?在下面的代码中,我使用 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>
        

        【讨论】:

          【解决方案4】:

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

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-04-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多