Как записать положение окна в настройках приложения Windows Forms

StackOverflow https://stackoverflow.com/questions/105932

  •  01-07-2019
  •  | 
  •  

Вопрос

Это похоже на стандартное требование:в следующий раз, когда пользователь запустит приложение, откройте окно в том же положении и состоянии, что и раньше.Вот мой список желаний:

  • Положение окна такое же, как и было
    • Если только размер экрана не изменился и старая позиция теперь не на экране.
  • Разделители должны сохранять свое положение
  • Контейнеры вкладок должны сохранять свой выбор
  • Некоторые выпадающие списки должны сохранить свой выбор
  • Состояние окна (максимизировать, минимизировать, нормальное) такое же, как и было.
    • Может быть, тебе никогда не стоит начинать сворачивать, я еще не решил.

Я добавлю свои текущие решения в качестве ответа вместе с ограничениями.

Это было полезно?

Решение

Мой другой вариант - написать больше пользовательского кода вокруг настроек приложения и выполнить его в FormLoad и FormClosed.При этом не используется привязка данных.

Недостатки:

  • Нужно написать еще больше кода.
  • Очень неудобно.Порядок, в котором вы устанавливаете свойства в FormLoad, сбивает с толку.Например, вы должны убедиться, что вы установили размер окна, прежде чем устанавливать расстояние между разделителями.

Прямо сейчас это мое предпочтительное решение, но мне кажется, что это слишком много работы.Чтобы сократить объем работы, я создал класс WindowSettings, который сериализует расположение окна, размер, состояние и любые позиции разделителей для одного параметра приложения.Затем я могу просто создать настройку этого типа для каждой формы в моем приложении, сохранить при закрытии и восстановить при загрузке.

Я опубликовал исходный код, включая класс WindowSettings и некоторые формы, которые его используют.Инструкции по добавлению его в проект содержатся в файле WindowSettings.cs.Самой сложной частью было выяснить, как добавить параметр приложения с пользовательским типом.Вы выбираете "Просмотреть"...из выпадающего списка тип, а затем вручную введите пространство имен и имя класса.Типы из вашего проекта не отображаются в списке.

Обновить: Я добавил несколько статических методов, чтобы упростить шаблонный код, который вы добавляете к каждой форме.После того как вы выполнили инструкции по добавлению класса WindowSettings в свой проект и созданию настроек приложения, вот пример кода, который необходимо добавить к каждой форме, положение которой вы хотите записать и восстановить.

    private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        Settings.Default.CustomWindowSettings = WindowSettings.Record(
            Settings.Default.CustomWindowSettings,
            this, 
            splitContainer1);
    }

    private void MyForm_Load(object sender, EventArgs e)
    {
        WindowSettings.Restore(
            Settings.Default.CustomWindowSettings, 
            this, 
            splitContainer1);
    }

Другие советы

Приведенный ниже пример показывает, как я это делаю

  • SavePreferences вызывается при закрытии формы и сохраняет размер формы, а также флаг, указывающий, увеличен ли он (в этой версии я не сохраняю, если он свернут - в следующий раз он будет восстановлен или развернут).

  • LoadPreferences вызывается из OnLoad.

  • Сначала сохраните WindowState времени разработки и установите для него значение Normal.Вы можете успешно установить размер формы только в том случае, если ее WindowState является нормальным.

  • Затем восстановите Размер из ваших сохраненных настроек.

  • Теперь убедитесь, что форма помещается на вашем экране (вызовите FitToScreen).Возможно, разрешение экрана изменилось с момента вашего последнего запуска приложения.

  • Наконец, верните WindowState обратно в значение Maximized (если оно сохранено как таковое) или в значение времени разработки, сохраненное ранее.

Очевидно, это можно было бы адаптировать для сохранения начальной позиции и того, была ли форма свернута при закрытии - мне не нужно было этого делать.Другие настройки для элементов управления в вашей форме, такие как положение разделителя и контейнер вкладок, просты.

private void FitToScreen()
{
    if (this.Width > Screen.PrimaryScreen.WorkingArea.Width)
    {
        this.Width = Screen.PrimaryScreen.WorkingArea.Width;
    }
    if (this.Height > Screen.PrimaryScreen.WorkingArea.Height)
    {
        this.Height = Screen.PrimaryScreen.WorkingArea.Height;
    }
}   
private void LoadPreferences()
{
    // Called from Form.OnLoad

    // Remember the initial window state and set it to Normal before sizing the form
    FormWindowState initialWindowState = this.WindowState;
    this.WindowState = FormWindowState.Normal;
    this.Size = UserPreferencesManager.LoadSetting("_Size", this.Size);
    _currentFormSize = Size;
    // Fit to the current screen size in case the screen resolution
    // has changed since the size was last persisted.
    FitToScreen();
    bool isMaximized = UserPreferencesManager.LoadSetting("_Max", initialWindowState == FormWindowState.Maximized);
    WindowState = isMaximized ? FormWindowState.Maximized : FormWindowState.Normal;
}
private void SavePreferences()
{
    // Called from Form.OnClosed
    UserPreferencesManager.SaveSetting("_Size", _currentFormSize);
    UserPreferencesManager.SaveSetting("_Max", this.WindowState == FormWindowState.Maximized);
    ... save other settings
}

x

Самое простое решение, которое я нашел, - это использовать привязку данных к настройкам приложения.Я привязываю свойства location и ClientSize к окну вместе с splitterDistance к разделителю.

Недостатки:

  • Если вы закроете окно в свернутом виде, в следующий раз оно откроется скрытым.Вернуть окно обратно действительно трудно.
  • Если вы закроете окно в развернутом виде, оно откроется, заполняя весь экран, но не в развернутом виде (незначительная проблема).
  • Изменять размер окна, используя правый верхний или левый нижний угол, просто некрасиво.Я предполагаю, что два свойства, связанные с базой данных, борются друг с другом.

