Как записать положение окна в настройках приложения Windows Forms - PullRequest
24 голосов
/ 20 сентября 2008

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

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

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

Ответы [ 8 ]

18 голосов
/ 20 сентября 2008

Мой другой вариант - написать больше пользовательского кода в настройках приложения и выполнить его в 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);
    }
6 голосов
/ 20 сентября 2008

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

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

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

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

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

  • Теперь убедитесь, что форма помещается на вашем экране (позвоните в 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
}

х

5 голосов
/ 20 сентября 2008

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

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

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

5 голосов
/ 20 сентября 2008

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

Недостатки:

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

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

3 голосов
/ 07 апреля 2011

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

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

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

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

2 голосов
/ 20 сентября 2008

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

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();
2 голосов
/ 20 сентября 2008

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

Properties.Settings.Default.Save();
2 голосов
/ 20 сентября 2008

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

...