.NET WPF Fenstergröße zwischen Sitzungen speichern

93

Wenn der Benutzer die Größe des Fensters meiner Anwendung ändert, möchte ich, dass die Anwendung dieselbe Größe hat, wenn die Anwendung erneut geöffnet wird.

Zuerst habe ich mir überlegt, das SizeChanged-Ereignis zu behandeln und Höhe und Breite zu speichern, aber ich denke, es muss eine einfachere Lösung geben.

Ziemlich einfaches Problem, aber ich kann keine einfache Lösung dafür finden.

Daniil Harik
quelle
2
Bitte beachten Sie, dass Sie, wenn Sie sowohl die Größe als auch die Position wiederherstellen (wie die meisten Codebeispiele unten), den Randfall behandeln möchten, in dem jemand den Monitor, auf dem das Fenster zuletzt angezeigt wurde, vom Stromnetz getrennt hat, um zu vermeiden, dass Ihr Fenster angezeigt wird Fenster außerhalb des Bildschirms.
Omer Raviv
@OmerRaviv Haben Sie ein Beispiel gefunden, das den Randfall berücksichtigt?
Andrew Truckle
Ich habe zu wenig Ansehen, um einen Kommentar hinzuzufügen, daher habe ich diesen neuen Awnser erstellt. Ich verwende die gleiche Lösung wie Lance Cleveland, einschließlich der Einstellung von RobJohnson , aber es funktioniert nicht, wenn Sie sie für Unterfenster verwenden und mehr davon gleichzeitig öffnen möchten ...
AelanY

Antworten:

121

Speichern Sie die Werte in der Datei user.config.

Sie müssen den Wert in der Einstellungsdatei erstellen - er sollte sich im Ordner Eigenschaften befinden. Erstellen Sie fünf Werte:

  • Top vom Typ double
  • Left vom Typ double
  • Height vom Typ double
  • Width vom Typ double
  • Maximizedvom Typ bool- um zu halten, ob das Fenster maximiert ist oder nicht. Wenn Sie weitere Informationen speichern möchten, wird ein anderer Typ oder eine andere Struktur benötigt.

Initialisieren Sie die ersten beiden auf 0 und die zweiten beiden auf die Standardgröße Ihrer Anwendung und die letzte auf false.

Erstellen Sie einen Window_OnSourceInitialized-Ereignishandler und fügen Sie Folgendes hinzu:

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

ANMERKUNG: Die festgelegte Fensterplatzierung muss im initialisierten Ereignis des Fensters und nicht im Konstruktor erfolgen. Wenn Sie das Fenster auf einem zweiten Monitor maximiert haben, wird es auf dem primären Monitor immer maximiert neu gestartet, und Sie können es nicht um darauf zuzugreifen.

Erstellen Sie einen Window_Closing-Ereignishandler und fügen Sie Folgendes hinzu:

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

Dies schlägt fehl, wenn der Benutzer den Anzeigebereich verkleinert - entweder durch Trennen eines Bildschirms oder Ändern der Bildschirmauflösung -, während die Anwendung geschlossen ist. Fügen Sie daher vor dem Anwenden der Werte eine Überprüfung hinzu, ob der gewünschte Speicherort und die gewünschte Größe noch gültig sind.

ChrisF
quelle
5
Tatsächlich werden Einstellungen mit dem Bereich "Benutzer" nicht in der Datei app.config in den Programmdateien gespeichert, sondern in einer Datei user.config im Anwendungsdatenverzeichnis des Benutzers. Es ist also kein Problem ...
Thomas Levesque
7
Eigentlich können Sie "WindowState" zu den Einstellungen hinzufügen. Wählen Sie Typ -> Durchsuchen -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka
2
FWIW, ich mache dies auch über den Handler mit geänderter Größe, falls die Anwendung abstürzt. Sie sind selten mit einer unbehandelten Ausnahmeverarbeitung, aber warum den Benutzer mit verlorener Größe / Position bestrafen, wenn sie auf mysteriöse Weise auftreten.
Thomas
7
Dieser Code weist einen Fehler auf: Wenn der Benutzer das Fenster auf seinem zweiten Bildschirm öffnet und diesen Bildschirm dann vom Computer trennt, wird er beim nächsten Öffnen außerhalb des Bildschirms außerhalb des Bildschirms angezeigt. Wenn das Fenster modal ist, kann der Benutzer überhaupt nicht mit der App interagieren und versteht nicht, was los ist. Sie müssen mit Window.GetScreen () eine Grenzüberprüfung hinzufügen, nachdem Sie die Bildschirmkoordinaten in DPI-abhängige Werte konvertiert haben.
Omer Raviv
2
@OmerRaviv - es ist kein Fehler, sondern eine Einschränkung :) Im Ernst - ich habe diesen Aspekt des Problems nicht angesprochen.
ChrisF
73

