c# - que - Consejos para desarrollar aplicaciones independientes de resolución.
proyectos c# ejemplos (3)
Gran respuesta por JacobJ, lo probé y funcionó perfectamente.
Para cualquier persona interesada hice un comportamiento adjunto que hace lo mismo. También agregué la opción para especificar los denominadores de ancho / alto de XAML. Se puede utilizar así.
<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
}
¿Es una buena práctica encontrar la medida de la zona de trabajo y establecer algunas propiedades en el código para que pueda vincularse a las propiedades de margen o altura / ancho de Control en xaml?
Hago esto para que mi ventana se redimensione de acuerdo con el área de trabajo disponible.
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}" />
Hago esto para algunos contenedores externos para que el diseño no se ensucie en resoluciones más bajas. Actualmente trabajo a 1600x900 res (96 dpi) en 20 ". Mi aplicación es similar a un gadget y no tiene la ventana normal.
Quiero saber si hay algunos enfoques alternativos.
Una búsqueda de [wpf] resolución 1 da muchas preguntas que abordan problemas similares, pero todavía estoy atascado y no puedo llegar a una conclusión sobre cómo lograr un buen diseño independiente de la resolución.
Hay dos formas de lidiar con la resolución en WPF.
Una opción es diseñar a una resolución mínima y solo asegurarse de que todo esté acoplado adecuadamente para que los elementos se vuelvan más grandes a medida que aumenta la resolución de la ventana. Esta es la cantidad de personas que hicieron las cosas en WinForms y aún funcionan bastante bien para WPF. Probablemente ya tenga algún concepto de cómo lidiar con esto al establecer HorizontalAlignment, VerticalAlignment y margins.
Lo más nuevo y moderno que se puede hacer en WPF que era casi imposible de hacer en WinForms es hacer que su aplicación realmente se acerque para que sus controles se hagan más grandes a medida que lo hace su ventana. Para hacer esto, aplicará un ScaleTransform en algún elemento raíz de su ventana y permitirá que WPF se encargue del resto. Es bastante genial.
Para mostrar cómo es esto, a continuación se muestra el aspecto que tendría una ventana al iniciar la aplicación, hacerla más pequeña y hacerla más grande: http://i.stack.imgur.com/QeoVK.png
Aquí está el código para la pequeña aplicación de muestra que hice:
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);
}
}
Y el 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>
Pequeña corrección a la respuesta de Fredrik Hedblad:
porque ha establecido DependencyProperty "Denominators" en el elemento Grid:
<Grid Name="MainGrid"
inf:ScaleToWindowSizeBehavior.Denominators="1000, 700"
<!--...-->
</Grid>
debe llamar al método GetDominator usando la cuadrícula. En lugar de:
private static void CalculateScale(Window window)
{
Size denominators = GetDenominators(window);
Debes usar algo como esto:
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);