WPF:防止 BorderThickness 缩放

发布于 2024-10-01 01:02:33 字数 3907 浏览 8 评论 0原文

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

我的 for 上有一个 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>

感谢您的帮助!

I'm rather new at WPF and I need some help.

I have a ViewBox on my for and inside the ViewBox an Ellipse and a Border.
When I resize the form I want the Ellipse and the Border to scale automatically (what it does).
But I don't want the BorderThickness to scale. The thickness of the border should stay 3 pixels.

Does anyone know how to accomplish this?

Here is my 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>

Thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

双马尾 2024-10-08 01:02:33

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

    <网格 Horizo​​ntalAlignment="居中" VerticalAlignment="居中">
        
            
            
            
            <行定义高度=“*”/>
        
        
            <列定义宽度=“*”/>
            <列定义宽度=“*”/>
            <列定义宽度=“*”/>
            <列定义宽度=“*”/>
            <列定义宽度=“*”/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="0" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="1" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="2" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="0" Grid.Column="3" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="0" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="1" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="2" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="1" Grid.Column="3" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="0" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="1" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="2" Stretch="UniformToFill"/>
        
        <椭圆填充="#FF7171EB" Stroke="#FF041374" StrokeThickness="3" Grid.Row="2" Grid.Column="3" Stretch="UniformToFill"/>
        
    
    

I would suggest not using a ViewBox. If you don't set the width and height of the ellipse and border, they will size with the window automatically. Instead use a grid to set the relative size of the controls using row height and column width percentages (eg. 0.6*)


Update - Example

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

Update

Hmmm, not seeing a easy way to do exactly what you need. Below is the closest I've been able to come.
The only other options I can see:

  1. Put each ellipse in it's own view box
  2. Size the grid columns & rows in code on window resize

    <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>
    
柒七 2024-10-08 01:02:33

作为防止 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>

As an alternative solution (if somebody will find this question later) to prevent BorderThickness from scaling, you can use a custom version of Viewbox with a special converter.

Usual Viewbox, but now with a read-only Scale property and a static 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

}

Usage example:

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

Shorter version using static resource:

<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>
櫻之舞 2024-10-08 01:02:33

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

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

希望这会有所帮助;)

The solution is to split content and border. Once it's done, set the border's width and height to grid actualWidth and Height with binding from inside the viewbox :

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

Hope this helps ;)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文