Если вы хотите поэкспериментировать со странным поведением, я опубликовал образец раствора используя эту технику.

Я создаю настройку для каждого значения, которое хочу сохранить, и использую следующий код:

private void MainForm_Load(object sender, EventArgs e) {
  RestoreState();
}

private void MainForm_FormClosing(object sender, FormClosingEventArgs e) {
  SaveState();
}

private void SaveState() {
  if (WindowState == FormWindowState.Normal) {
    Properties.Settings.Default.MainFormLocation = Location;
    Properties.Settings.Default.MainFormSize = Size;
  } else {
    Properties.Settings.Default.MainFormLocation = RestoreBounds.Location;
    Properties.Settings.Default.MainFormSize = RestoreBounds.Size;
  }
  Properties.Settings.Default.MainFormState = WindowState;
  Properties.Settings.Default.SplitterDistance = splitContainer1.SplitterDistance;
  Properties.Settings.Default.Save();
}

private void RestoreState() {
  if (Properties.Settings.Default.MainFormSize == new Size(0, 0)) {
    return; // state has never been saved
  }
  StartPosition = FormStartPosition.Manual;
  Location = Properties.Settings.Default.MainFormLocation;
  Size = Properties.Settings.Default.MainFormSize;
  // I don't like an app to be restored minimized, even if I closed it that way
  WindowState = Properties.Settings.Default.MainFormState == 
    FormWindowState.Minimized ? FormWindowState.Normal : Properties.Settings.Default.MainFormState;
  splitContainer1.SplitterDistance = Properties.Settings.Default.SplitterDistance;
}

Имейте в виду, что при перекомпиляции стирается конфигурационный файл, в котором хранятся настройки, поэтому протестируйте его, не внося изменений в код между сохранением и восстановлением.

Основываясь на принятом ответе Дона Киркби и классе WindowSettings, который он написал, вы могли бы вывести пользовательскую форму из стандартной, чтобы уменьшить количество идентичного кода, написанного для каждой формы, возможно, вот так:

using System;
using System.Configuration;
using System.Reflection;
using System.Windows.Forms;

namespace CustomForm
{
  public class MyCustomForm : Form
  {
    private ApplicationSettingsBase _appSettings = null;
    private string _settingName = "";

    public Form() : base() { }

    public Form(ApplicationSettingsBase settings, string settingName)
      : base()
    {
      _appSettings = settings;
      _settingName = settingName;

      this.Load += new EventHandler(Form_Load);
      this.FormClosing += new FormClosingEventHandler(Form_FormClosing);
    }

    private void Form_Load(object sender, EventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null) return;

      previousSettings.Restore(this);
    }

    private void Form_FormClosing(object sender, FormClosingEventArgs e)
    {
      if (_appSettings == null) return;

      PropertyInfo settingProperty = _appSettings.GetType().GetProperty(_settingName);
      if (settingProperty == null) return;

      WindowSettings previousSettings = settingProperty.GetValue(_appSettings, null) as WindowSettings;
      if (previousSettings == null)
        previousSettings = new WindowSettings();

      previousSettings.Record(this);

      settingProperty.SetValue(_appSettings, previousSettings, null);

      _appSettings.Save();
    }
  }
}

Чтобы использовать это, передайте класс настроек вашего приложения и имя параметра в конструкторе:

CustomForm.MyCustomForm f = new CustomForm.MyCustomForm(Properties.Settings.Default, "formSettings");

Это использует отражение для получения / установки предыдущих настроек из / в класс settings.Возможно, было бы неоптимально помещать вызов Save в процедуру Form_Closing, можно было бы удалить это и сохранять файл настроек при выходе из основного приложения.

Чтобы использовать его как обычную форму, просто используйте конструктор без параметров.

Вы можете использовать настройки приложения, чтобы установить, какие свойства элемента управления будут сохранены, в событии Form_closed вы должны использовать метод сохранения в настройках приложения, чтобы записать их на диск:

Properties.Settings.Default.Save();

Вот пример из нескольких, которые я использую сам.Он учитывает только основной монитор, поэтому, возможно, было бы лучше обрабатывать его по-другому, если использовать на нескольких мониторах.

Size size;
int x;
int y;
if (WindowState.Equals(FormWindowState.Normal))
{
    size = Size;
    if (Location.X + size.Width > Screen.PrimaryScreen.Bounds.Width)
        x = Screen.PrimaryScreen.Bounds.Width - size.Width;
    else
        x = Location.X;
    if (Location.Y + Size.Height > Screen.PrimaryScreen.Bounds.Height)
        y = Screen.PrimaryScreen.Bounds.Height - size.Height;
    else
        y = Location.Y;
}
else
{
size = RestoreBounds.Size;
x = (Screen.PrimaryScreen.Bounds.Width - size.Width)/2;
y = (Screen.PrimaryScreen.Bounds.Height - size.Height)/2;
}
Properties.Settings.Position.AsPoint = new Point(x, y); // Property setting is type of Point
Properties.Settings.Size.AsSize = size;                 // Property setting is type of Size
Properties.Settings.SplitterDistance.Value = splitContainer1.SplitterDistance; // Property setting is type of int
Properties.Settings.IsMaximized = WindowState == FormWindowState.Maximized;    // Property setting is type of bool
Properties.Settings.DropDownSelection = DropDown1.SelectedValue;
Properties.Settings.Save();

Вы можете использовать настройки для хранения этой информации.Все, что вам нужно сделать, это привязать желаемое свойство (например.форма.Размер и вид.Местоположение) к определенной настройке, и она автоматически сохраняется и обновляется.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top