.NET WPF Ricorda le dimensioni della finestra tra le sessioni


93

Fondamentalmente, quando l'utente ridimensiona la finestra della mia applicazione, voglio che l'applicazione abbia le stesse dimensioni quando l'applicazione viene riaperta.

All'inizio ho pensato di gestire l'evento SizeChanged e di salvare Altezza e Larghezza, ma penso che debba esserci una soluzione più semplice.

Problema piuttosto semplice, ma non riesco a trovare una soluzione facile.


2
Tieni presente che se stai ripristinando sia la dimensione che la posizione (come fanno la maggior parte degli esempi di codice di seguito), ti consigliamo di gestire il caso limite in cui qualcuno ha scollegato il monitor su cui era stata presentata l'ultima volta, per evitare di presentare il tuo finestra fuori dallo schermo.
Omer Raviv

@OmerRaviv Hai trovato un esempio che prenda in considerazione il caso limite?
Andrew Truckle

Ho troppa meno reputazione per aggiungere un commento, quindi ho creato questo nuovo proprietario. Uso la stessa soluzione di Lance Cleveland inclusa l'impostazione di RobJohnson , ma non funziona se la usi per le finestre secondarie e vuoi aprirne più allo stesso tempo ...
AelanY

Risposte:


121

Salvare i valori nel file user.config.

Dovrai creare il valore nel file delle impostazioni: dovrebbe essere nella cartella Proprietà. Crea cinque valori:

  • Top di tipo double
  • Left di tipo double
  • Height di tipo double
  • Width di tipo double
  • Maximizeddi tipo bool- per contenere se la finestra è ingrandita o meno. Se si desidera memorizzare più informazioni, sarà necessario un tipo o una struttura diversa.

Inizializza i primi due su 0 e i secondi due sulla dimensione predefinita dell'applicazione e l'ultimo su false.

Crea un gestore di eventi Window_OnSourceInitialized e aggiungi quanto segue:

this.Top = Properties.Settings.Default.Top;
this.Left = Properties.Settings.Default.Left;
this.Height = Properties.Settings.Default.Height;
this.Width = Properties.Settings.Default.Width;
// Very quick and dirty - but it does the job
if (Properties.Settings.Default.Maximized)
{
    WindowState = WindowState.Maximized;
}

NOTA: Il posizionamento della finestra impostato deve andare nell'evento inizializzato all'origine della finestra e non nel costruttore, altrimenti se hai la finestra ingrandita su un secondo monitor, verrà sempre riavviata ingrandita sul monitor principale e non sarai in grado per accedervi.

Crea un gestore di eventi Window_Closing e aggiungi quanto segue:

if (WindowState == WindowState.Maximized)
{
    // Use the RestoreBounds as the current values will be 0, 0 and the size of the screen
    Properties.Settings.Default.Top = RestoreBounds.Top;
    Properties.Settings.Default.Left = RestoreBounds.Left;
    Properties.Settings.Default.Height = RestoreBounds.Height;
    Properties.Settings.Default.Width = RestoreBounds.Width;
    Properties.Settings.Default.Maximized = true;
}
else
{
    Properties.Settings.Default.Top = this.Top;
    Properties.Settings.Default.Left = this.Left;
    Properties.Settings.Default.Height = this.Height;
    Properties.Settings.Default.Width = this.Width;
    Properties.Settings.Default.Maximized = false;
}

Properties.Settings.Default.Save();

Questo fallirà se l'utente riduce l'area di visualizzazione, scollegando uno schermo o modificando la risoluzione dello schermo, mentre l'applicazione è chiusa, quindi è necessario aggiungere un controllo che la posizione e le dimensioni desiderate siano ancora valide prima di applicare i valori.


5
In realtà, le impostazioni con ambito "Utente" non vengono salvate nel file app.config in Programmi, ma in un file user.config nella directory dei dati dell'applicazione dell'utente. Quindi non è un problema ...
Thomas Levesque

7
In realtà puoi aggiungere "WindowState" alle impostazioni. Seleziona tipo -> sfoglia -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka

2
FWIW, lo faccio anche dal gestore di dimensioni modificate, in caso di crash dell'applicazione. Sono rari con un'elaborazione delle eccezioni non gestita, ma perché punire l'utente con la dimensione / posizione persa quando si verificano misteriosamente.
Thomas,

