.NET WPF จำขนาดหน้าต่างระหว่างเซสชัน


94

โดยทั่วไปเมื่อผู้ใช้ปรับขนาดหน้าต่างแอปพลิเคชันของฉันฉันต้องการให้แอปพลิเคชันมีขนาดเท่ากันเมื่อเปิดแอปพลิเคชันอีกครั้ง

ตอนแรกฉันจัดการเหตุการณ์ SizeChanged และบันทึกความสูงและความกว้าง แต่ฉันคิดว่าต้องมีวิธีแก้ปัญหาที่ง่ายกว่านี้

ปัญหาค่อนข้างง่าย แต่ฉันไม่สามารถหาวิธีแก้ปัญหาได้ง่ายๆ


2
โปรดทราบว่าหากคุณกำลังปรับขนาดและตำแหน่งใหม่ (ตามที่ตัวอย่างโค้ดส่วนใหญ่ด้านล่างทำ) คุณจะต้องจัดการกับขอบกรณีที่มีคนถอดปลั๊กจอภาพที่มีการนำเสนอหน้าต่างครั้งล่าสุดเพื่อหลีกเลี่ยงการนำเสนอของคุณ หน้าต่างปิดหน้าจอ
Omer Raviv

@OmerRaviv คุณพบตัวอย่างที่นำขอบกรณีเข้าบัญชีหรือไม่?
Andrew Truckle

ฉันมีตำหนิน้อยเกินไปที่จะเพิ่มความคิดเห็นดังนั้นฉันจึงสร้างกันสาดใหม่นี้ ฉันใช้โซลูชันเดียวกับLance Clevelandรวมถึงการตั้งค่าของRobJohnsonแต่จะไม่ได้ผลหากคุณใช้สำหรับหน้าต่างย่อยและต้องการเปิดเพิ่มเติมพร้อมกัน ...
AelanY

คำตอบ:


122

บันทึกค่าในไฟล์ user.config

คุณจะต้องสร้างค่าในไฟล์การตั้งค่าซึ่งควรอยู่ในโฟลเดอร์ Properties สร้างค่านิยม 5 ประการ:

  • Top ประเภท double
  • Left ประเภท double
  • Height ประเภท double
  • Width ประเภท double
  • Maximizedประเภทbool- เพื่อถือว่าหน้าต่างถูกขยายใหญ่สุดหรือไม่ หากคุณต้องการจัดเก็บข้อมูลเพิ่มเติมจำเป็นต้องใช้ประเภทหรือโครงสร้างอื่น

เริ่มต้นสองครั้งแรกเป็น 0 และสองตัวที่สองเป็นขนาดเริ่มต้นของแอปพลิเคชันของคุณและอันสุดท้ายเป็นเท็จ

สร้างตัวจัดการเหตุการณ์ Window_OnSourceInitialized และเพิ่มสิ่งต่อไปนี้:

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

หมายเหตุ:ตำแหน่งหน้าต่างที่ตั้งไว้จะต้องอยู่ในเหตุการณ์เริ่มต้นที่มาของหน้าต่างไม่ใช่ตัวสร้างมิฉะนั้นหากคุณมีการขยายหน้าต่างให้ใหญ่สุดบนจอภาพที่สองหน้าต่างนั้นจะเริ่มขยายขนาดใหญ่สุดบนจอภาพหลักเสมอและคุณจะไม่สามารถ เพื่อเข้าถึง

สร้างตัวจัดการเหตุการณ์ Window_Closing และเพิ่มสิ่งต่อไปนี้:

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

สิ่งนี้จะล้มเหลวหากผู้ใช้ทำให้พื้นที่แสดงผลเล็กลงไม่ว่าจะโดยการยกเลิกการเชื่อมต่อหน้าจอหรือเปลี่ยนความละเอียดของหน้าจอในขณะที่แอปพลิเคชันปิดอยู่ดังนั้นคุณควรเพิ่มการตรวจสอบว่าตำแหน่งและขนาดที่ต้องการยังคงถูกต้องก่อนที่จะใช้ค่า


5
จริงๆแล้วการตั้งค่าที่มีขอบเขต "User" จะไม่ถูกบันทึกไว้ในไฟล์ app.config ใน Program Files แต่อยู่ในไฟล์ user.config ในไดเร็กทอรีข้อมูลแอปพลิเคชันของผู้ใช้ ดังนั้นจึงไม่ใช่ปัญหา ...
Thomas Levesque

