советы по разработке независимого от разрешения приложения - PullRequest
19 голосов
/ 07 июля 2010

Является ли хорошей практикой найти измерение рабочей области и установить некоторые свойства в коде, чтобы его можно было связать со свойствами поля Control или height / Width в 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);
}

XAML:

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

Я делаю это для некоторых внешних контейнеров, чтобы макет не портился в более низких разрешениях. В настоящее время я работаю с разрешением 1600x900 (96 точек на дюйм) и разрешением 20 дюймов. Мое приложение имеет вид гаджета и не имеет обычного окна.

Я хочу знать, есть ли альтернативные подходы.

Поиск разрешения [wpf] * 1 дает много вопросов, касающихся аналогичной проблемы, но все же я застрял и не могу прийти к выводу, как добиться хорошего независимого от разрешения макета.

Ответы [ 3 ]

48 голосов
/ 15 февраля 2011

Существует два способа решения проблем в WPF.

Один из вариантов - создать минимальное разрешение и просто убедиться, что все закреплено соответствующим образом, чтобы элементы увеличивались по мере увеличения разрешения окна.Именно так много людей делали вещи в WinForms и до сих пор неплохо работает для WPF.Возможно, у вас уже есть представление о том, как справиться с этим, установив HorizontalAlignment, VerticalAlignment и поля.

Более новая и модная вещь в WPF, которую было практически невозможно сделать в WinForms, - это просто сделать ваше приложение простоувеличьте масштаб, чтобы ваши элементы управления стали больше, чем ваше окно.Для этого вы примените 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
        {
            return (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>

16 голосов
/ 25 октября 2013

Отличный ответ от 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
}
0 голосов
/ 17 сентября 2015

Небольшая поправка к ответу Фредрика Хедблада:

, потому что вы установили DependencyProperty "Знаменатели" в элементе Grid:

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

, вы должны вызвать метод GetDominator с использованием сетки,Вместо:

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

вы должны использовать что-то вроде этого:

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

private static void CalculateScale(Window window, FrameworkElement mainElement)
        {
            Size denominators = GetDenominators(mainElement);
...