WPF:防止边界厚度缩放
问题描述:
我在WPF上比较新,我需要一些帮助。WPF:防止边界厚度缩放
我在ViewBox上有一个ViewBox,里面有一个Ellipse和一个边框。 当我调整窗体大小时,我想让椭圆和边框自动缩放(它的功能)。 但我不想让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>
感谢您的帮助!
答
我会建议不要使用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>
更新
嗯,没有看到一个简单的方法来完成你所需要的。下面是我能够来的最近的。 唯一的其他选择,我可以看到:
- 将每个椭圆在它自己的视图框
-
尺寸网格列&行代码的窗口大小调整
<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>
答
作为替代解决方案(如果稍后有人会发现此问题)以防止边框厚度缩放,则可以使用具有特殊转换器的自定义版本的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
}
用例:
<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>
:
使用静态资源较短的版本谢谢你的建议Thomas。 问题是椭圆和边框应拉伸=均匀。只有BorderThickness不应该。如果我将Ellipse和Border放置在一个网格单元格中,我如何指定网格应拉伸=均匀? – Jackerd 2010-11-06 20:38:52
我已经给我的答案添加了一个例子,希望它可以帮助 – Thomas 2010-11-07 03:32:11
谢谢托马斯,我检查了你的例子,但它不完全是我需要的。我设法在我的问题中添加了我的XAML。行为正是我所需要的,但只有BorderThickness不应该伸展。 – Jackerd 2010-11-07 09:10:04