7
จริงๆแล้วคุณสามารถเพิ่ม "WindowState" ในการตั้งค่าได้ เลือกประเภท -> เรียกดู -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka

2
FWIW ฉันทำสิ่งนี้จากตัวจัดการที่เปลี่ยนขนาดเช่นกันในกรณีที่แอปพลิเคชันล่ม พวกมันหายากด้วยการประมวลผลข้อยกเว้นที่ไม่สามารถจัดการได้ แต่ทำไมต้องลงโทษผู้ใช้ด้วยขนาด / ตำแหน่งที่หายไปเมื่อพวกมันเกิดขึ้นอย่างลึกลับ
Thomas

7
มีข้อผิดพลาดในรหัสนี้หากผู้ใช้เปิดหน้าต่างบนหน้าจอที่สองจากนั้นให้ตัดการเชื่อมต่อหน้าจอนั้นออกจากคอมพิวเตอร์ในครั้งถัดไปที่เปิดหน้าต่างจะปรากฏขึ้นนอกหน้าจอ หากหน้าต่างเป็นโมดอลผู้ใช้จะไม่สามารถโต้ตอบกับแอปได้เลยและจะไม่เข้าใจว่าเกิดอะไรขึ้น คุณต้องเพิ่มการตรวจสอบขอบเขตโดยใช้ Window.GetScreen () หลังจากแปลงพิกัดหน้าจอเป็นค่าที่ขึ้นกับ DPI
Omer Raviv

2
@OmerRaviv - ไม่ใช่ข้อบกพร่อง แต่เป็นข้อ จำกัด :) อย่างจริงจัง - ฉันไม่ได้แก้ไขปัญหาดังกล่าว
ChrisF

74

จริงๆแล้วคุณไม่จำเป็นต้องใช้โค้ดข้างหลังในการทำเช่นนั้น (ยกเว้นการบันทึกการตั้งค่า) คุณสามารถใช้ส่วนขยายมาร์กอัปที่กำหนดเองเพื่อผูกขนาดหน้าต่างและตำแหน่งกับการตั้งค่าดังนี้:

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

คุณสามารถค้นหาโค้ดสำหรับส่วนขยายมาร์กอัปนี้ได้ที่นี่: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/


4
ฉันชอบคำตอบนี้มากกว่าคำตอบที่ได้รับการยอมรับ ทำได้ดี.
moswald

6
+1 - ฉันชอบใช้การผูกและส่วนขยาย! หากคุณเพิ่ม WindowState ในการตั้งค่าที่ผูกไว้ของคุณจะมีความสามารถเต็มรูปแบบ หรือหากคุณมีการตั้งค่าผู้ใช้ใน DataContext คุณสามารถใช้สิ่งต่างๆเช่น{Binding Settings.Height}ฯลฯ
Matt DeKrey

แนวทางนี้มีปัญหาเมื่อผู้ใช้ปิดแอปพลิเคชันเมื่อหน้าต่างถูกขยายใหญ่สุด
Vinicius Rocha

@ วินิซิอุสอธิบายละเอียดได้ไหม? ปัญหาคืออะไรกันแน่?
Thomas Levesque

4
แล้วเมื่อใดที่ผู้คนมีจอภาพสองจอและอาจมีพิกัดเชิงลบจากนั้นพวกเขาเปลี่ยนการกำหนดค่าจอภาพและค่าจะไม่ถูกต้องอีกต่อไป
Andrew Truckle

33

ในขณะที่คุณสามารถ "หมุนของคุณเอง" และบันทึกการตั้งค่าได้ด้วยตนเองที่ใดที่หนึ่งและโดยทั่วไปแล้วจะใช้งานได้ แต่ก็เป็นเรื่องง่ายมากที่จะไม่จัดการทุกกรณีอย่างถูกต้อง จะดีกว่ามากถ้าปล่อยให้ระบบปฏิบัติการทำงานแทนคุณโดยเรียกGetWindowPlacement ()ที่ exit และSetWindowPlacement ()เมื่อเริ่มต้น จัดการกรณีขอบที่บ้าคลั่งทั้งหมดที่อาจเกิดขึ้น (จอภาพหลายจอบันทึกขนาดปกติของหน้าต่างหากปิดขณะขยายใหญ่สุด ฯลฯ ) เพื่อที่คุณจะได้ไม่ต้องทำ

