WPF:阻止BorderThickness扩展

时间:2010-11-05 19:47:23

标签: wpf border scaling

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

我在ViewBox上有一个ViewBox,一个是椭圆和一个边框。 当我调整表单大小时,我希望椭圆和边框自动缩放(它的作用)。 但我不希望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>

谢谢你的帮助!

3 个答案:

答案 0 :(得分:0)

我建议不要使用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. 调整网格列的大小&amp;窗口调整大小的代码行

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

答案 1 :(得分:0)

作为替代解决方案(如果稍后有人会发现此问题)以防止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>

答案 2 :(得分:0)

解决方案是拆分内容和边框。完成后,从视图框内部通过绑定将边框的宽度和高度设置为grid ActualWidth和Height:

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

希望这会有所帮助;)