7
C'è un bug in questo codice in quanto, se l'utente apre la finestra sul suo secondo schermo, quindi disconnette quello schermo dal computer, la prossima volta che aprirà la finestra, verrà presentato fuori dallo schermo. Se la finestra è modale, l'utente non sarà in grado di interagire affatto con l'app e non capirà cosa sta succedendo. È necessario aggiungere un controllo dei limiti utilizzando Window.GetScreen (), dopo aver convertito le coordinate dello schermo in valori dipendenti da DPI.
Omer Raviv

2
@OmerRaviv - non è un bug, ma una limitazione :) Scherzi a parte, non ho affrontato questo aspetto del problema.
ChrisF

73

In realtà non è necessario utilizzare il code-behind per farlo (tranne per il salvataggio delle impostazioni). Puoi utilizzare un'estensione di markup personalizzata per associare la dimensione e la posizione della finestra alle impostazioni in questo modo:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:my="clr-namespace:WpfApplication1"
        Title="Window1"
        Height="{my:SettingBinding Height}"
        Width="{my:SettingBinding Width}"
        Left="{my:SettingBinding Left}"
        Top="{my:SettingBinding Top}">

Puoi trovare il codice per questa estensione di markup qui: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/


4
Mi piace questa risposta più della risposta accettata scelta. Molto bene.
moswald

6
+1 - Adoro l'uso di rilegature ed estensioni! Se aggiungi WindowState alle impostazioni associate, fornisce tutte le funzionalità. In alternativa, se hai le impostazioni utente disponibili nel DataContext, puoi usare qualcosa come {Binding Settings.Height}, ecc.
Matt DeKrey

Questo approccio presenta un problema quando l'utente chiude l'applicazione quando la finestra è ingrandita.
Vinicius Rocha

@Vinicius, puoi approfondire? Qual è esattamente il problema?
Thomas Levesque,

4
E quando le persone hanno due monitor e quindi potrebbero avere coordinate negative e quindi cambiano le configurazioni del monitor ei valori non sono più validi?
Andrew Truckle

33

Sebbene sia possibile "eseguire il rollio personale" e salvare manualmente le impostazioni da qualche parte, e in generale funzionerà, è molto facile non gestire correttamente tutti i casi. È molto meglio lasciare che il sistema operativo faccia il lavoro per te, chiamando GetWindowPlacement () all'uscita e SetWindowPlacement () all'avvio. Gestisce tutti i casi limite pazzi che possono verificarsi (più monitor, salva la dimensione normale della finestra se viene chiusa mentre è ingrandita, ecc.) In modo che tu non debba farlo.

Questo esempio di MSDN mostra come usarli con un'app WPF. L'esempio non è perfetto (la finestra inizierà nell'angolo in alto a sinistra il più piccolo possibile alla prima esecuzione e c'è un comportamento strano con il designer delle impostazioni che salva un valore di tipo WINDOWPLACEMENT), ma dovrebbe almeno farti iniziare.


Bella soluzione. Tuttavia, ho appena scoperto che GetWindowPlacement / SetWindowPlacement non è a conoscenza di Aero Snap
Mark Bell

1
@RandomEngy ha pubblicato una risposta migliorata basata su questo.
Stéphane Gourichon

27

Il binding "long form" che Thomas ha postato sopra non richiede quasi nessuna codifica, assicurati solo di avere il binding dello spazio dei nomi:

<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:p="clr-namespace:WpfApplication1.Properties"
        Title="Window1"
        Height="{Binding Source={x:Static p:Settings.Default}, Path=Height, Mode=TwoWay}"
        Width="{Binding Source={x:Static p:Settings.Default}, Path=Width, Mode=TwoWay}"
        Left="{Binding Source={x:Static p:Settings.Default}, Path=Left, Mode=TwoWay}"
        Top="{Binding Source={x:Static p:Settings.Default}, Path=Top, Mode=TwoWay}">

Quindi per salvare sul code-behind:

private void frmMain_Closed(object sender, EventArgs e)
{
    Properties.Settings.Default.Save();
}

