.NET WPF Ingat ukuran jendela antar sesi

94

Pada dasarnya ketika pengguna mengubah ukuran jendela aplikasi saya, saya ingin aplikasi memiliki ukuran yang sama ketika aplikasi dibuka kembali.

Awalnya saya berpikir untuk menangani event SizeChanged dan menyimpan Height dan Width, tapi saya rasa pasti ada solusi yang lebih mudah.

Masalah yang cukup sederhana, tetapi saya tidak dapat menemukan solusi yang mudah untuk itu.

Daniil Harik
sumber
2
Harap dicatat bahwa jika Anda mengubah ukuran dan posisi (seperti kebanyakan contoh kode di bawah ini), Anda akan ingin menangani kasus tepi di mana seseorang mencabut monitor tempat jendela terakhir ditampilkan, untuk menghindari presentasi Anda. jendela di luar layar.
Omer Raviv
@OmerRaviv Pernahkah Anda menemukan contoh mengambil kasus edge untuk akun?
Andrew Truckle
Saya memiliki reputasi yang terlalu sedikit untuk menambahkan komentar, maka saya membuat awnser baru ini. Saya menggunakan solusi yang sama dengan Lance Cleveland termasuk pengaturan RobJohnson , tetapi tidak berfungsi jika Anda menggunakannya untuk sub jendela dan ingin membukanya lebih banyak pada saat yang sama ...
AelanY

Jawaban:

122

Simpan nilai di file user.config.

Anda harus membuat nilai di file pengaturan - itu harus ada di folder Properties. Buat lima nilai:

  • Top tipe double
  • Left tipe double
  • Height tipe double
  • Width tipe double
  • Maximizedjenis bool- untuk menahan apakah jendela dimaksimalkan atau tidak. Jika Anda ingin menyimpan lebih banyak informasi, maka dibutuhkan tipe atau struktur yang berbeda.

Inisialisasi dua yang pertama menjadi 0 dan dua yang kedua ke ukuran default aplikasi Anda, dan yang terakhir menjadi false.

Buat event handler Window_OnSourceInitialized dan tambahkan yang berikut ini:

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

CATATAN: Penempatan jendela yang disetel harus masuk ke acara yang diinisialisasi pada sumber dari jendela bukan konstruktor, jika tidak, jika jendela dimaksimalkan pada monitor kedua, itu akan selalu dimaksimalkan ulang pada monitor utama dan Anda tidak akan dapat untuk mengaksesnya.

Buat event handler Window_Closing dan tambahkan yang berikut ini:

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

Ini akan gagal jika pengguna membuat area tampilan lebih kecil - baik dengan memutuskan sambungan layar atau mengubah resolusi layar - saat aplikasi ditutup, jadi Anda harus menambahkan tanda centang bahwa lokasi dan ukuran yang diinginkan masih valid sebelum menerapkan nilai.

ChrisF
sumber
5
Sebenarnya, pengaturan dengan cakupan "Pengguna" tidak disimpan di file app.config di Program Files, tetapi di file user.config di direktori data aplikasi pengguna. Jadi itu bukan masalah ...
Thomas Levesque
7
Sebenarnya Anda bisa menambahkan "WindowState" ke pengaturan. Pilih jenis -> telusuri -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka
2
FWIW, saya melakukan ini dari handler yang diubah ukurannya juga, jika aplikasi macet. Mereka jarang terjadi dengan pemrosesan pengecualian yang tidak tertangani, tetapi mengapa menghukum pengguna dengan ukuran / lokasi yang hilang ketika mereka terjadi secara misterius.
Thomas
7
Ada bug dalam kode ini di mana, jika pengguna membuka jendela pada layar keduanya, kemudian memutus sambungan layar itu dari komputer, saat mereka membuka jendela, itu akan ditampilkan dari layar. Jika jendela adalah modal, pengguna tidak akan bisa berinteraksi dengan aplikasi sama sekali, dan tidak akan mengerti apa yang sedang terjadi. Anda perlu menambahkan pemeriksaan batas menggunakan Window.GetScreen (), setelah mengubah koordinat layar menjadi nilai yang bergantung pada DPI.
Omer Raviv
2
@OmerRaviv - ini bukan bug, tapi batasan :) Serius - Saya tidak membahas aspek masalah itu.
ChrisF
74