ตัวอย่าง MSDN นี้แสดงวิธีใช้สิ่งเหล่านี้กับแอป WPF ตัวอย่างไม่สมบูรณ์แบบ (หน้าต่างจะเริ่มต้นที่มุมซ้ายบนให้เล็กที่สุดในการรันครั้งแรกและมีพฤติกรรมแปลก ๆ ที่ตัวออกแบบการตั้งค่าบันทึกค่าประเภทWINDOWPLACEMENT) แต่อย่างน้อยก็ควรช่วยให้คุณเริ่มต้นได้


ทางออกที่ดี อย่างไรก็ตามฉันเพิ่งค้นพบว่า GetWindowPlacement / SetWindowPlacement ไม่ใช่ Aero Snap ที่ทราบ
Mark Bell

1
@RandomEngy ได้โพสต์คำตอบที่ปรับปรุงตามนี้
Stéphane Gourichon

27

การผูก "แบบยาว" ที่ Thomas โพสต์ไว้ข้างต้นแทบไม่ต้องใช้การเข้ารหัสเลยเพียงตรวจสอบให้แน่ใจว่าคุณมีการผูกเนมสเปซ:

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

จากนั้นเพื่อบันทึกรหัสหลัง:

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

ฉันเลือกวิธีนี้ แต่จะบันทึกการตั้งค่าเฉพาะในกรณีที่สถานะหน้าต่างเป็นปกติมิฉะนั้นอาจทำให้มันออกจากโหมดขยายใหญ่สุด
David Sykes

7
+1 ฉันก็ใช้สิ่งนี้เช่นกัน @DavidSykes - การเพิ่มการตั้งค่าอื่นสำหรับสถานะหน้าต่างดูเหมือนจะทำงานได้ดีเช่นWindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson

@RobJohnson ฉันลองทำตามคำแนะนำของคุณแล้วและมันได้ผลดีมากขอบคุณ
David Sykes

4

หรือคุณอาจชอบแนวทางต่อไปนี้ด้วย ( ดูแหล่งที่มา ) เพิ่มคลาส WindowSettings ในโครงการของคุณและแทรกWindowSettings.Save="True"ในส่วนหัวของหน้าต่างหลักของคุณ:

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

โดยที่ WindowSettings ถูกกำหนดดังนี้:

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

วิธีแก้ปัญหาเริ่มต้นคือการใช้ไฟล์การตั้งค่า ปัญหาเกี่ยวกับไฟล์การตั้งค่าคือคุณต้องกำหนดการตั้งค่าทั้งหมดและเขียนโค้ดที่คัดลอกข้อมูลไปมาด้วยตัวเอง ค่อนข้างน่าเบื่อหากคุณมีคุณสมบัติมากมายให้ติดตาม

ฉันสร้างไลบรารีที่ยืดหยุ่นและใช้งานง่ายมากสำหรับสิ่งนี้คุณเพียงแค่บอกมันว่าคุณสมบัติของวัตถุใดที่จะติดตามและมันจะทำส่วนที่เหลือ คุณสามารถกำหนดค่าอึออกมาได้เช่นกันหากต้องการ

ห้องสมุดเรียกว่าJot (github)นี่คือบทความ CodeProjectเก่าที่ฉันเขียนเกี่ยวกับเรื่องนี้

นี่คือวิธีที่คุณจะใช้เพื่อติดตามขนาดและตำแหน่งของหน้าต่าง:

