.NET WPF Zapamiętaj rozmiar okna między sesjami


94

Zasadniczo, gdy użytkownik zmieni rozmiar okna mojej aplikacji, chcę, aby aplikacja miała taki sam rozmiar po ponownym otwarciu aplikacji.

Na początku myślałem o obsłużeniu zdarzenia SizeChanged i zapisaniu Height i Width, ale myślę, że musi być łatwiejsze rozwiązanie.

Dość prosty problem, ale nie mogę znaleźć łatwego rozwiązania.


2
Pamiętaj, że jeśli zmieniasz zarówno rozmiar, jak i położenie (jak większość przykładów kodu poniżej), będziesz chciał obsłużyć przypadek krawędzi, w którym ktoś odłączył monitor, na którym ostatnio było wyświetlane okno, aby uniknąć prezentowania okno poza ekranem.
Omer Raviv

@OmerRaviv Czy znalazłeś przykład uwzględniający przypadek krawędzi?
Andrew Truckle

Mam zbyt mniejszą reputację, aby dodać komentarz, dlatego stworzyłem ten nowy awnser. Używam tego samego rozwiązania, co Lance Cleveland, w tym ustawienia RobJohnson , ale nie działa, jeśli używasz go do okien podrzędnych i chcesz otworzyć więcej z nich w tym samym czasie ...
AelanY

Odpowiedzi:


122

Zapisz wartości w pliku user.config.

Musisz utworzyć wartość w pliku ustawień - powinna znajdować się w folderze Właściwości. Utwórz pięć wartości:

  • Top typu double
  • Left typu double
  • Height typu double
  • Width typu double
  • Maximizedtypu bool- aby zatrzymać, czy okno jest zmaksymalizowane, czy nie. Jeśli chcesz przechowywać więcej informacji, potrzebny będzie inny typ lub struktura.

Zainicjuj pierwsze dwa na 0, a drugie dwa na domyślny rozmiar aplikacji, a ostatnie na false.

Utwórz procedurę obsługi zdarzeń Window_OnSourceInitialized i dodaj następujące elementy:

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

UWAGA: Ustawione rozmieszczenie okna musi zostać wprowadzone w zdarzeniu zainicjowanym na źródle okna, a nie konstruktorze, w przeciwnym razie, jeśli masz zmaksymalizowane okno na drugim monitorze, zawsze zostanie ponownie uruchomione zmaksymalizowane na monitorze podstawowym i nie będzie aby uzyskać do niego dostęp.

Utwórz procedurę obsługi zdarzeń Window_Closing i dodaj następujące elementy:

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

To się nie powiedzie, jeśli użytkownik zmniejszy obszar wyświetlania - poprzez odłączenie ekranu lub zmianę rozdzielczości ekranu - podczas gdy aplikacja jest zamknięta, więc przed zastosowaniem wartości należy dodać sprawdzenie, czy żądana lokalizacja i rozmiar są nadal aktualne.


5
W rzeczywistości ustawienia z zakresem „Użytkownik” nie są zapisywane w pliku app.config w Program Files, ale w pliku user.config w katalogu danych aplikacji użytkownika. Więc to nie jest problem ...
Thomas Levesque

7
Właściwie możesz dodać „WindowState” do ustawień. Wybierz typ -> przeglądaj -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka

2
FWIW, robię to również z programu obsługi o zmienionym rozmiarze, w przypadku awarii aplikacji. Są rzadkie z nieobsługiwanym przetwarzaniem wyjątków, ale po co karać użytkownika utraconym rozmiarem / lokalizacją, gdy pojawiają się w tajemniczy sposób.
Thomas,

7
W tym kodzie jest błąd polegający na tym, że jeśli użytkownik otworzy okno na swoim drugim ekranie, a następnie odłączy ten ekran od komputera, przy następnym otwarciu okna zostanie ono wyświetlone poza ekranem. Jeśli okno jest modalne, użytkownik w ogóle nie będzie mógł wchodzić w interakcję z aplikacją i nie będzie rozumiał, co się dzieje. Musisz dodać kontrolę granic za pomocą Window.GetScreen (), po konwersji współrzędnych ekranu na wartości zależne od DPI.
Omer Raviv