Sebenarnya Anda tidak perlu menggunakan kode di belakang untuk melakukan itu (kecuali untuk menyimpan pengaturan). Anda dapat menggunakan ekstensi markup khusus untuk mengikat ukuran dan posisi jendela ke pengaturan seperti ini:

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

Anda dapat menemukan kode untuk ekstensi markup ini di sini: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/

Thomas Levesque
sumber
4
Saya menyukai jawaban ini lebih dari jawaban yang diterima yang dipilih. Sudah selesai dilakukan dengan baik.
moswald
6
+1 - Saya suka penggunaan penjilidan dan ekstensi! Jika Anda menambahkan WindowState ke pengaturan terikat Anda, itu memberikan kemampuan penuh. Atau, jika Anda memiliki pengaturan pengguna yang tersedia di DataContext, Anda dapat menggunakan sesuatu seperti {Binding Settings.Height}, dll.
Matt DeKrey
Pendekatan ini memiliki masalah saat pengguna menutup aplikasi saat Jendela Dimaksimalkan.
Vinicius Rocha
@Vinicius, bisakah Anda menjelaskan? Apa sebenarnya masalahnya?
Thomas Levesque
4
Bagaimana jika orang memiliki dua monitor dan dengan demikian mungkin memiliki koordinat negatif dan kemudian mereka mengubah konfigurasi monitor dan nilainya tidak lagi valid?
Andrew Truckle
33

Meskipun Anda dapat "menggulung sendiri" dan secara manual menyimpan pengaturan di suatu tempat, dan secara umum ini akan berhasil, sangat mudah untuk tidak menangani semua kasus dengan benar. Jauh lebih baik membiarkan OS yang bekerja untuk Anda, dengan memanggil GetWindowPlacement () saat keluar dan SetWindowPlacement () saat memulai. Ini menangani semua kasus tepi gila yang dapat terjadi (beberapa monitor, simpan ukuran normal jendela jika ditutup saat dimaksimalkan, dll.) Sehingga Anda tidak perlu melakukannya.

Contoh MSDN ini menunjukkan cara menggunakannya dengan aplikasi WPF. Sampel tidak sempurna (jendela akan dimulai di sudut kiri atas sekecil mungkin saat pertama kali dijalankan, dan ada beberapa perilaku aneh dengan desainer Pengaturan menyimpan nilai tipe WINDOWPLACEMENT), tetapi setidaknya itu harus membantu Anda memulai.

Andy
sumber
Solusi bagus. Namun saya baru saja menemukan bahwa GetWindowPlacement / SetWindowPlacement tidak sadar Aero Snap
Mark Bell
1
@RandomEngy telah memposting jawaban yang ditingkatkan berdasarkan ini.
Stéphane Gourichon
27

Pengikatan "bentuk panjang" yang diposting Thomas di atas hampir tidak memerlukan pengkodean, cukup pastikan Anda memiliki pengikatan namespace:

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

Kemudian untuk menghemat kode di belakang:

private void frmMain_Closed(object sender, EventArgs e)
{
    Properties.Settings.Default.Save();
}
Lance Cleveland
sumber
Saya memilih solusi ini, tetapi hanya menyimpan pengaturan jika keadaan jendela normal, jika tidak maka dapat dengan fiddly mengeluarkannya dari mode maksimal
David Sykes
7
+1 Saya juga menggunakan ini, @DavidSykes - Menambahkan pengaturan lain untuk keadaan jendela tampaknya bekerja dengan cukup baik, misalnyaWindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson
@RobJohnson Saya mencoba saran Anda dan itu bekerja dengan sangat baik, terima kasih.
David Sykes
4

Atau, Anda mungkin juga menyukai pendekatan berikut ( lihat sumber ). Tambahkan kelas WindowSettings ke proyek Anda dan masukkan WindowSettings.Save="True"di header jendela utama Anda:

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

Di mana WindowSettings didefinisikan sebagai berikut:

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
sumber
3

Cara default untuk mengatasinya adalah dengan menggunakan file pengaturan. Masalah dengan file pengaturan adalah Anda harus menentukan semua pengaturan dan menulis kode yang menyalin data sendiri. Cukup membosankan jika Anda memiliki banyak properti yang harus diperhatikan.