Eigentlich müssen Sie dafür kein Code-Behind verwenden (außer zum Speichern der Einstellungen). Sie können eine benutzerdefinierte Markup-Erweiterung verwenden, um die Fenstergröße und -position an folgende Einstellungen zu binden:

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

Den Code für diese Markup-Erweiterung finden Sie hier: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/

Thomas Levesque
quelle
4
Ich mag diese Antwort mehr als die gewählte akzeptierte Antwort. Gut gemacht.
Moswald
6
+1 - Ich liebe die Verwendung von Bindungen und Verlängerungen! Wenn Sie den WindowState zu Ihren gebundenen Einstellungen hinzufügen, bietet er alle Funktionen. Alternativ, wenn Sie die Benutzereinstellungen in der Datacontext zur Verfügung haben, können Sie so etwas wie verwenden {Binding Settings.Height}usw.
Matt DeKrey
Dieser Ansatz hat ein Problem, wenn der Benutzer die Anwendung schließt, wenn das Fenster maximiert ist.
Vinicius Rocha
@ Vinicius, kannst du das näher erläutern? Was genau ist das Problem?
Thomas Levesque
4
Was ist, wenn Personen zwei Monitore haben und diese daher möglicherweise negative Koordinaten haben und dann die Monitorkonfigurationen ändern und die Werte nicht mehr gültig sind?
Andrew Truckle
33

Während Sie "Ihre eigenen rollen" und die Einstellungen manuell irgendwo speichern können und es im Allgemeinen funktioniert, ist es sehr einfach, nicht alle Fälle richtig zu behandeln. Es ist viel besser, das Betriebssystem die Arbeit für Sie erledigen zu lassen, indem Sie GetWindowPlacement () beim Beenden und SetWindowPlacement () beim Start aufrufen . Es behandelt alle verrückten Randfälle, die auftreten können (mehrere Monitore, speichern Sie die normale Größe des Fensters, wenn es bei Maximierung geschlossen wird usw.), damit Sie dies nicht tun müssen.

Dieses MSDN-Beispiel zeigt, wie diese mit einer WPF-App verwendet werden. Das Beispiel ist nicht perfekt (das Fenster wird in der oberen linken Ecke beim ersten Durchlauf so klein wie möglich gestartet, und es gibt ein merkwürdiges Verhalten WINDOWPLACEMENT, wenn der Einstellungsdesigner einen Wert vom Typ speichert ), aber es sollte zumindest den Einstieg erleichtern.

Andy
quelle
Schöne Lösung. Allerdings habe ich gerade festgestellt, dass GetWindowPlacement / SetWindowPlacement nicht Aero Snap-
Mark Bell
1
@RandomEngy hat basierend darauf eine verbesserte Antwort veröffentlicht.
Stéphane Gourichon
27

Die "Langform" -Bindung, die Thomas oben gepostet hat, erfordert fast keine Codierung. Stellen Sie nur sicher, dass Sie die Namespace-Bindung haben:

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

Dann, um den Code-Behind zu speichern:

private void frmMain_Closed(object sender, EventArgs e)
{
    Properties.Settings.Default.Save();
}
Lance Cleveland
quelle
Ich habe mich für diese Lösung entschieden, die Einstellungen jedoch nur gespeichert, wenn der Fensterstatus normal war. Andernfalls kann es schwierig sein, den maximierten Modus zu verlassen
David Sykes,
7
+1 Ich habe dies auch verwendet, @DavidSykes - Das Hinzufügen einer weiteren Einstellung für den Fensterstatus scheint gut genug zu funktionieren, z. B.WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson
@RobJohnson Ich habe Ihren Vorschlag ausprobiert und es hat sehr gut funktioniert, danke.
David Sykes
4

Alternativ könnte Ihnen auch der folgende Ansatz gefallen ( siehe Quelle ). Fügen Sie die WindowSettings-Klasse zu Ihrem Projekt hinzu und fügen Sie sie WindowSettings.Save="True"in die Kopfzeile Ihres Hauptfensters ein:

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

Wobei WindowSettings wie folgt definiert ist:

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
}
}
Erik Vullings
quelle
3

Die Standardlösung besteht darin, Einstellungsdateien zu verwenden. Das Problem mit Einstellungsdateien besteht darin, dass Sie alle Einstellungen definieren und den Code schreiben müssen, der die Daten selbst hin und her kopiert. Ziemlich langweilig, wenn Sie viele Eigenschaften im Auge behalten müssen.