Ho scelto questa soluzione, ma ho salvato le impostazioni solo se lo stato della finestra era normale, altrimenti può essere complicato uscire dalla modalità massimizzata
David Sykes

7
+1 Ho usato anche questo, @DavidSykes - L'aggiunta di un'altra impostazione per lo stato della finestra sembra funzionare abbastanza bene, ad esempioWindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson,

@ RobJohnson Ho provato il tuo suggerimento e ha funzionato molto bene, grazie.
David Sykes

4

In alternativa, potrebbe piacerti anche il seguente approccio ( vedi fonte ). Aggiungi la classe WindowSettings al tuo progetto e inseriscila WindowSettings.Save="True"nell'intestazione della finestra principale:

<Window x:Class="YOURPROJECT.Views.ShellView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Services="clr-namespace:YOURNAMESPACE.Services" 
    Services:WindowSettings.Save="True">

Dove WindowSettings è definito come segue:

using System;
using System.ComponentModel;
using System.Configuration;
using System.Windows;

namespace YOURNAMESPACE.Services
{
/// <summary>
///   Persists a Window's Size, Location and WindowState to UserScopeSettings
/// </summary>
public class WindowSettings
{
    #region Fields

    /// <summary>
    ///   Register the "Save" attached property and the "OnSaveInvalidated" callback
    /// </summary>
    public static readonly DependencyProperty SaveProperty = DependencyProperty.RegisterAttached("Save", typeof (bool), typeof (WindowSettings), new FrameworkPropertyMetadata(OnSaveInvalidated));

    private readonly Window mWindow;

    private WindowApplicationSettings mWindowApplicationSettings;

    #endregion Fields

    #region Constructors

    public WindowSettings(Window pWindow) { mWindow = pWindow; }

    #endregion Constructors

    #region Properties

    [Browsable(false)] public WindowApplicationSettings Settings {
        get {
            if (mWindowApplicationSettings == null) mWindowApplicationSettings = CreateWindowApplicationSettingsInstance();
            return mWindowApplicationSettings;
        }
    }

    #endregion Properties

    #region Methods

    public static void SetSave(DependencyObject pDependencyObject, bool pEnabled) { pDependencyObject.SetValue(SaveProperty, pEnabled); }

    protected virtual WindowApplicationSettings CreateWindowApplicationSettingsInstance() { return new WindowApplicationSettings(this); }

    /// <summary>
    ///   Load the Window Size Location and State from the settings object
    /// </summary>
    protected virtual void LoadWindowState() {
        Settings.Reload();
        if (Settings.Location != Rect.Empty) {
            mWindow.Left = Settings.Location.Left;
            mWindow.Top = Settings.Location.Top;
            mWindow.Width = Settings.Location.Width;
            mWindow.Height = Settings.Location.Height;
        }
        if (Settings.WindowState != WindowState.Maximized) mWindow.WindowState = Settings.WindowState;
    }

    /// <summary>
    ///   Save the Window Size, Location and State to the settings object
    /// </summary>
    protected virtual void SaveWindowState() {
        Settings.WindowState = mWindow.WindowState;
        Settings.Location = mWindow.RestoreBounds;
        Settings.Save();
    }

    /// <summary>
    ///   Called when Save is changed on an object.
    /// </summary>
    private static void OnSaveInvalidated(DependencyObject pDependencyObject, DependencyPropertyChangedEventArgs pDependencyPropertyChangedEventArgs) {
        var window = pDependencyObject as Window;
        if (window != null)
            if ((bool) pDependencyPropertyChangedEventArgs.NewValue) {
                var settings = new WindowSettings(window);
                settings.Attach();
            }
    }

    private void Attach() {
        if (mWindow != null) {
            mWindow.Closing += WindowClosing;
            mWindow.Initialized += WindowInitialized;
            mWindow.Loaded += WindowLoaded;
        }
    }

    private void WindowClosing(object pSender, CancelEventArgs pCancelEventArgs) { SaveWindowState(); }

    private void WindowInitialized(object pSender, EventArgs pEventArgs) { LoadWindowState(); }

    private void WindowLoaded(object pSender, RoutedEventArgs pRoutedEventArgs) { if (Settings.WindowState == WindowState.Maximized) mWindow.WindowState = Settings.WindowState; }

