22

找到工作区测量并在代码中设置一些属性以便它可以绑定到控件的边距或 xaml 中的高度/宽度属性是一种好习惯吗?

我这样做是为了让我的窗口根据可用的工作区调整大小。

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);
}

xml:

<Grid Margin="{Binding OuterGridMargin}" />

我对一些外部容器执行此操作,以便布局不会在较低分辨率下被弄乱。目前我在 20" 中以 1600x900 res(96 dpi) 工作。我的应用程序类似于小工具,没有常规窗口。

我想知道是否有一些替代方法。

搜索 [wpf] resolution] 1给出了很多解决类似问题的问题,但我仍然被卡住,无法得出如何实现良好的独立于分辨率的布局的结论。

4

4 回答 4

61

在 WPF 中有两种处理分辨率的方法。

一种选择是设计最小分辨率,并确保所有内容都正确停靠,以便元素随着窗口分辨率的增大而变大。这就是有多少人在 WinForms 中做的事情,并且仍然适用于 WPF。您可能已经对如何通过设置 Horizo​​ntalAlignment、VerticalAlignment 和边距来处理这个问题有了一些概念。

在 WPF 中几乎不可能在 WinForms 中做的更新、更时尚的事情是让您的应用程序实际上只是放大,这样您的控件就会像 Window 一样变得更大。为此,您将在 Window 中的某个根元素上应用 ScaleTransform,并让 WPF 处理其余部分。这真的很酷。

为了展示这是什么情况,下面是启动应用程序时窗口的外观,将其变小和变大:http: //i.stack.imgur.com/QeoVK.png

这是我制作的小示例应用程序的代码隐藏:

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>
于 2011-02-15T05:15:09.000 回答
18

JacobJ 的回答很好,我试过了,效果很好。

对于任何有兴趣的人,我做了一个附加的行为,它做同样的事情。我还添加了从 XAML 指定宽度/高度分母的选项。可以这样使用

<Grid Name="MainGrid"
      inf:ScaleToWindowSizeBehavior.Denominators="1000, 700"
      inf:ScaleToWindowSizeBehavior.ParentWindow="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
    <!--...-->
</Grid>

ScaleToWindowSizeBehavior

public static class ScaleToWindowSizeBehavior
{
    #region ParentWindow

    public static readonly DependencyProperty ParentWindowProperty =
        DependencyProperty.RegisterAttached("ParentWindow",
                                             typeof(Window),
                                             typeof(ScaleToWindowSizeBehavior),
                                             new FrameworkPropertyMetadata(null, OnParentWindowChanged));

    public static void SetParentWindow(FrameworkElement element, Window value)
    {
        element.SetValue(ParentWindowProperty, value);
    }

    public static Window GetParentWindow(FrameworkElement element)
    {
        return (Window)element.GetValue(ParentWindowProperty);
    }

    private static void OnParentWindowChanged(DependencyObject target,
                                              DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement mainElement = target as FrameworkElement;
        Window window = e.NewValue as Window;

        ScaleTransform scaleTransform = new ScaleTransform();
        scaleTransform.CenterX = 0;
        scaleTransform.CenterY= 0;
        Binding scaleValueBinding = new Binding
        {
            Source = window,
            Path = new PropertyPath(ScaleValueProperty)
        };
        BindingOperations.SetBinding(scaleTransform, ScaleTransform.ScaleXProperty, scaleValueBinding);
        BindingOperations.SetBinding(scaleTransform, ScaleTransform.ScaleYProperty, scaleValueBinding);
        mainElement.LayoutTransform = scaleTransform;
        mainElement.SizeChanged += mainElement_SizeChanged;
    }

    #endregion // ParentWindow

    #region ScaleValue

    public static readonly DependencyProperty ScaleValueProperty =
        DependencyProperty.RegisterAttached("ScaleValue",
                                            typeof(double),
                                            typeof(ScaleToWindowSizeBehavior),
                                            new UIPropertyMetadata(1.0, OnScaleValueChanged, OnCoerceScaleValue));

    public static double GetScaleValue(DependencyObject target)
    {
        return (double)target.GetValue(ScaleValueProperty);
    }
    public static void SetScaleValue(DependencyObject target, double value)
    {
        target.SetValue(ScaleValueProperty, value);
    }

    private static void OnScaleValueChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
    }

    private static object OnCoerceScaleValue(DependencyObject d, object baseValue)
    {
        if (baseValue is double)
        {
            double value = (double)baseValue;
            if (double.IsNaN(value))
            {
                return 1.0f;
            }
            value = Math.Max(0.1, value);
            return value;
        }
        return 1.0f;
    }

    private static void mainElement_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        FrameworkElement mainElement = sender as FrameworkElement;
        Window window = GetParentWindow(mainElement);
        CalculateScale(window);
    }

    private static void CalculateScale(Window window)
    {
        Size denominators = GetDenominators(window);
        double xScale = window.ActualWidth / denominators.Width;
        double yScale = window.ActualHeight / denominators.Height;
        double value = Math.Min(xScale, yScale);
        SetScaleValue(window, value);
    }

    #endregion // ScaleValue

    #region Denominators

    public static readonly DependencyProperty DenominatorsProperty =
        DependencyProperty.RegisterAttached("Denominators",
                                            typeof(Size),
                                            typeof(ScaleToWindowSizeBehavior),
                                            new UIPropertyMetadata(new Size(1000.0, 700.0)));

    public static Size GetDenominators(DependencyObject target)
    {
        return (Size)target.GetValue(DenominatorsProperty);
    }
    public static void SetDenominators(DependencyObject target, Size value)
    {
        target.SetValue(DenominatorsProperty, value);
    }

    #endregion // Denominators
}
于 2013-10-25T01:38:37.953 回答
0

对 Fredrik Hedblad 答案的小修正:

因为您在 Grid 元素中设置了 DependencyProperty“分母”:

<Grid Name="MainGrid"
      inf:ScaleToWindowSizeBehavior.Denominators="1000, 700"
    <!--...-->
</Grid>

您必须使用网格调用 GetDominator 方法。代替:

private static void CalculateScale(Window window)
{
    var denominators = GetDenominators(window);
}

你必须使用这样的东西:

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);
}
于 2015-09-17T06:46:50.323 回答
-1

按照 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 中设置 this.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();
    }
}
于 2020-06-27T07:52:30.210 回答