Советы по разработке независимого приложения разрешения
Хорошо ли найти измерение рабочей области и установить некоторые свойства в коде, чтобы он мог быть привязан к параметру "Управление запасом" или "Высота/Ширина" в 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 дает много вопросов для решения аналогичной проблемы, но все же я застрял и не могу прийти к выводу, как достичь хорошего независимого от разрешения макета.
Ответы
Ответ 1
Существует два способа решения проблемы в 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>
Ответ 2
Отличный ответ от 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
}
Ответ 3
Небольшая поправка к ответу Фредрика Хедблада:
потому что вы задали "Denominators" 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);