.NET WPF Ghi kích thước cửa sổ giữa các phiên


93

Về cơ bản, khi người dùng thay đổi kích thước cửa sổ ứng dụng của tôi, tôi muốn ứng dụng có cùng kích thước khi ứng dụng được mở lại.

Lúc đầu, tôi mặc dù xử lý sự kiện SizeChanged và lưu Chiều cao và Chiều rộng, nhưng tôi nghĩ phải có giải pháp dễ dàng hơn.

Vấn đề khá đơn giản, nhưng tôi không thể tìm ra giải pháp dễ dàng cho nó.


2
Xin lưu ý rằng nếu bạn đang cộng hưởng cả kích thước và vị trí (như hầu hết các mẫu mã bên dưới), bạn sẽ muốn xử lý trường hợp cạnh mà ai đó đã rút phích cắm màn hình mà cửa sổ được hiển thị lần cuối, để tránh trình bày cửa sổ ngoài màn hình.
Omer Raviv

@OmerRaviv Bạn đã tìm thấy một ví dụ nào về trường hợp nguy hiểm chưa?
Andrew Truckle

Tôi không có quá nhiều sự thận trọng để thêm một bình luận, do đó tôi đã tạo ra chiếc awnser mới này. Tôi sử dụng giải pháp tương tự như Lance Cleveland bao gồm cài đặt của RobJohnson , nhưng nó không hoạt động nếu bạn sử dụng nó cho các cửa sổ phụ và muốn mở thêm chúng cùng lúc ...
AelanY

Câu trả lời:


121

Lưu các giá trị trong tệp user.config.

Bạn sẽ cần tạo giá trị trong tệp cài đặt - giá trị đó phải nằm trong thư mục Thuộc tính. Tạo năm giá trị:

  • Top thuộc loại double
  • Left thuộc loại double
  • Height thuộc loại double
  • Width thuộc loại double
  • Maximizedloại bool- để giữ xem cửa sổ có được phóng to hay không. Nếu bạn muốn lưu trữ thêm thông tin thì sẽ cần một kiểu hoặc cấu trúc khác.

Khởi tạo hai cái đầu tiên thành 0 và hai cái thứ hai thành kích thước mặc định của ứng dụng của bạn và cái cuối cùng thành false.

Tạo trình xử lý sự kiện Window_OnSourceInitialized và thêm phần sau:

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

GHI CHÚ: Vị trí đặt cửa sổ cần phải đi vào sự kiện khởi tạo trên nguồn của cửa sổ không phải là hàm tạo, nếu không nếu bạn đã phóng to cửa sổ trên màn hình thứ hai, nó sẽ luôn khởi động lại với mức tối đa trên màn hình chính và bạn sẽ không thể để truy cập nó.

Tạo trình xử lý sự kiện Window_Closing và thêm phần sau:

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

Điều này sẽ không thành công nếu người dùng làm cho vùng hiển thị nhỏ hơn - bằng cách ngắt kết nối màn hình hoặc thay đổi độ phân giải màn hình - trong khi ứng dụng đang đóng, vì vậy bạn nên thêm kiểm tra xem vị trí và kích thước mong muốn có còn hợp lệ không trước khi áp dụng các giá trị.


5
Trên thực tế, cài đặt có phạm vi "Người dùng" không được lưu trong tệp app.config trong Tệp chương trình, mà trong tệp user.config trong thư mục dữ liệu ứng dụng của người dùng. Vì vậy, nó không phải là một vấn đề ...
Thomas Levesque

7
Trên thực tế, bạn có thể thêm "WindowState" vào cài đặt. Chọn loại -> duyệt -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka

2
FWIW, tôi cũng làm điều này từ trình xử lý thay đổi kích thước, trong trường hợp ứng dụng bị treo. Chúng hiếm khi xảy ra quá trình xử lý ngoại lệ không được xử lý, nhưng tại sao lại trừng phạt người dùng với kích thước / vị trí bị mất khi chúng xảy ra một cách bí ẩn.
Thomas

7
Có một lỗi trong mã này ở chỗ, nếu người dùng mở cửa sổ trên màn hình thứ hai của họ, sau đó ngắt kết nối màn hình đó khỏi máy tính, lần sau khi họ mở cửa sổ, nó sẽ hiển thị ngoài màn hình. Nếu cửa sổ là phương thức, người dùng sẽ không thể tương tác với ứng dụng và sẽ không hiểu chuyện gì đang xảy ra. Bạn cần thêm kiểm tra giới hạn bằng Window.GetScreen (), sau khi chuyển đổi tọa độ màn hình thành các giá trị phụ thuộc DPI.
Omer Raviv