2
@OmerRaviv - to nie błąd, ale ograniczenie :) Poważnie - nie odniosłem się do tego aspektu problemu.
ChrisF

74

Właściwie nie musisz do tego używać kodu związanego (z wyjątkiem zapisywania ustawień). Możesz użyć niestandardowego rozszerzenia znaczników, aby powiązać rozmiar i położenie okna z ustawieniami takimi jak:

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

Kod tego rozszerzenia znaczników można znaleźć tutaj: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/


4
Ta odpowiedź podoba mi się bardziej niż wybrana, zaakceptowana odpowiedź. Dobra robota.
moswald

6
+1 - Uwielbiam używanie wiązań i przedłużek! Jeśli dodasz WindowState do powiązanych ustawień, zapewnia to pełne możliwości. Alternatywnie, jeśli masz ustawienia użytkownika dostępne w DataContext, możesz użyć czegoś takiego {Binding Settings.Height}, jak itp.
Matt DeKrey

Takie podejście powoduje problem, gdy użytkownik zamyka aplikację, gdy okno jest zmaksymalizowane.
Vinicius Rocha

@Vinicius, czy możesz to rozwinąć? Na czym dokładnie polega problem?
Thomas Levesque

4
A co z sytuacją, gdy ludzie mają dwa monitory i mogą mieć ujemne współrzędne, a następnie zmieniają konfiguracje monitorów i wartości są już nieaktualne?
Andrew Truckle

33

Chociaż możesz „przewinąć własne” i ręcznie zapisać gdzieś ustawienia, i ogólnie to zadziała, bardzo łatwo jest nie obsłużyć poprawnie wszystkich przypadków. O wiele lepiej jest pozwolić systemowi operacyjnemu wykonać pracę za Ciebie, wywołując GetWindowPlacement () przy wyjściu i SetWindowPlacement () podczas uruchamiania. Obsługuje wszystkie szalone przypadki krawędzi, które mogą wystąpić (wiele monitorów, zapisz normalny rozmiar okna, jeśli jest zamknięte, gdy jest zmaksymalizowane, itp.), Więc nie musisz tego robić.

Ten przykład MSDN pokazuje, jak używać ich z aplikacją WPF. Próbka nie jest idealna (okno zostanie uruchomione w lewym górnym rogu tak małe, jak to możliwe przy pierwszym uruchomieniu, a projektant ustawień zapisuje wartość typu WINDOWPLACEMENT), ale powinien przynajmniej zacząć.


Niezłe rozwiązanie. Jednak właśnie odkryłem, że GetWindowPlacement / SetWindowPlacement nie
Mark Bell

1
@RandomEngy opublikował ulepszoną odpowiedź na tej podstawie.
Stéphane Gourichon

27

Wiązanie "długiej formy", które Thomas opublikował powyżej, nie wymaga prawie żadnego kodowania, po prostu upewnij się, że masz powiązanie przestrzeni nazw:

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

Następnie, aby zaoszczędzić na kodzie związanym z:

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

Wybrałem to rozwiązanie, ale zapisałem ustawienia tylko wtedy, gdy stan okna był normalny, w przeciwnym razie może być kłopotliwe wyprowadzenie go z trybu zmaksymalizowanego
David Sykes

7
+1 Ja też tego użyłem, @DavidSykes - Dodanie kolejnego ustawienia dla stanu okna wydaje się działać wystarczająco dobrze, np.WindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson

@RobJohnson Wypróbowałem twoją sugestię i zadziałała bardzo dobrze, dzięki.
David Sykes,

4

Alternatywnie możesz również polubić następujące podejście ( patrz źródło ). Dodaj klasę WindowSettings do swojego projektu i wstaw WindowSettings.Save="True"w nagłówku swojego głównego okna:

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

Gdzie WindowSettings jest zdefiniowany w następujący sposób:

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