    #endregion Methods

    #region Nested Types

    public class WindowApplicationSettings : ApplicationSettingsBase
    {
        #region Constructors

        public WindowApplicationSettings(WindowSettings pWindowSettings) { }

        #endregion Constructors

        #region Properties

        [UserScopedSetting] public Rect Location {
            get {
                if (this["Location"] != null) return ((Rect) this["Location"]);
                return Rect.Empty;
            }
            set { this["Location"] = value; }
        }

        [UserScopedSetting] public WindowState WindowState {
            get {
                if (this["WindowState"] != null) return (WindowState) this["WindowState"];
                return WindowState.Normal;
            }
            set { this["WindowState"] = value; }
        }

        #endregion Properties
    }

    #endregion Nested Types
}
}

3

Il modo predefinito per risolverlo è utilizzare i file delle impostazioni. Il problema con i file delle impostazioni è che devi definire tutte le impostazioni e scrivere il codice che copia i dati avanti e indietro da solo. Abbastanza noioso se hai molte proprietà di cui tenere traccia.

Ho creato una libreria abbastanza flessibile e molto facile da usare per questo, devi solo dirgli quali proprietà di quale oggetto tenere traccia e lui fa il resto. Puoi anche configurare la schifezza, se lo desideri.

La libreria si chiama Jot (github) , ecco un vecchio articolo di CodeProject ho scritto a riguardo.

Ecco come lo useresti per tenere traccia delle dimensioni e della posizione di una finestra:

public MainWindow()
{
    InitializeComponent();

    _stateTracker.Configure(this)
        .IdentifyAs("MyMainWindow")
        .AddProperties(nameof(Height), nameof(Width), nameof(Left), nameof(Top), nameof(WindowState))
        .RegisterPersistTrigger(nameof(Closed))
        .Apply();
}

Jot e file di impostazioni: con Jot c'è molto meno codice ed è molto meno soggetto a errori poiché è necessario menzionare ogni proprietà una sola volta . Con i file delle impostazioni è necessario menzionare ciascuna proprietà 5 volte : una volta quando si crea esplicitamente la proprietà e altre quattro volte nel codice che copia i valori avanti e indietro.

Archiviazione, serializzazione ecc. Sono completamente configurabili. Inoltre, quando si utilizza IOC, è anche possibile collegarlo in modo che applichi automaticamente il tracciamento a tutti gli oggetti che risolve in modo che tutto ciò che è necessario fare per rendere persistente una proprietà è schiaffeggiare un attributo [Trackable] su di essa.

Sto scrivendo tutto questo perché penso che la libreria sia di prim'ordine e voglio parlarne con la bocca aperta.


Bene, grazie per questo: ho usato il tuo frammento di codice in una nuova classe per impostare il tracker di stato con un percorso basato sul nome del programma. D'ora in poi devo solo scrivere una riga e tutte le proprietà della finestra vengono gestite
Awesomeness

1