2
@OmerRaviv - đó không phải là một lỗi, mà là một hạn chế :) Nghiêm túc - Tôi đã không giải quyết khía cạnh đó của vấn đề.
ChrisF

73

Trên thực tế, bạn không cần phải sử dụng mã phía sau để làm điều đó (ngoại trừ việc lưu cài đặt). Bạn có thể sử dụng tiện ích mở rộng đánh dấu tùy chỉnh để ràng buộc kích thước và vị trí cửa sổ với các cài đặt như sau:

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

Bạn có thể tìm thấy mã cho tiện ích mở rộng đánh dấu này tại đây: http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/


4
Tôi thích câu trả lời này hơn câu trả lời được chấp nhận đã chọn. Làm tốt.
moswald

6
+1 - Tôi thích sử dụng ràng buộc và tiện ích mở rộng! Nếu bạn thêm WindowState vào cài đặt liên kết của mình, nó sẽ cung cấp đầy đủ các tính năng. Ngoài ra, nếu bạn có thiết lập người dùng có sẵn trong DataContext, bạn có thể sử dụng giống như {Binding Settings.Height}vv
Matt DeKrey

Cách tiếp cận này gặp sự cố khi người dùng đóng ứng dụng khi Cửa sổ được phóng to.
Vinicius Rocha

@Vinicius, bạn có thể nói rõ hơn không? Vấn đề chính xác là gì?
Thomas Levesque

4
Còn khi mọi người có hai màn hình và do đó nó có thể có tọa độ âm và sau đó họ thay đổi cấu hình màn hình và các giá trị không còn hợp lệ nữa?
Andrew Truckle

33

Mặc dù bạn có thể tự "cuộn" và lưu cài đặt theo cách thủ công ở đâu đó, và nói chung là nó sẽ hoạt động, nhưng rất dễ xảy ra nếu không xử lý đúng tất cả các trường hợp. Sẽ tốt hơn nhiều nếu để HĐH thực hiện công việc cho bạn, bằng cách gọi GetWindowPlacement () khi thoát và SetWindowPlacement () khi khởi động. Nó xử lý tất cả các trường hợp cạnh điên rồ có thể xảy ra (nhiều màn hình, lưu kích thước bình thường của cửa sổ nếu nó được đóng trong khi mở rộng tối đa, v.v.) để bạn không phải làm vậy.

Mẫu MSDN này cho thấy cách sử dụng chúng với ứng dụng WPF. Mẫu không hoàn hảo (cửa sổ sẽ bắt đầu ở góc trên bên trái càng nhỏ càng tốt trong lần chạy đầu tiên và có một số hành vi kỳ lạ với trình thiết kế Cài đặt lưu giá trị của loại WINDOWPLACEMENT), nhưng ít nhất nó sẽ giúp bạn bắt đầu.


Giải pháp tốt. Tuy nhiên, tôi vừa phát hiện ra rằng GetWindowPlacement / SetWindowPlacement không nhận biết được Aero Snap
Mark Bell

1
@RandomEngy đã đăng một câu trả lời được cải thiện dựa trên điều này.
Stéphane Gourichon

27

Ràng buộc "dạng dài" mà Thomas đã đăng ở trên hầu như không yêu cầu mã hóa, chỉ cần đảm bảo bạn có ràng buộc vùng tên:

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

Sau đó, để lưu mã phía sau:

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

Tôi chọn giải pháp này, nhưng chỉ lưu các thiết lập nếu tình trạng cửa sổ là bình thường, nếu không nó có thể được khó sử dụng nhận được nó ra khỏi chế độ tối đa hóa
David Sykes

7
1 Tôi sử dụng này quá, @DavidSykes - Thêm một thiết lập cho trạng thái cửa sổ dường như làm việc tốt đủ, ví dụ nhưWindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson

@RobJohnson Tôi đã thử đề xuất của bạn và nó hoạt động rất tốt, cảm ơn.
David Sykes

4

Ngoài ra, bạn cũng có thể thích cách tiếp cận sau ( xem nguồn ). Thêm lớp WindowSettings vào dự án của bạn và chèn WindowSettings.Save="True"vào tiêu đề cửa sổ chính của bạn:

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

Trong đó WindowSettings được định nghĩa như sau:

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

Cách giải quyết mặc định là sử dụng các tệp cài đặt. Vấn đề với tệp cài đặt là bạn phải xác định tất cả cài đặt và tự viết mã sao chép dữ liệu qua lại. Khá tẻ nhạt nếu bạn có rất nhiều tài sản để theo dõi.

