WPF: предотвращение масштабирования BorderThickness - PullRequest
0 голосов
/ 05 ноября 2010

Я довольно новичок в 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>

Спасибо за любую помощь!

Ответы [ 2 ]

0 голосов
/ 13 января 2017

В качестве альтернативного решения (если кто-то найдет этот вопрос позже) для предотвращения масштабирования 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>
0 голосов
/ 06 ноября 2010

Я бы предложил не использовать 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>
    
...