2009-01-12 9 views
2

J'essaye de faire une sorte de forme dans wpf, qui se redimensionne au contenu (qui devrait être du texte). Malheureusement, la propriété d'étirement n'est pas la bonne chose, puisque je veux seulement la largeur de la forme redimensionnée et sans les frontières (pls copier l'exemple du bas dans xamlpad pour voir par vous-même) de cette forme étirée. Les frontières doivent rester telles qu'elles sont, ou au moins redimensionner en Uniforme. J'ai essayé beaucoup d'idées. Avec différentes tranches de la forme dans une grille, StackPanel, ou avec un panneau clipsé, etc Mon approche suivante serait la suivante:WPF Forme Rectangle Reliure

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

Cela fonctionne dès la sortie de la boîte à xamlpad. La partie non commentée à la ligne 19 est ce que je veux vraiment réaliser: Relier le rectangle du rectangle à autre chose. Malheureusement, la largeur de Rect n'est pas dp, c'est pourquoi j'utilise directement une liaison formatée en chaîne pour se Rect.

Comme prévu avec la vie, cela ne fonctionne pas (rien n'est visible): D Qu'est-ce que je fais mal ici?

Répondre

1

J'utilise un ensemble de classes nommées ViewboxPath, ViewboxLine, ViewboxPolyline, etc. qui modifient la sémantique d'étirement de Shape pour être un peu plus traitable. Je ne suis pas sûr d'avoir compris votre question, donc je ne sais pas si ma technique résoudra votre problème ou non.Comme je l'ai lu, soit vous voulez contrôler l'étirement, que cette solution va fournir, ou vous voulez que les traits s'étendent avec l'image, ce que la réponse de Sam fournira.

Quoi qu'il en soit, voici le code pour ces classes, ce qui est la façon dont vous les utilisez:

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

Mes cours ViewBox de forme sont utilisé comme des formes normales (Polyline, Polygon, Path et Line) sauf pour le paramètre supplémentaire Viewbox, et le fait qu'ils sont par défaut Stretch="Fill". Le paramètre Viewbox spécifie, dans le système de coordonnées utilisé pour spécifier la forme, la zone de la géométrie qui doit être étendue en utilisant les paramètres Fill, Uniform ou UniformToFill, au lieu d'utiliser Geometry.GetBounds.

Cela permet un contrôle très précis de l'étirement et facilite l'alignement des formes séparées les unes par rapport aux autres.

Voici le code réel pour mes cours ViewBox de forme, y compris la classe de base abstraite ViewboxShape qui contient des fonctionnalités communes:

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

Vous pouvez essayer d'utiliser une transformation pour modifier la taille du rectangle plutôt que de lier directement la largeur du rectangle. Je pense que cela devrait fonctionner.

E.g. mettre quelque chose comme ça dans votre tag RectangleGeometry:

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

textBoxName est le nom de votre zone de texte. Je n'arrivais pas à l'appeler Texte - trop confus.

Vous devrez fournir un convertisseur pour vous assurer que la mise à l'échelle est correcte - par ex. Vous aurez probablement envie de retourner quelque chose comme Largeur/150 compte tenu de votre exemple de code.

Je vois un comportement légèrement étrange lorsque la largeur du rectangle est définie sur Auto dans le concepteur Visual Studio - je pense que c'est probablement un concepteur bizarre. Devrait fonctionner une fois que le convertisseur est branché au moment de l'exécution.

1

Que diriez-vous d'utiliser votre chemin comme pinceau? Dans le code suivant, j'utilise un DrawingBrush comme arrière-plan pour le TextBox lui-même ou comme arrière-plan pour une bordure englobante. Juste un indice ... Espérons que cela aide.

<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

Je fais quelque chose comme ça. Je veux des formes personnalisées qui se redimensionnent automatiquement lorsque je redimensionne la fenêtre. Pour ma solution je dérive de la forme et remplace la propriété definingGeometry. Pour les dimensions de ma forme, j'utilise les propriétés ActualWidth et ActualHeight car elles reflètent la largeur et la hauteur réelles. Je suis également overiding la méthode MeasureOverride() de ce type de

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

je générer en utilisant la forme de code (comme je l'ai déjà dit) actualWidth et actualHeight en tant que valeurs maximales. j'espère que cela aide

Questions connexes