Ho scritto una lezione veloce che lo fa. Ecco come si chiama:

    public MainWindow()
    {
        FormSizeSaver.RegisterForm(this, () => Settings.Default.MainWindowSettings,
                                   s =>
                                   {
                                       Settings.Default.MainWindowSettings = s;
                                       Settings.Default.Save();
                                   });
        InitializeComponent();
        ...

Ed ecco il codice:

public class FormSizeSaver
{
    private readonly Window window;
    private readonly Func<FormSizeSaverSettings> getSetting;
    private readonly Action<FormSizeSaverSettings> saveSetting;
    private FormSizeSaver(Window window, Func<string> getSetting, Action<string> saveSetting)
    {
        this.window = window;
        this.getSetting = () => FormSizeSaverSettings.FromString(getSetting());
        this.saveSetting = s => saveSetting(s.ToString());

        window.Initialized += InitializedHandler;
        window.StateChanged += StateChangedHandler;
        window.SizeChanged += SizeChangedHandler;
        window.LocationChanged += LocationChangedHandler;
    }

    public static FormSizeSaver RegisterForm(Window window, Func<string> getSetting, Action<string> saveSetting)
    {
        return new FormSizeSaver(window, getSetting, saveSetting);
    }


    private void SizeChangedHandler(object sender, SizeChangedEventArgs e)
    {
        var s = getSetting();
        s.Height = e.NewSize.Height;
        s.Width = e.NewSize.Width;
        saveSetting(s);
    }

    private void StateChangedHandler(object sender, EventArgs e)
    {
        var s = getSetting();
        if (window.WindowState == WindowState.Maximized)
        {
            if (!s.Maximized)
            {
                s.Maximized = true;
                saveSetting(s);
            }
        }
        else if (window.WindowState == WindowState.Normal)
        {
            if (s.Maximized)
            {
                s.Maximized = false;
                saveSetting(s);
            }
        }
    }

    private void InitializedHandler(object sender, EventArgs e)
    {
        var s = getSetting();
        window.WindowState = s.Maximized ? WindowState.Maximized : WindowState.Normal;

        if (s.Height != 0 && s.Width != 0)
        {
            window.Height = s.Height;
            window.Width = s.Width;
            window.WindowStartupLocation = WindowStartupLocation.Manual;
            window.Left = s.XLoc;
            window.Top = s.YLoc;
        }
    }

    private void LocationChangedHandler(object sender, EventArgs e)
    {
        var s = getSetting();
        s.XLoc = window.Left;
        s.YLoc = window.Top;
        saveSetting(s);
    }
}

[Serializable]
internal class FormSizeSaverSettings
{
    public double Height, Width, YLoc, XLoc;
    public bool Maximized;

    public override string ToString()
    {
        using (var ms = new MemoryStream())
        {
            var bf = new BinaryFormatter();
            bf.Serialize(ms, this);
            ms.Position = 0;
            byte[] buffer = new byte[(int)ms.Length];
            ms.Read(buffer, 0, buffer.Length);
            return Convert.ToBase64String(buffer);
        }
    }

    internal static FormSizeSaverSettings FromString(string value)
    {
        try
        {
            using (var ms = new MemoryStream(Convert.FromBase64String(value)))
            {
                var bf = new BinaryFormatter();
                return (FormSizeSaverSettings) bf.Deserialize(ms);
            }
        }
        catch (Exception)
        {
            return new FormSizeSaverSettings();
        }
    }
}

window.Intitialized should be window.Loaded see mosttech.blogspot.com/2008/01/…
Gleb Sevruk

@Gleb, entrambi funzionano credo. Hai problemi con esso su Initialized?
tster

Sì, poiché la finestra ingrandita sarà sullo schermo errato se si utilizza solo l'evento inizializzato. Quello che ho fatto e questo sembra funzionare: ora mi sto iscrivendo anche all'evento Loaded. Ho spostato _window.WindowState = s.Maximized? WindowState.Maximized: WindowState.Normal; riga all'interno del gestore di eventi "Loaded". window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; btw: Mi piace questo approccio
Gleb Sevruk

1

C'è un NuGet Project RestoreWindowPlace vedere su GitHub che fa tutto questo per te, salvando le informazioni in un file XML.

Per farlo funzionare su una finestra, è semplice come chiamare:

((App)Application.Current).WindowPlace.Register(this);

In App crei la classe che gestisce le tue finestre. Vedere il collegamento github sopra per ulteriori informazioni.


0

Potrebbe piacerti questo:

public class WindowStateHelper
{
    public static string ToXml(System.Windows.Window win)
    {
        XElement bounds = new XElement("Bounds");
        if (win.WindowState == System.Windows.WindowState.Maximized)
        {
            bounds.Add(new XElement("Top", win.RestoreBounds.Top));
            bounds.Add(new XElement("Left", win.RestoreBounds.Left));
            bounds.Add(new XElement("Height", win.RestoreBounds.Height));
            bounds.Add(new XElement("Width", win.RestoreBounds.Width));
        }
        else
        {
            bounds.Add(new XElement("Top", win.Top));
            bounds.Add(new XElement("Left", win.Left));
            bounds.Add(new XElement("Height", win.Height));
            bounds.Add(new XElement("Width", win.Width));
        }
        XElement root = new XElement("WindowState",
            new XElement("State", win.WindowState.ToString()),
            new XElement("Visibility", win.Visibility.ToString()),
            bounds);

        return root.ToString();
    }

    public static void FromXml(string xml, System.Windows.Window win)
    {
        try
        {
            XElement root = XElement.Parse(xml);
            string state = root.Descendants("State").FirstOrDefault().Value;
            win.WindowState = (System.Windows.WindowState)Enum.Parse(typeof(System.Windows.WindowState), state);

            state = root.Descendants("Visibility").FirstOrDefault().Value;
            win.Visibility = (System.Windows.Visibility)Enum.Parse(typeof(System.Windows.Visibility), state);

            XElement bounds = root.Descendants("Bounds").FirstOrDefault();
            win.Top = Convert.ToDouble(bounds.Element("Top").Value);
            win.Left = Convert.ToDouble(bounds.Element("Left").Value);
            win.Height = Convert.ToDouble(bounds.Element("Height").Value);
            win.Width = Convert.ToDouble(bounds.Element("Width").Value);
        }
        catch (Exception x)
        {
            System.Console.WriteLine(x.ToString());
        }
    }
}

Quando l'app si chiude:

        Properties.Settings.Default.Win1Placement = WindowStateHelper.ToXml(win1);
        Properties.Settings.Default.Win2Placement = WindowStateHelper.ToXml(win2);
        ...

All'avvio dell'app:

        WindowStateHelper.FromXml(Properties.Settings.Default.Win1Placement, win1);
        WindowStateHelper.FromXml(Properties.Settings.Default.Win2Placement, win2);
        ...

0

Crea una stringa denominata WindowXml nelle impostazioni predefinite.

Utilizzare questo metodo di estensione sugli eventi Finestra caricata e Chiusura per ripristinare e salvare le dimensioni e la posizione della finestra.

using YourProject.Properties;
using System;
using System.Linq;
using System.Windows;
using System.Xml.Linq;

namespace YourProject.Extensions
{
    public static class WindowExtensions
    {
        public static void SaveSizeAndLocation(this Window w)
        {
            try
            {
                var s = "<W>";
                s += GetNode("Top", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Top : w.Top);
                s += GetNode("Left", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Left : w.Left);
                s += GetNode("Height", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Height : w.Height);
                s += GetNode("Width", w.WindowState == WindowState.Maximized ? w.RestoreBounds.Width : w.Width);
                s += GetNode("WindowState", w.WindowState);
                s += "</W>";

                Settings.Default.WindowXml = s;
                Settings.Default.Save();
            }
            catch (Exception)
            {
            }
        }

        public static void RestoreSizeAndLocation(this Window w)
        {
            try
            {
                var xd = XDocument.Parse(Settings.Default.WindowXml);
                w.WindowState = (WindowState)Enum.Parse(typeof(WindowState), xd.Descendants("WindowState").FirstOrDefault().Value);
                w.Top = Convert.ToDouble(xd.Descendants("Top").FirstOrDefault().Value);
                w.Left = Convert.ToDouble(xd.Descendants("Left").FirstOrDefault().Value);
                w.Height = Convert.ToDouble(xd.Descendants("Height").FirstOrDefault().Value);
                w.Width = Convert.ToDouble(xd.Descendants("Width").FirstOrDefault().Value);
            }
            catch (Exception)
            {
            }
        }

        private static string GetNode(string name, object value)
        {
            return string.Format("<{0}>{1}</{0}>", name, value);
        }
    }
}

0

Sto usando la risposta di Lance Cleveland e vincolo l'ambientazione. Ma sto usando un po 'di codice per evitare che la mia finestra sia fuori dallo schermo.

private void SetWindowSettingsIntoScreenArea()
{
    // first detect Screen, where we will display the Window
    // second correct bottom and right position
    // then the top and left position.
    // If Size is bigger than current Screen, it's still possible to move and size the Window

    // get the screen to display the window
    var screen = System.Windows.Forms.Screen.FromPoint(new System.Drawing.Point((int)Default.Left, (int)Default.Top));

    // is bottom position out of screen for more than 1/3 Height of Window?
    if (Default.Top + (Default.Height / 3) > screen.WorkingArea.Height)
        Default.Top = screen.WorkingArea.Height - Default.Height;

    // is right position out of screen for more than 1/2 Width of Window?
    if (Default.Left + (Default.Width / 2) > screen.WorkingArea.Width)
        Default.Left = screen.WorkingArea.Width - Default.Width;

    // is top position out of screen?
    if (Default.Top < screen.WorkingArea.Top)
        Default.Top = screen.WorkingArea.Top;

    // is left position out of screen?
    if (Default.Left < screen.WorkingArea.Left)
        Default.Left = screen.WorkingArea.Left;
}

0

Ho creato una soluzione più generica basata sulla brillante risposta di RandomEngys. Salva la posizione nel file nella cartella in esecuzione e non è necessario creare nuove proprietà per ogni nuova finestra che si crea. Questa soluzione funziona alla grande per me con un codice minimo nel codice dietro.

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Xml;
using System.Xml.Serialization;

namespace WindowPlacementNameSpace
{

    // RECT structure required by WINDOWPLACEMENT structure
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;

        public RECT(int left, int top, int right, int bottom)
        {
            this.Left = left;
            this.Top = top;
            this.Right = right;
            this.Bottom = bottom;
        }
    }

    // POINT structure required by WINDOWPLACEMENT structure
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;

        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
    }

    // WINDOWPLACEMENT stores the position, size, and state of a window
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct WINDOWPLACEMENT
    {
        public int length;
        public int flags;
        public int showCmd;
        public POINT minPosition;
        public POINT maxPosition;
        public RECT normalPosition;
    }

    public static class WindowPlacement
    {
        private static readonly Encoding Encoding = new UTF8Encoding();
        private static readonly XmlSerializer Serializer = new XmlSerializer(typeof(WINDOWPLACEMENT));

        [DllImport("user32.dll")]
        private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);

        [DllImport("user32.dll")]
        private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);

        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOWMINIMIZED = 2;

        private static void SetPlacement(IntPtr windowHandle, string placementXml)
        {
            if (string.IsNullOrEmpty(placementXml))
            {
                return;
            }

            byte[] xmlBytes = Encoding.GetBytes(placementXml);

            try
            {
                WINDOWPLACEMENT placement;
                using (MemoryStream memoryStream = new MemoryStream(xmlBytes))
                {
                    placement = (WINDOWPLACEMENT)Serializer.Deserialize(memoryStream);
                }

                placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
                placement.flags = 0;
                placement.showCmd = (placement.showCmd == SW_SHOWMINIMIZED ? SW_SHOWNORMAL : placement.showCmd);
                SetWindowPlacement(windowHandle, ref placement);
            }
            catch (InvalidOperationException)
            {
                // Parsing placement XML failed. Fail silently.
            }
        }

        private static string GetPlacement(IntPtr windowHandle)
        {
            WINDOWPLACEMENT placement;
            GetWindowPlacement(windowHandle, out placement);

            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, Encoding.UTF8))
                {
                    Serializer.Serialize(xmlTextWriter, placement);
                    byte[] xmlBytes = memoryStream.ToArray();
                    return Encoding.GetString(xmlBytes);
                }
            }
        }
        public static void ApplyPlacement(this Window window)
        {
            var className = window.GetType().Name;
            try
            {
                var pos = File.ReadAllText(Directory + "\\" + className + ".pos");
                SetPlacement(new WindowInteropHelper(window).Handle, pos);
            }
            catch (Exception exception)
            {
                Log.Error("Couldn't read position for " + className, exception);
            }

        }

        public static void SavePlacement(this Window window)
        {
            var className = window.GetType().Name;
            var pos =  GetPlacement(new WindowInteropHelper(window).Handle);
            try
            {
                File.WriteAllText(Directory + "\\" + className + ".pos", pos);
            }
            catch (Exception exception)
            {
                Log.Error("Couldn't write position for " + className, exception);
            }
        }
        private static string Directory => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

    }
}

Nel tuo codice dietro di te aggiungi questi due metodi

///This method is save the actual position of the window to file "WindowName.pos"
private void ClosingTrigger(object sender, EventArgs e)
{
    this.SavePlacement();
}
///This method is load the actual position of the window from the file
protected override void OnSourceInitialized(EventArgs e)
{
    base.OnSourceInitialized(e);
    this.ApplyPlacement();
}

nella finestra xaml aggiungi questo

Closing="ClosingTrigger"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.