C# 开发分辨率无关应用程序的技巧
在代码中查找工作区测量值并设置一些属性,以便可以将其绑定到xaml中控件的边距或高度/宽度属性,这是一种好的做法吗 我这样做是为了根据可用的工作区域调整窗口大小C# 开发分辨率无关应用程序的技巧,c#,wpf,resolution-independence,C#,Wpf,Resolution Independence,在代码中查找工作区测量值并设置一些属性,以便可以将其绑定到xaml中控件的边距或高度/宽度属性,这是一种好的做法吗 我这样做是为了根据可用的工作区域调整窗口大小 const int w = SystemParameters.WorkArea.Width; const int h = SystemParameters.WorkArea.Height; public Thickness OuterGridMargin { get; } MainViewModel() { OuterGri
const int w = SystemParameters.WorkArea.Width;
const int h = SystemParameters.WorkArea.Height;
public Thickness OuterGridMargin { get; }
MainViewModel()
{
OuterGridMargin = new Thickness(w/5,h/6,w/5,h/4);
}
xaml:
我对一些外部容器这样做是为了在较低的分辨率下布局不会混乱。目前我在一个20英寸的屏幕上以1600x900分辨率(96 dpi)工作,我的应用程序类似于小工具,没有常规窗口
我想知道是否有其他方法
搜索[wpf]resolution]会提出许多问题来解决类似的问题,但我仍然被卡住了,无法得出如何实现良好的分辨率独立布局的结论。在wpf中有两种处理分辨率的方法 一种选择是设计到最低分辨率,只需确保所有内容都正确对接,以便元素随着窗口分辨率的增大而变大。这就是有多少人在WinForms中做了一些事情,但仍然可以很好地用于WPF。您可能已经有了一些如何通过设置HorizontalAli来处理这一问题的概念垂直对齐、垂直对齐和边缘 在WPF中要做的更新、更时尚的事情在WinForms中几乎是不可能做到的,就是让你的应用程序实际上只是放大,这样你的控件就会随着你的窗口变大。要做到这一点,你需要在你的窗口中的某个根元素上应用ScaleTransform,让WPF来处理其余的事情。这真的很酷 为了说明这是什么样子,下面是启动应用程序时窗口的外观,使其变小,变大: 以下是我制作的小样本应用程序的代码:
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
#region ScaleValue Depdency Property
public static readonly DependencyProperty ScaleValueProperty = DependencyProperty.Register("ScaleValue", typeof(double), typeof(MainWindow), new UIPropertyMetadata(1.0, new PropertyChangedCallback(OnScaleValueChanged), new CoerceValueCallback(OnCoerceScaleValue)));
private static object OnCoerceScaleValue(DependencyObject o, object value)
{
MainWindow mainWindow = o as MainWindow;
if (mainWindow != null)
return mainWindow.OnCoerceScaleValue((double)value);
else return value;
}
private static void OnScaleValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
MainWindow mainWindow = o as MainWindow;
if (mainWindow != null)
mainWindow.OnScaleValueChanged((double)e.OldValue, (double)e.NewValue);
}
protected virtual double OnCoerceScaleValue(double value)
{
if (double.IsNaN(value))
return 1.0f;
value = Math.Max(0.1, value);
return value;
}
protected virtual void OnScaleValueChanged(double oldValue, double newValue) { }
public double ScaleValue
{
get => (double)GetValue(ScaleValueProperty);
set => SetValue(ScaleValueProperty, value);
}
#endregion
private void MainGrid_SizeChanged(object sender, EventArgs e) => CalculateScale();
private void CalculateScale()
{
double yScale = ActualHeight / 250f;
double xScale = ActualWidth / 200f;
double value = Math.Min(xScale, yScale);
ScaleValue = (double)OnCoerceScaleValue(myMainWindow, value);
}
}
以及XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Name="myMainWindow"
Width="200" Height="250">
<Grid Name="MainGrid" SizeChanged="MainGrid_SizeChanged">
<Grid.LayoutTransform>
<ScaleTransform x:Name="ApplicationScaleTransform"
CenterX="0"
CenterY="0"
ScaleX="{Binding ElementName=myMainWindow, Path=ScaleValue}"
ScaleY="{Binding ElementName=myMainWindow, Path=ScaleValue}" />
</Grid.LayoutTransform>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Height="150">
<TextBlock FontSize="20" Text="Hello World" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center"/>
<Button Content="Button" VerticalAlignment="Bottom" HorizontalAlignment="Center"/>
</Grid>
</Grid>
雅各布的回答很好,我试过了,效果很好 对于任何感兴趣的人,我做了一个附加的行为,做了同样的事情。我还添加了从XAML指定宽度/高度分母的选项。它可以这样使用
<Grid Name="MainGrid"
inf:ScaleToWindowSizeBehavior.Denominators="1000, 700"
inf:ScaleToWindowSizeBehavior.ParentWindow="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
<!--...-->
</Grid>
Fredrik Hedblad回答的小更正: 因为您已经在网格元素中设置了DependencyProperty“Denominators”:
<Grid Name="MainGrid"
inf:ScaleToWindowSizeBehavior.Denominators="1000, 700"
<!--...-->
</Grid>
您必须使用以下内容:
private static void mainElement_SizeChanged(object sender, SizeChangedEventArgs e)
{
var mainElement = sender as FrameworkElement;
var window = GetParentWindow(mainElement);
CalculateScale(window, mainElement);
}
private static void CalculateScale(Window window, FrameworkElement mainElement)
{
var denominators = GetDenominators(mainElement);
}
按照JacobJ先生的做法,我做了自己的衍生产品。 我做了一个界面,在上面我们可以更改基址
public interface IResolutionDecorator
{
double ActualWidth { get; }
double ActualHeight { get; }
double ResolutionHeight { get; set; }
double ResolutionWidth { get; set; }
object CurentContent { get; }
}
对于这个接口,我有一个扩展
#region IResolutionDecorator
private static double OnCoerceScaleValue(double value)
{
return double.IsNaN(value) ? 1d : Math.Max(0.1, value);
}
public static void UpdateScale(this IResolutionDecorator source)
{
if (source.CurentContent is Visual visual)
{
double yScale = source.ActualHeight / source.ResolutionHeight;
double xScale = source.ActualWidth / source.ResolutionWidth;
double value = Math.Min(xScale, yScale);
double ScaleValue = (double)OnCoerceScaleValue(value);
visual.SetValue(Grid.LayoutTransformProperty, new ScaleTransform(ScaleValue, ScaleValue, 0, 0));
}
}
#endregion
现在我们只需要添加主窗口中缺少的参数,并在主网格中的事件sizeChanged中设置此.UpdateScale()
public partial class MainWindow : Window, IResolutionDecorator
{
public MainWindow()
{
InitializeComponent();
this.Height = ResolutionHeight;
this.Width = ResolutionWidth;
}
#region IResolutionDecorator
public object CurentContent { get{ return this.Content; } }
public double ResolutionHeight { get; set; } = 400d;
public double ResolutionWidth { get; set; } = 800d;
#endregion
private void MainGrid_SizeChanged(object sender, EventArgs e)
{
this.UpdateScale();
}
}
+1为简单、清晰和完整起见,这似乎不公平。您也可以将所有内容都放在Viewbox中以实现相同的效果。不,Viewbox没有相同的效果,并且有其他限制。即,如何指定“本机”选项“解决方案是应用程序的起点。视图框只是缩放以填充边界。此机制还允许您根据需要设置最小和最大缩放距离。也许你只想放大,或者你只想缩小;在我看来这是不对的。OnCerceScaleValue将第一个参数强制转换到主窗口,因此传入网格将简单地返回该值,而不会强制执行该值。我认为您需要调用oncercascalevalue(主窗口,value);相反。请注意,布局转换不适用于分离的树,如工具提示和上下文菜单。您的答案看起来很有趣,但我在XAML和C#方面仍然太差,无法让它工作。我缺少了“inf:”在它的XAML部分的用法…@AndreaAntonangeli“inf:”是在使用AttachedProperty之前声明的名称空间,即
xmlns:inf=“clr namespace:My.App.attachedrops”
。这实际上非常有效,而且可能会将其限制在视图的某些部分!干得好+1还不够。请在发布时删除额外的选项卡,这不需要很多努力
public interface IResolutionDecorator
{
double ActualWidth { get; }
double ActualHeight { get; }
double ResolutionHeight { get; set; }
double ResolutionWidth { get; set; }
object CurentContent { get; }
}
#region IResolutionDecorator
private static double OnCoerceScaleValue(double value)
{
return double.IsNaN(value) ? 1d : Math.Max(0.1, value);
}
public static void UpdateScale(this IResolutionDecorator source)
{
if (source.CurentContent is Visual visual)
{
double yScale = source.ActualHeight / source.ResolutionHeight;
double xScale = source.ActualWidth / source.ResolutionWidth;
double value = Math.Min(xScale, yScale);
double ScaleValue = (double)OnCoerceScaleValue(value);
visual.SetValue(Grid.LayoutTransformProperty, new ScaleTransform(ScaleValue, ScaleValue, 0, 0));
}
}
#endregion
public partial class MainWindow : Window, IResolutionDecorator
{
public MainWindow()
{
InitializeComponent();
this.Height = ResolutionHeight;
this.Width = ResolutionWidth;
}
#region IResolutionDecorator
public object CurentContent { get{ return this.Content; } }
public double ResolutionHeight { get; set; } = 400d;
public double ResolutionWidth { get; set; } = 800d;
#endregion
private void MainGrid_SizeChanged(object sender, EventArgs e)
{
this.UpdateScale();
}
}