Tôi đã tạo một thư viện khá linh hoạt và rất dễ sử dụng cho việc này, bạn chỉ cần cho nó biết thuộc tính nào của đối tượng nào cần theo dõi và nó thực hiện phần còn lại. Bạn cũng có thể định cấu hình nó nếu bạn muốn.

Thư viện có tên là Jot (github) , đây là một bài viết CodeProject mà tôi đã viết về nó.

Đây là cách bạn sử dụng nó để theo dõi kích thước và vị trí của cửa sổ:

public MainWindow()
{
    InitializeComponent();

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

Jot so với các tập tin cài đặt: Với Jot, có ít mã hơn đáng kể và nó ít bị lỗi hơn rất nhiều vì bạn chỉ cần đề cập đến từng thuộc tính một lần . Với các tệp cài đặt, bạn cần đề cập đến từng thuộc tính 5 lần : một lần khi bạn tạo thuộc tính rõ ràng và thêm bốn lần trong mã sao chép các giá trị qua lại.

Lưu trữ, tuần tự hóa, v.v. hoàn toàn có thể định cấu hình. Ngoài ra, khi sử dụng IOC, bạn thậm chí có thể kết nối nó để nó áp dụng theo dõi tự động cho tất cả các đối tượng mà nó giải quyết để tất cả những gì bạn cần làm để làm cho thuộc tính trở nên bền bỉ là gắn thuộc tính [Trackable] vào nó.

Tôi đang viết tất cả những điều này bởi vì tôi nghĩ rằng thư viện là hàng đầu và tôi muốn nói về nó.


Rất vui, cảm ơn vì điều này - Tôi đã sử dụng đoạn mã của bạn trong một lớp mới để thiết lập trình theo dõi trạng thái với đường dẫn dựa trên tên của chương trình. Từ giờ trở đi tôi chỉ phải viết một dòng và tất cả các thuộc tính cửa sổ được xử lý
khiếp sợ

1

Tôi đã viết một lớp học nhanh để thực hiện điều này. Đây là cách nó được gọi:

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

Và đây là mã:

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 nên window.Loaded thấy mostlytech.blogspot.com/2008/01/...
Gleb Sevruk

@Gleb, tôi nghĩ cả hai đều hoạt động. Bạn đang gặp sự cố với nó khi Khởi tạo?
tster

Có, vì cửa sổ được phóng to sẽ hiển thị trên màn hình không chính xác nếu bạn chỉ sử dụng sự kiện đã khởi tạo. Những gì tôi đã làm và điều này dường như hoạt động: Bây giờ tôi cũng đang đăng ký sự kiện Đã tải. Tôi đã chuyển _window.WindowState = s.Maximized? WindowState.Maximized: WindowState.Normal; dòng bên trong trình xử lý sự kiện "Đã tải". window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; btw: Tôi thích cách tiếp cận này
Gleb Sevruk

1

Có một NuGet Project RestoreWindowPlace xem trên github thực hiện tất cả những điều này cho bạn, lưu thông tin vào một tệp XML.

Để làm cho nó hoạt động trên một cửa sổ, bạn chỉ cần gọi:

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

Trong Ứng dụng, bạn tạo lớp quản lý các cửa sổ của mình. Xem liên kết github ở trên để biết thêm thông tin.


0

Bạn có thể thích điều này:

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

Khi ứng dụng đóng:

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

Khi ứng dụng khởi động:

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

0

Tạo một chuỗi có tên WindowXml trong Cài đặt mặc định của bạn.

Sử dụng phương pháp mở rộng này trên các sự kiện Đã tải và Đóng Cửa sổ của bạn để khôi phục và lưu kích thước và vị trí Cửa sổ.

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

Tôi đang sử dụng câu trả lời từ Lance Cleveland và ràng buộc Cài đặt. Nhưng tôi đang sử dụng thêm một số Mã để tránh việc Cửa sổ của tôi ở ngoài Màn hình.

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

Tôi đã đưa ra một giải pháp chung chung hơn dựa trên câu trả lời tuyệt vời của RandomEngys. Nó lưu vị trí của tệp trong thư mục đang chạy và bạn không cần tạo thuộc tính mới cho mỗi cửa sổ mới mà bạn tạo. Sollution này hoạt động tuyệt vời đối với tôi với mã tối thiểu phía sau.

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

    }
}

Trong mã của bạn đằng sau, bạn thêm hai phương pháp này

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

trong cửa sổ xaml bạn thêm cái này

Closing="ClosingTrigger"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.