public MainWindow()
{
    InitializeComponent();

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

Jot เทียบกับการตั้งค่าไฟล์:ด้วย Jot มีรหัสมากน้อยและมันมากน้อยข้อผิดพลาดง่ายเนื่องจากคุณจะต้องพูดถึงสถานที่ให้บริการในแต่ละครั้งเดียว ด้วยไฟล์การตั้งค่าคุณต้องพูดถึงคุณสมบัติแต่ละรายการ5 ครั้ง : หนึ่งครั้งเมื่อคุณสร้างคุณสมบัติอย่างชัดเจนและอีกสี่ครั้งในรหัสที่คัดลอกค่าไปมา

การจัดเก็บการทำให้เป็นอนุกรม ฯลฯ สามารถกำหนดค่าได้อย่างสมบูรณ์ นอกจากนี้เมื่อใช้ IOC คุณยังสามารถเชื่อมต่อเพื่อให้ใช้การติดตามโดยอัตโนมัติกับวัตถุทั้งหมดที่แก้ไขได้ดังนั้นสิ่งที่คุณต้องทำเพื่อให้คุณสมบัติคงอยู่คือการตบแอตทริบิวต์ [ติดตามได้]

ฉันเขียนทั้งหมดนี้เพราะฉันคิดว่าห้องสมุดเป็นอันดับต้น ๆ และฉันอยากจะพูดถึงเรื่องนี้


ดีมากขอบคุณสำหรับสิ่งนี้ - ฉันได้ใช้ข้อมูลโค้ดของคุณในคลาสใหม่เพื่อตั้งค่าตัวติดตามสถานะด้วยพา ธ ตามชื่อโปรแกรม จากนี้ไปฉันต้องเขียนเพียงบรรทัดเดียวและคุณสมบัติทั้งหมดของหน้าต่างจะถูกจัดการ
Awesomeness

1

ฉันเขียนคลาสด่วนซึ่งทำสิ่งนี้ นี่คือวิธีที่เรียกว่า:

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

และนี่คือรหัส:

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

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

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


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

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

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

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

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

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

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

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

window.Intitialized ควรเป็น window โหลดดูmosttech.blogspot.com/2008/01/…
Gleb Sevruk

@Gleb ทั้งสองงานฉันคิดว่า. คุณประสบปัญหากับ Initialized หรือไม่?
tster

ใช่เนื่องจากหน้าต่างขยายใหญ่สุดจะอยู่บนหน้าจอที่ไม่ถูกต้องหากคุณใช้เฉพาะเหตุการณ์เริ่มต้น สิ่งที่ฉันทำและดูเหมือนว่าจะได้ผล: ตอนนี้ฉันสมัครรับกิจกรรม Loaded ด้วย ฉันย้าย _window.WindowState = s. Maximized? WindowState.Maximized: WindowState.Normal; บรรทัดภายในตัวจัดการเหตุการณ์ "โหลดแล้ว" windowInitialized + = InitializedHandler; window.Loaded + = LoadedHandler; btw: ฉันชอบแนวทางนี้
Gleb Sevruk

1

มีNuGet Project RestoreWindowPlace ที่ เห็นบนgithubที่ทำสิ่งนี้ให้คุณทั้งหมดโดยบันทึกข้อมูลในไฟล์ XML

ในการทำให้มันทำงานบนหน้าต่างทำได้ง่ายๆเพียงแค่โทร:

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

ในแอพคุณสร้างคลาสที่จัดการหน้าต่างของคุณ ดูลิงก์ github ด้านบนสำหรับข้อมูลเพิ่มเติม


0

คุณอาจชอบสิ่งนี้:

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

เมื่อแอปปิด:

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

เมื่อแอปเริ่มทำงาน:

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

0

สร้างสตริงชื่อ WindowXml ในการตั้งค่าเริ่มต้นของคุณ

ใช้วิธีการขยายนี้ในเหตุการณ์ที่โหลดและปิดหน้าต่างของคุณเพื่อกู้คืนและบันทึกขนาดหน้าต่างและตำแหน่ง

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

ฉันใช้คำตอบจาก Lance Cleveland และผูกการตั้งค่า แต่ฉันใช้โค้ดมากกว่านี้เพื่อหลีกเลี่ยงไม่ให้หน้าต่างของฉันอยู่นอกหน้าจอ

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

ฉันสร้างโซลูชันทั่วไปขึ้นโดยใช้คำตอบที่ยอดเยี่ยมของ RandomEngys จะบันทึกตำแหน่งลงในโฟลเดอร์ที่ทำงานอยู่และคุณไม่จำเป็นต้องสร้างคุณสมบัติใหม่สำหรับแต่ละหน้าต่างใหม่ที่คุณสร้างขึ้น การละลายนี้ใช้งานได้ดีสำหรับฉันโดยมีโค้ดน้อยที่สุดอยู่ข้างหลัง

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

    }
}

ในโค้ดของคุณด้านหลังคุณเพิ่มสองวิธีนี้

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

ในหน้าต่าง xaml คุณเพิ่มสิ่งนี้

Closing="ClosingTrigger"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.