2009-01-12 17 views
2

Я пытаюсь сделать какую-то форму в wpf, которая сама изменяет размер контента (который должен быть текстом). К сожалению, свойство растяжки не так, потому что я хочу только ширину Shape resized и без границ (пример примера копирования в примере на xamlpad, чтобы увидеть сами) этой формы. Границы должны оставаться такими, какие они есть, или, по крайней мере, в масштабе. Я пробовал много идей. С разными срезами формы в виде сетки, StackPanel, или с подрезанной панелью и т.д. Моим следующим подходом будет следующим:WPF Shape Rectangle Binding

<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 - это то, чего я действительно хочу достичь: привязка Прямоугольника к другому. К сожалению, ширина Rect не является dp, поэтому я использую непосредственно строковое форматирование Binding to Rect.

Как и ожидалось с жизнью, это не работает (ничего не видно): D Что я здесь делаю неправильно?

ответ

1

Я использую набор классов с именем 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 формы классов используются так же, как нормальные формы (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(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!

1

Вы можете попробовать использовать преобразование, чтобы изменить размер прямоугольника, а не напрямую связывать ширину прямоугольника. Я думаю, что это должно сработать.

E.g. положить что-то подобное в вашем RectangleGeometry тег:

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

Где textBoxName это имя вашего текстового поля. Не мог заставить себя называть его текстом - слишком запутанным.

Для обеспечения правильного масштабирования вам необходимо будет установить преобразователь. Вероятно, вы захотите вернуть что-то вроде Ширина/150 с учетом вашего образца кода.

Я вижу немного странное поведение, когда для дизайнера Visual Studio значение ширины прямоугольника установлено в Auto. Я думаю, что это, вероятно, дизайнерский приступ. Должен работать, как только конвертер подключается во время выполнения.

1

Как насчет использования вашего пути в качестве кисти? В следующем коде я использую 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> 
0

Я делаю что-то вроде этого. Я хочу, чтобы пользовательские формы автоматически изменялись при изменении размера окна. для моего решения я получил форму и переопределил свойство defineGeometry. Для размеров моей формы я использую свойства ActualWidth и ActualHeight, поскольку они отражают истинную ширину и высоту. я также overiding метода MeasureOverride(), как этот

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

Я создающая форму в коде, используя (как я говорил ранее) ActualWidth и ActualHeight в качестве максимальных значений. надеюсь, что это поможет

 Смежные вопросы

  • Нет связанных вопросов^_^