.NET WPF Запам'ятайте розмір вікна між сесіями


93

В основному, коли користувач змінює розмір вікна моєї програми, я хочу, щоб програма мала однаковий розмір при повторному відкритті програми.

Спочатку я хоч обробляв подію SizeChanged і зберігав висоту та ширину, але я думаю, що рішення має бути простішим.

Досить проста проблема, але я не можу знайти простого рішення.


2
Зверніть увагу, що якщо ви зберігаєте розмір і позицію (як це робить більшість зразків коду нижче), вам потрібно буде обробляти край, де хтось відключив монітор, на якому востаннє було представлено вікно, щоб уникнути подання вашого вікно поза екраном.
Омер Равів,

@OmerRaviv Ви знайшли приклад, який враховує крайовий випадок?
Andrew Truckle

У мене занадто менша репутація, щоб додати коментар, отже, я створив цей новий інструмент. Я використовую те саме рішення, що і Ленс Клівленд, включаючи налаштування Роб Джонсона , але воно не працює, якщо ви використовуєте його для підвікон і хочете відкрити більше з них одночасно ...
AelanY

Відповіді:


121

Збережіть значення у файлі user.config.

Вам потрібно буде створити значення у файлі налаштувань - воно повинно бути в папці Властивості. Створіть п’ять значень:

  • Top типу double
  • Left типу double
  • Height типу double
  • Width типу double
  • Maximized типу bool - щоб утримувати розгорнене вікно чи ні. Якщо ви хочете зберегти більше інформації, тоді потрібен інший тип або структура.

Ініціалізуйте перші два до 0, а другі два - до розміру програми за замовчуванням, а останній - до false.

Створіть обробник події 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
Насправді налаштування з обсягом "Користувач" зберігаються не у файлі app.config у програмних файлах, а у файлі user.config у каталозі даних програми користувача. Тож це не проблема ...
Томас Левеск

7
Фактично ви можете додати "WindowState" до налаштувань. Виберіть тип -> перегляд -> PresentationFramework -> System.Windows -> WindowState :)
Martin Vseticka

2
FWIW, я роблю це також із обробника розміру, на випадок збою програми. Вони рідко зустрічаються з необробленою обробкою винятків, але навіщо карати користувача втраченим розміром / місцем розташування, коли це відбувається загадково.
Томас

7
У цьому коді є помилка в тому, що якщо користувач відкриє вікно на своєму другому екрані, а потім від’єднає цей екран від комп’ютера, наступного разу, коли вони відкриють вікно, воно буде представлено поза екраном. Якщо вікно модальне, користувач взагалі не зможе взаємодіяти з додатком і не зрозуміє, що відбувається. Вам потрібно додати перевірку меж за допомогою Window.GetScreen (), після перетворення координат екрана у значення, що залежать від DPI.
Омер Равів,

2
@OmerRaviv - це не помилка, а обмеження :) Серйозно - я не звертався до цього аспекту проблеми.
ChrisF

73

Насправді для цього не потрібно використовувати кодовий код (за винятком збереження налаштувань). Ви можете використовувати власне розширення розмітки, щоб прив’язати розмір і положення вікна до таких налаштувань:

<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

@Vinicius, ти можеш пояснити? У чому саме проблема?
Thomas Levesque,

4
Що можна сказати про те, коли люди мають два монітори, і, отже, вони можуть мати від’ємні координати, а потім вони змінюють конфігурації монітора, і значення більше не дійсні?
Ендрю Truckle

33

Незважаючи на те, що ви можете «прокрутити власні» і десь зберегти налаштування вручну, і загалом це буде працювати, дуже легко не правильно розглянути всі справи. Набагато краще дозволити ОС виконувати роботу за вас, викликаючи GetWindowPlacement () при виході та SetWindowPlacement () при запуску. Він обробляє всі божевільні випадки краю, які можуть трапитися (кілька моніторів, збережіть нормальний розмір вікна, якщо воно закрите, якщо воно розгорнуте тощо), так що вам не потрібно.

Цей зразок MSDN показує, як використовувати їх із програмою WPF. Зразок не ідеальний (вікно почнеться у верхньому лівому куті якнайменше при першому запуску, і існує деяка дивна поведінка, коли дизайнер налаштувань зберігає значення типу WINDOWPLACEMENT), але це, принаймні, повинно розпочати.


Гарне рішення. Однак я щойно виявив, що GetWindowPlacement / SetWindowPlacement не знають Aero Snap
Марк Белл

1
На основі цього @RandomEngy опублікував покращену відповідь.
Стефан Гурішон,

27

Прив'язка "довгої форми", яку Томас розмістив вище, майже не вимагає кодування, просто переконайтеся, що у вас є прив'язка простору імен:

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

Я вибрав це рішення, але зберег налаштування лише в тому випадку, якщо стан вікна був нормальним, інакше це можна хитро виводити з режиму максимізації
Девід Сайкс

7
+1 Я теж використовував це, @DavidSykes - Додавання іншого налаштування стану вікна, здається, працює досить добре, наприкладWindowState="{Binding Source={x:Static properties:Settings.Default}, Path=WindowState, Mode=TwoWay}"
RobJohnson

@RobJohnson Я спробував вашу пропозицію, і вона спрацювала дуже добре, дякую.
Девід Сайкс

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, ви навіть можете підключити його, щоб він автоматично застосовував відстеження до всіх об'єктів, які він вирішує, так що все, що вам потрібно зробити, щоб зробити властивість стійким, - це ляпати атрибут [Trackable].

Я пишу все це, тому що вважаю, що бібліотека на високому рівні, і я хочу про це сказати.


Приємно, дякую за це - я використав ваш фрагмент коду в новому класі, щоб налаштувати відстежувач стану із шляхом на основі назви програми. Відтепер мені залишається писати лише один рядок, і всі властивості вікна обробляються
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.Завантажений див. в основному
Гліб Севрук

@Gleb, думаю, обидва працюють. У вас проблеми з цим на Initialized?
tster

Так, оскільки розгорнуте вікно буде на неправильному екрані, якщо ви використовуєте лише ініціалізовану подію. Те, що я зробив, і це, здається, працює: Зараз я також підписуюсь на Loaded event. Я перемістив _window.WindowState = s.Maximized? WindowState.Maximized: WindowState.Normal; рядок всередині обробника подій "Loaded". window.Initialized + = InitializedHandler; window.Loaded + = LoadedHandler; до речі: Мені подобається такий підхід
Гліб Севрук

1

Там в NuGet проекту RestoreWindowPlace побачити на GitHub , що робить все це для вас, зберігаючи інформацію в файлі XML.

Щоб змусити його працювати у вікні, це просто, як дзвінок:

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

В App ви створюєте клас, який керує вашими вікнами. Для отримання додаткової інформації див. Посилання на 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"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.