Привязка прямоугольника формы WPF

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

<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 of the Rectangle к чему-то еще. К сожалению, Width of Rect не dp, поэтому я использую напрямую привязку к Rect в строковом формате.

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
15 360
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

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

Например. поместите что-то вроде этого в свой тег ПрямоугольникГеометрия:

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

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

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

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

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

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

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

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

Ответ принят как подходящий

Я использую набор классов с именами 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" />

Мои Классы форм окна просмотра используются точно так же, как обычные формы (Polyline, Polygon, Path и Line), за исключением дополнительного параметра Viewbox и того факта, что они по умолчанию равны Stretch = "Fill". Параметр Viewbox указывает в системе координат, используемой для задания формы, область геометрии, которая должна быть растянута с использованием настроек Fill, Uniform или UniformToFill вместо использования Geometry.GetBounds.

Это дает очень точный контроль над растяжением и упрощает точное совмещение отдельных фигур друг с другом.

Вот фактический код для моего Классы форм окна просмотра, включая абстрактный базовый класс 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,
    });
  }
}

Наслаждаться!

Другие вопросы по теме