Ich habe dafür eine ziemlich flexible und sehr einfach zu verwendende Bibliothek erstellt. Sie sagen ihr einfach, welche Eigenschaften von welchem ​​Objekt verfolgt werden sollen, und sie erledigt den Rest. Sie können den Mist auch daraus konfigurieren, wenn Sie möchten.

Die Bibliothek heißt Jot (Github) , hier ist ein alter CodeProject-Artikel, den ich darüber geschrieben habe.

So würden Sie es verwenden, um die Größe und Position eines Fensters zu verfolgen:

public MainWindow()
{
    InitializeComponent();

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

Jot vs. Einstellungsdateien: Mit Jot gibt es erheblich weniger Code und es ist viel weniger fehleranfällig, da Sie jede Eigenschaft nur einmal erwähnen müssen . Bei Einstellungsdateien müssen Sie jede Eigenschaft fünfmal erwähnen : einmal, wenn Sie die Eigenschaft explizit erstellen, und weitere viermal im Code, der die Werte hin und her kopiert.

Speicher, Serialisierung usw. sind vollständig konfigurierbar. Wenn Sie IOC verwenden, können Sie es sogar so anschließen, dass die Nachverfolgung automatisch auf alle aufgelösten Objekte angewendet wird. Alles, was Sie tun müssen, um eine Eigenschaft dauerhaft zu machen, ist, ein Attribut [Nachverfolgbar] darauf zu setzen.

Ich schreibe das alles, weil ich denke, dass die Bibliothek erstklassig ist und ich darüber reden möchte.

anakisch
quelle
Schön, danke dafür - ich habe Ihr Code-Snippet in einer neuen Klasse verwendet, um den Status-Tracker mit einem Pfad einzurichten, der auf dem Namen des Programms basiert. Von jetzt an muss ich nur noch eine Zeile schreiben und alle Fenstereigenschaften werden behandelt
Awesomeness
1

Ich habe eine kurze Klasse geschrieben, die dies tut. So heißt es:

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

Und hier ist der Code:

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();
        }
    }
}
tster
quelle
window.Intitialized sollte window.Loaded sein, siehe mosttech.blogspot.com/2008/01/…
Gleb Sevruk
@ Gleb, beide arbeiten ich denke. Haben Sie Probleme damit bei Initialized?
Tster
Ja, da das maximierte Fenster auf einem falschen Bildschirm angezeigt wird, wenn Sie nur das initialisierte Ereignis verwenden. Was ich getan habe und das scheint zu funktionieren: Jetzt abonniere ich auch das Loaded-Ereignis. Ich habe _window.WindowState = s.Maximized verschoben? WindowState.Maximized: WindowState.Normal; Zeile innerhalb des Ereignishandlers "Geladen". window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; Übrigens: Ich mag diesen Ansatz
Gleb Sevruk
1

Es gibt ein NuGet-Projekt RestoreWindowPlace auf github , das all dies für Sie erledigt und die Informationen in einer XML-Datei speichert.

Damit es in einem Fenster funktioniert, müssen Sie lediglich Folgendes aufrufen:

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

In der App erstellen Sie die Klasse, die Ihre Fenster verwaltet. Weitere Informationen finden Sie oben unter dem Github-Link.

Chuck Savage
quelle
0

Das könnte dir gefallen:

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

Wenn die App geschlossen wird:

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

Wenn die App startet:

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

Erstellen Sie in Ihren Standardeinstellungen eine Zeichenfolge mit dem Namen WindowXml.

Verwenden Sie diese Erweiterungsmethode für Ihre Ereignisse zum Laden und Schließen von Fenstern, um die Größe und den Speicherort des Fensters wiederherzustellen und zu speichern.

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);
        }
    }
}
Tempeck
quelle
0

Ich verwende die Antwort von Lance Cleveland und binde die Einstellung. Aber ich verwende etwas mehr Code, um zu vermeiden, dass mein Fenster nicht auf dem Bildschirm angezeigt wird.

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;
}
Markus
quelle
0

Ich habe eine allgemeinere Lösung basierend auf RandomEngys brillanter Antwort gemacht. Es speichert die Position in der Datei im laufenden Ordner und Sie müssen nicht für jedes neue Fenster, das Sie erstellen, neue Eigenschaften erstellen. Diese Lösung funktioniert hervorragend für mich mit minimalem Code im Code dahinter.

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

    }
}

Fügen Sie in Ihrem Code dahinter diese beiden Methoden hinzu

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

Im XAML-Fenster fügen Sie dies hinzu

Closing="ClosingTrigger"
Björn
quelle