Domyślnym sposobem rozwiązania tego problemu jest użycie plików ustawień. Problem z plikami ustawień polega na tym, że musisz zdefiniować wszystkie ustawienia i napisać kod, który sam kopiuje dane tam iz powrotem. Dość uciążliwe, jeśli masz wiele nieruchomości do śledzenia.

Stworzyłem do tego dość elastyczną i bardzo łatwą w użyciu bibliotekę, po prostu powiedz jej, które właściwości obiektu śledzić, a ona zajmie się resztą. Możesz też skonfigurować bzdury, jeśli chcesz.

Biblioteka nazywa się Jot (github) , oto stary artykuł w CodeProject , który o niej napisałem.

Oto, jak można go użyć do śledzenia rozmiaru i lokalizacji okna:

public MainWindow()
{
    InitializeComponent();

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

Jot a pliki ustawień: Jot zawiera znacznie mniej kodu i jest dużo mniej podatny na błędy, ponieważ wystarczy wspomnieć o każdej właściwości tylko raz . W przypadku plików ustawień musisz wspomnieć o każdej właściwości 5 razy : raz, gdy jawnie utworzysz właściwość i dodatkowe cztery razy w kodzie, który kopiuje wartości w tę iz powrotem.

Pamięć masowa, serializacja itp. Są w pełni konfigurowalne. Ponadto, korzystając z IOC, możesz nawet podłączyć go tak, aby automatycznie stosował śledzenie do wszystkich obiektów, które rozwiązuje, tak że wszystko, co musisz zrobić, aby właściwość stała się trwała, to nałożyć na nią atrybut [Trackable].

Piszę to wszystko, bo uważam, że biblioteka jest na najwyższym poziomie i chcę o tym mówić.


Świetnie, dzięki za to - użyłem twojego fragmentu kodu w nowej klasie, aby skonfigurować moduł śledzenia stanu ze ścieżką opartą na nazwie programu. Odtąd muszę napisać tylko jedną linię i wszystkie właściwości okna są obsługiwane
Awesomeness

1

Napisałem krótką lekcję, która to robi. Oto jak to się nazywa:

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

A oto kod:

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


@Gleb, myślę, że oba działają. Czy masz z tym problemy na Initialized?
tster

Tak, ponieważ zmaksymalizowane okno będzie wyświetlane na nieprawidłowym ekranie, jeśli używasz tylko zainicjowanego zdarzenia. Co zrobiłem i wydaje się, że to działa: teraz subskrybuję również wydarzenie Loaded. Przeniosłem _window.WindowState = s.Maximized? WindowState.Maximized: WindowState.Normal; wiersz w module obsługi zdarzenia „Loaded”. window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; btw: Podoba mi się to podejście
Gleb Sevruk

1

Jest Nuget Projekt RestoreWindowPlace zobaczyć na github , że to wszystko dla Ciebie, zapisywania informacji w pliku XML.

Aby uruchomić go w oknie, wystarczy zadzwonić:

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

W aplikacji tworzysz klasę zarządzającą Twoimi oknami. Zobacz link do github powyżej, aby uzyskać więcej informacji.


0

Może ci się to spodobać:

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

Po zamknięciu aplikacji:

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

Po uruchomieniu aplikacji:

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

0

Utwórz ciąg o nazwie WindowXml w ustawieniach domyślnych.

Użyj tej metody rozszerzenia w przypadku zdarzeń załadowania i zamknięcia okna, aby przywrócić i zapisać rozmiar i lokalizację okna.

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

Używam odpowiedzi od Lance'a Clevelanda i wiążę Ustawienie. Ale używam więcej kodu, aby uniknąć tego, że moje okno jest poza ekranem.

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

Stworzyłem bardziej ogólne rozwiązanie w oparciu o genialną odpowiedź RandomEngys. Zapisuje pozycję do pliku w bieżącym folderze i nie musisz tworzyć nowych właściwości dla każdego nowego okna, które tworzysz. To rozwiązanie działa świetnie dla mnie z minimalnym kodem w kodzie.

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

    }
}

W swoim kodzie za Tobą dodaj te dwie metody

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

w oknie XAML dodajesz to

Closing="ClosingTrigger"
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.