2010-07-07 12 views
18

E 'una buona pratica per trovare la misura workarea e impostare alcune proprietà nel codice in modo che potesse essere associato a margine di controllo o di altezza/larghezza proprietà in XAML?suggerimenti su come sviluppare un'applicazione indipendente risoluzione

Faccio questo in modo che la mia finestra venga ridimensionata in base alla zona di lavoro disponibile.

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}" /> 

Lo faccio per alcuni contenitori esterni in modo che il layout non sarebbe in disordine risoluzioni inferiori. Attualmente lavoro a 1600x900 risoluzione (96 dpi) in un 20" . La mia domanda è gadget come e non ha la finestra regolare.

Voglio sapere se ci sono alcuni approcci alternativi.

Una ricerca di [wpf] risoluzione] 1 fornisce molte domande per risolvere problemi simili, ma sono ancora bloccato e non riesco a giungere a una conclusione su come ottenere un buon layout indipendente dalla risoluzione

risposta

42

Ci sono due modi per gestire la risoluzione in WPF

Un'opzione è progettare a una risoluzione minima e basta assicurarsi che sia tutto viene agganciato in modo appropriato in modo che gli elementi si ingrandiscano man mano che la risoluzione della finestra aumenta. Questo è il numero di persone che hanno fatto cose in WinForms e funziona ancora decentemente bene per WPF. Probabilmente hai già un concetto su come affrontarlo impostando HorizontalAlignment, VerticalAlignment e margins.

La cosa più nuova, più alla moda da fare in WPF che era quasi impossibile da fare in WinForms è che l'applicazione in realtà esegue solo lo zoom in modo che i controlli diventino più grandi come fa la finestra. Per fare questo, applicherai un ScaleTransform su qualche elemento radice nella tua Finestra e lascerai che WPF si occupi del resto. E `veramente forte.

Per mostrare ciò che questo è come, ecco cosa una finestra sarebbe simile a quando si avvia l'applicazione, renderlo più piccolo, e renderlo più grande: http://i.stack.imgur.com/QeoVK.png

Ecco il code-behind per la piccola applicazione di esempio che ho fatto :

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

E il 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> 

+0

Grande risposta! Molto dettagliato. –

+0

+1 per semplicità, chiarezza e completezza, non sembra giusto. – Arjang

+0

Puoi anche inserire tutto in una Viewbox per ottenere lo stesso effetto. – Surfbutler

12

Ottima risposta di JacobJ, l'ho provato e ha funzionato perfettamente.

Per chiunque sia interessato ho fatto un comportamento annesso che fa la stessa cosa. Ho anche aggiunto l'opzione per specificare i denominatori larghezza/altezza da XAML.Può essere utilizzato come questo

<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

La tua risposta sembra molto interessante, ma sono ancora troppo male in XAML e C# per farlo funzionare. Mi manca l'uso di "inf:" nella parte XAML di esso ... –

+0

@AndreaAntonangeli "inf:" è uno spazio dei nomi dichiarato prima di utilizzare la proprietà AttachedProperty, ad esempio 'xmlns: inf =" clr-namespace: My. App.AttachedProps "'. – jv42

0

piccola correzione alla risposta di Fredrik Hedblad:

perché si è impostato il "denominatori" DependencyProperty nell'elemento griglia:

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

y è necessario chiamare il metodo GetDominator utilizzando la griglia. Invece di:

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

è necessario utilizzare qualcosa di simile:

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); 
Problemi correlati