Saya membuat pustaka yang cukup fleksibel dan sangat mudah digunakan untuk ini, Anda cukup memberi tahu properti mana dari objek mana yang akan dilacak dan itu melakukan sisanya. Anda dapat mengkonfigurasi omong kosong itu juga jika Anda mau.

Library tersebut bernama Jot (github) , ini adalah artikel CodeProject lama yang saya tulis tentangnya.

Inilah cara Anda menggunakannya untuk melacak ukuran dan lokasi jendela:

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. file pengaturan: Dengan Jot kode jauh lebih sedikit, dan jauh lebih sedikit rawan kesalahan karena Anda hanya perlu menyebutkan setiap properti satu kali . Dengan file pengaturan, Anda perlu menyebutkan setiap properti 5 kali : sekali ketika Anda secara eksplisit membuat properti dan empat kali tambahan dalam kode yang menyalin nilai bolak-balik.

Penyimpanan, serialisasi, dll sepenuhnya dapat dikonfigurasi. Selain itu, saat menggunakan IOC, Anda bahkan dapat menghubungkannya sehingga menerapkan pelacakan secara otomatis ke semua objek yang diselesaikannya sehingga semua yang perlu Anda lakukan untuk membuat properti persisten adalah memasang atribut [Trackable] di atasnya.

Saya menulis semua ini karena saya pikir perpustakaan itu adalah yang terbaik dan saya ingin berbicara banyak tentangnya.

anakic
sumber
Bagus, terima kasih untuk ini - Saya telah menggunakan potongan kode Anda di kelas baru untuk menyiapkan pelacak status dengan jalur berdasarkan nama program. Mulai sekarang saya hanya perlu menulis satu baris dan semua properti jendela ditangani
Awesomeness
1

Saya menulis kelas singkat yang melakukan ini. Inilah namanya:

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

Dan inilah kodenya:

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
sumber
window.Intitialized harus window.Loaded lihat mosttech.blogspot.com/2008/01/…
Gleb Sevruk
@ Gleb, saya pikir keduanya bekerja. Apakah Anda mengalami masalah dengan itu di Inisialisasi?
tster
Ya, karena jendela yang dimaksimalkan akan berada di layar yang salah jika Anda hanya menggunakan acara yang diinisialisasi. Apa yang telah saya lakukan dan ini sepertinya berhasil: Sekarang saya juga berlangganan acara Loaded. Saya memindahkan _window.WindowState = s.Maximized? WindowState.Maximized: WindowState.Normal; baris di dalam event handler "Dimuat". window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; btw: Saya suka pendekatan ini
Gleb Sevruk
1

Ada NuGet Project RestoreWindowPlace lihat di github yang melakukan semua ini untuk Anda, menyimpan informasi dalam file XML.

Untuk membuatnya berfungsi di jendela, itu semudah menelepon:

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

Di App Anda membuat kelas yang mengelola jendela Anda. Lihat tautan github di atas untuk informasi lebih lanjut.

Chuck Savage
sumber
0

Anda mungkin menyukai ini:

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

Saat aplikasi ditutup:

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

Saat aplikasi dimulai:

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

Buat string bernama WindowXml di Pengaturan default Anda.

Gunakan metode ekstensi ini pada acara Window Loaded dan Closing Anda untuk memulihkan dan menyimpan ukuran dan lokasi Window.

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
sumber
0

Saya menggunakan jawaban dari Lance Cleveland dan mengikat Setting. Tetapi saya menggunakan beberapa Kode lagi untuk menghindari Jendela saya keluar dari Layar.

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
sumber
0

Saya membuat solusi yang lebih umum berdasarkan jawaban brilian RandomEngys. Ini menyimpan posisi ke file di folder yang sedang berjalan dan Anda tidak perlu membuat properti baru untuk setiap jendela baru yang Anda buat. Solusi ini bekerja sangat baik untuk saya dengan kode minimal di belakang kode.

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

    }
}

Dalam kode Anda di belakang Anda menambahkan dua metode ini

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

di jendela xaml Anda menambahkan ini

Closing="ClosingTrigger"
Bjorn
sumber