【问题标题】:WPF: Prevent BorderThickness from scalingWPF:防止 BorderThickness 缩放
【发布时间】:2011-05-05 19:10:33
【问题描述】:

我是 WPF 的新手,需要一些帮助。

我的 for 和内部有一个 ViewBox 一个椭圆和一个边框。 当我调整表单大小时,我希望 Ellipse 和 Border 自动缩放(它的作用)。 但我不希望 BorderThickness 缩放。边框的粗细应保持 3 像素。

有谁知道如何做到这一点?

这是我的 XAML:

<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="StretchTest.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="700" Height="400">

<Grid x:Name="LayoutRoot">
    <Viewbox>
        <Grid Height="300" Width="400" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="0"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="0"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="1"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="1"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="2"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="2"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="3"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="3"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="0"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="0"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="1"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="1"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="2"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="2"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="3"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="3"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="0"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="0"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="1"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="1"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="2"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="2"/>
            <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="3"/>
            <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="3"/>
        </Grid>
    </Viewbox>
</Grid>

感谢您的帮助!

【问题讨论】:

    标签: wpf border scaling


    【解决方案1】:

    解决方法是拆分内容和边框。完成后,将边框的宽度和高度设置为网格实际宽度和高度,并从视图框内绑定:

      <Grid>
        <Viewbox>
          <Border
            Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth}"
            Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=ActualHeight}"
            Background="{StaticResource TcsFilledColor}"
            BorderBrush="{StaticResource TcsBaseColor}"
            BorderThickness="10">
          </Border>
        </Viewbox>
        <Viewbox>
          <Label
            Padding="10"
            Style="{StaticResource BaseLabelStyle}">
            <TextBlock
              Style="{StaticResource BaseTextBlockStyle}"
              Text="{Binding SomeText, FallbackValue=SomeText}" />
          </Label>
        </Viewbox>
      </Grid>
    

    希望这会有所帮助;)

    【讨论】:

      【解决方案2】:

      作为防止 BorderThickness 缩放的替代解决方案(如果以后有人会发现这个问题),您可以使用带有特殊转换器的自定义版本的 Viewbox。

      通常的 Viewbox,但现在具有只读的 Scale 属性和静态的 ThicknessConverter

      public class BorderyViewbox : Decorator {
          public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(nameof(Stretch),
                  typeof(Stretch), typeof(BorderyViewbox), new FrameworkPropertyMetadata(Stretch.Uniform, FrameworkPropertyMetadataOptions.AffectsMeasure));
      
          public Stretch Stretch {
              get { return (Stretch)GetValue(StretchProperty); }
              set { SetValue(StretchProperty, value); }
          }
      
          public static readonly DependencyProperty StretchDirectionProperty = DependencyProperty.Register(nameof(StretchDirection),
                  typeof(StretchDirection), typeof(BorderyViewbox), new FrameworkPropertyMetadata(StretchDirection.Both, FrameworkPropertyMetadataOptions.AffectsMeasure));
      
          public StretchDirection StretchDirection {
              get { return (StretchDirection)GetValue(StretchDirectionProperty); }
              set { SetValue(StretchDirectionProperty, value); }
          }
      
          public static readonly DependencyPropertyKey ScalePropertyKey = DependencyProperty.RegisterReadOnly(nameof(Scale), typeof(Size),
                  typeof(BorderyViewbox), new PropertyMetadata(default(Size)));
      
          public static readonly DependencyProperty ScaleProperty = ScalePropertyKey.DependencyProperty;
      
          public Size Scale => (Size)GetValue(ScaleProperty);
      
          private ContainerVisual InternalVisual {
              get {
                  if (_internalVisual == null) {
                      _internalVisual = new ContainerVisual();
                      AddVisualChild(_internalVisual);
                  }
                  return _internalVisual;
              }
          }
      
          private UIElement InternalChild {
              get {
                  var vc = InternalVisual.Children;
                  return vc.Count != 0 ? vc[0] as UIElement : null;
              }
              set {
                  var vc = InternalVisual.Children;
                  if (vc.Count != 0) vc.Clear();
                  vc.Add(value);
              }
          }
      
          private Transform InternalTransform {
              get { return InternalVisual.Transform; }
              set { InternalVisual.Transform = value; }
          }
      
          public override UIElement Child {
              get { return InternalChild; }
              set {
                  var old = InternalChild;
                  if (!ReferenceEquals(old, value)) {
                      RemoveLogicalChild(old);
      
                      if (value != null) {
                          AddLogicalChild(value);
                      }
      
                      InternalChild = value;
                      InvalidateMeasure();
                  }
              }
          }
      
          protected override int VisualChildrenCount => 1;
      
          protected override Visual GetVisualChild(int index) {
              if (index != 0) throw new ArgumentOutOfRangeException(nameof(index));
              return InternalVisual;
          }
      
          protected override IEnumerator LogicalChildren => InternalChild == null ? EmptyEnumerator.Instance : new SingleChildEnumerator(InternalChild);
      
          protected override Size MeasureOverride(Size constraint) {
              var child = InternalChild;
              var parentSize = new Size();
      
              if (child != null) {
                  var infinteConstraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
      
                  child.Measure(infinteConstraint);
                  var childSize = child.DesiredSize;
      
                  var scale = ComputeScaleFactor(constraint, childSize, Stretch, StretchDirection);
                  SetValue(ScalePropertyKey, scale);
      
                  parentSize.Width = scale.Width * childSize.Width;
                  parentSize.Height = scale.Height * childSize.Height;
              }
      
              return parentSize;
      
          }
      
          protected override Size ArrangeOverride(Size arrangeSize) {
              var child = InternalChild;
              if (child != null) {
                  var childSize = child.DesiredSize;
      
                  var scale = ComputeScaleFactor(arrangeSize, childSize, Stretch, StretchDirection);
                  SetValue(ScalePropertyKey, scale);
      
                  InternalTransform = new ScaleTransform(scale.Width, scale.Height);
                  child.Arrange(new Rect(new Point(), child.DesiredSize));
      
                  arrangeSize.Width = scale.Width * childSize.Width;
                  arrangeSize.Height = scale.Height * childSize.Height;
              }
              return arrangeSize;
          }
      
          private static Size ComputeScaleFactor(Size availableSize, Size contentSize, Stretch stretch, StretchDirection stretchDirection) {
              var scaleX = 1.0;
              var scaleY = 1.0;
      
              var isConstrainedWidth = !double.IsPositiveInfinity(availableSize.Width);
              var isConstrainedHeight = !double.IsPositiveInfinity(availableSize.Height);
      
              if ((stretch == Stretch.Uniform || stretch == Stretch.UniformToFill || stretch == Stretch.Fill)
                      && (isConstrainedWidth || isConstrainedHeight)) {
                  scaleX = Equals(0d, contentSize.Width) ? 0.0 : availableSize.Width / contentSize.Width;
                  scaleY = Equals(0d, contentSize.Height) ? 0.0 : availableSize.Height / contentSize.Height;
      
                  if (!isConstrainedWidth) {
                      scaleX = scaleY;
                  } else if (!isConstrainedHeight) {
                      scaleY = scaleX;
                  } else {
                      switch (stretch) {
                          case Stretch.Uniform:
                              var minscale = scaleX < scaleY ? scaleX : scaleY;
                              scaleX = scaleY = minscale;
                              break;
      
                          case Stretch.UniformToFill:
                              var maxscale = scaleX > scaleY ? scaleX : scaleY;
                              scaleX = scaleY = maxscale;
                              break;
      
                          case Stretch.Fill:
                              break;
                      }
                  }
      
                  switch (stretchDirection) {
                      case StretchDirection.UpOnly:
                          if (scaleX < 1.0) scaleX = 1.0;
                          if (scaleY < 1.0) scaleY = 1.0;
                          break;
      
                      case StretchDirection.DownOnly:
                          if (scaleX > 1.0) scaleX = 1.0;
                          if (scaleY > 1.0) scaleY = 1.0;
                          break;
      
                      case StretchDirection.Both:
                          break;
                  }
              }
      
              return new Size(scaleX, scaleY);
          }
      
          private ContainerVisual _internalVisual;
      
          #region Converter-related stuff
          private class ConverterInner : IValueConverter {
              public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
                  if (!(value is Size)) return parameter;
      
                  var scale = (Size)value;
                  var thickness = parameter as Thickness? ?? new Thickness(1d);
      
                  return new Thickness(thickness.Left / scale.Width, thickness.Top / scale.Height,
                          thickness.Right / scale.Width, thickness.Bottom / scale.Height);
              }
      
              public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
                  if (!(value is Size)) return parameter;
      
                  var scale = (Size)value;
                  var thickness = parameter as Thickness? ?? new Thickness(1d);
      
                  return new Thickness(thickness.Left * scale.Width, thickness.Top * scale.Height,
                          thickness.Right * scale.Width, thickness.Bottom * scale.Height);
              }
          }
      
          public static IValueConverter ThicknessConverter { get; } = new ConverterInner();
          #endregion
      
      }
      

      使用示例:

      <BorderyViewbox x:Name="Viewbox">
          <Border BorderBrush="Red" SnapsToDevicePixels="True">
              <Border.BorderThickness>
                  <Binding Path="Scale" ElementName="Viewbox" Converter="{x:Static BorderyViewbox.ThicknessConverter}">
                      <Binding.ConverterParameter>
                          <Thickness>3</Thickness>
                      </Binding.ConverterParameter>
                  </Binding>
              </Border.BorderThickness>
          </Border>
      </BorderyViewbox>
      

      使用静态资源的较短版本:

      <Thickness x:Key="BorderThickness">3</Thickness>
      
      <BorderyViewbox x:Name="Viewbox">
          <Border BorderBrush="Red" SnapsToDevicePixels="True"
                  BorderThickness="{Binding Path=Scale, ElementName=Viewbox, 
                          Converter={x:Static BorderyViewbox.ThicknessConverter},
                          ConverterParameter={StaticResource BorderThickness}}" />
      </BorderyViewbox>
      

      【讨论】:

        【解决方案3】:

        我建议不要使用 ViewBox。如果您不设置椭圆和边框的宽度和高度,它们将自动随窗口调整大小。而是使用网格来设置控件的相对大小,使用行高和列宽百分比(例如 0.6*)


        更新 - 示例

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="0.4*" />
                <ColumnDefinition Width="0.6*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
        
            <Border Grid.Row="1" Name="border1" BorderBrush="Black" BorderThickness="1" Padding="2">
                <Ellipse Name="ellipse1" Stroke="Black" Grid.Row="1" Stretch="UniformToFill" />
            </Border>
        </Grid>
        

        更新

        嗯,没有找到一种简单的方法来完全满足您的需求。下面是我能来的最接近的。 我能看到的唯一其他选项:

        1. 将每个椭圆放在自己的视图框中
        2. 调整窗口大小时在代码中调整网格列和行的大小

          <Grid HorizontalAlignment="Center" VerticalAlignment="Center">
              <Grid.RowDefinitions>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="*"/>
              </Grid.RowDefinitions>
              <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="*"/>
              </Grid.ColumnDefinitions>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="0" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="0"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="1" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="1"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="2" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="2"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="3" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="0" Grid.Column="3"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="0" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="0"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="1" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="1"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="2" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="2"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="3" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="1" Grid.Column="3"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="0" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="0"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="1" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="1"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="2" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="2"/>
              <Ellipse Fill="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="3" Stretch="UniformToFill"/>
              <Border BorderBrush="Red" BorderThickness="3" CornerRadius="8" Grid.Row="2" Grid.Column="3"/>
          </Grid>
          

        【讨论】:

        • 感谢您的建议,托马斯。问题是椭圆和边框应该拉伸=统一。只有 BorderThickness 不应该。如果我将椭圆和边框放在网格单元格中,我如何指定网格应该拉伸 = 均匀?
        • 我在回答中添加了一个示例,希望对您有所帮助
        • 谢谢托马斯,我检查了你的例子,但这并不是我所需要的。我设法在我的问题中添加了我的 XAML。这种行为正是我所需要的,但只有 BorderThickness 不应该拉伸。
        • 嗨,托马斯。我将使用您的第二个选项来调整代码中的网格大小。这样椭圆的边界不会伸展,但我可以忍受。非常感谢!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-09
        • 1970-01-01
        • 2019-03-30
        • 1970-01-01
        相关资源
        最近更新 更多