Сохранение и восстановление позиции и размера формы

В приложении WinForms 2.0 С#, каков типичный метод, используемый для сохранения и восстановления позиции и размера позиции в приложении?

Связано, можно ли добавить новые пользовательские настройки приложения AT RUNTIME? Я полностью понимаю, как добавлять настройки во время разработки, это не проблема. Но что, если я хочу создать его во время выполнения?

Подробнее:

Мое приложение - это преобразование существующего приложения Visual FoxPro. Я пытался как можно больше узнать о настройках приложения, настройках пользователя и т.д. И проясниться на пути .Net, но есть еще несколько вещей, которые меня путают.

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

В .Net, у меня создается впечатление, что я должен использовать настройки области пользователя для таких вещей, как пользовательские настройки. Размер и расположение формы определенно кажутся предпочтениям пользователей. Но я не вижу возможности автоматически добавлять эти параметры в проект. Другими словами, каждый раз, когда я добавляю новую форму в свой проект (и их 100 форм), я должен помнить, что ADD пользовательский параметр приложения и не забудьте дать ему то же имя, что и форма, т.е. FormMySpecialSizePosition ", чтобы сохранить размер и положение. Я бы предпочел не вспоминать об этом. Это просто тяжелая удача? Или я полностью лаяю неправильное дерево, пытаясь использовать настройки, ограниченные пользователем? Мне нужно создать свой собственный XML файл для хранения настроек, чтобы я мог делать все, что захочу (т.е. Добавить новый параметр во время выполнения)? Или что-то еще?

Конечно, это очень часто, и кто-то может сказать "правильный" способ сделать это. Спасибо заранее!

Ответ 1

private void Form1_Load( object sender, EventArgs e )
{
    // restore location and size of the form on the desktop
    this.DesktopBounds =
        new Rectangle(Properties.Settings.Default.Location,
    Properties.Settings.Default.Size);
    // restore form window state
    this.WindowState = ( FormWindowState )Enum.Parse(
        typeof(FormWindowState),
        Properties.Settings.Default.WindowState);
}

private void Form1_FormClosing( object sender, FormClosingEventArgs e )
{
    System.Drawing.Rectangle bounds = this.WindowState != FormWindowState.Normal ? this.RestoreBounds : this.DesktopBounds;
    Properties.Settings.Default.Location = bounds.Location;
    Properties.Settings.Default.Size = bounds.Size;
    Properties.Settings.Default.WindowState =
        Enum.GetName(typeof(FormWindowState), this.WindowState);
    // persist location ,size and window state of the form on the desktop
    Properties.Settings.Default.Save();
}

Ответ 2

Я получил этот код откуда-то, но, к сожалению, в то время (давно) не комментировал, откуда я его получил.

Это сохраняет информацию о форме пользователю реестра HKCU:

using System;
using System.Windows.Forms;
using Microsoft.Win32;

/// <summary>Summary description for FormPlacement.</summary>
public class PersistentForm : System.Windows.Forms.Form
{
    private const string DIALOGKEY = "Dialogs";

    /// <summary></summary>
    protected override void OnCreateControl()
    {
        LoadSettings();
        base.OnCreateControl ();
    }

    /// <summary></summary>
    protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
    {
        SaveSettings();
        base.OnClosing(e);
    }

    /// <summary>Saves the form settings.</summary>
    public void SaveSettings()
    {
        RegistryKey dialogKey = Application.UserAppDataRegistry.CreateSubKey(DIALOGKEY);
        if (dialogKey != null)
        {
            RegistryKey formKey = dialogKey.CreateSubKey(this.GetType().ToString());
            if (formKey != null)
            {
                formKey.SetValue("Left", this.Left);
                formKey.SetValue("Top", this.Top);
                formKey.Close();
            }
            dialogKey.Close();
        }
    }

    /// <summary></summary>
    public void LoadSettings()
    {
        RegistryKey dialogKey = Application.UserAppDataRegistry.OpenSubKey(DIALOGKEY);
        if (dialogKey != null)
        {
            RegistryKey formKey = dialogKey.OpenSubKey(this.GetType().ToString());
            if (formKey != null)
            {
                this.Left = (int)formKey.GetValue("Left");
                this.Top = (int)formKey.GetValue("Top");
                formKey.Close();
            }
            dialogKey.Close();
        }
    }
}

Ответ 3

На самом деле существует реальная нехватка одного, "просто работает" решения для этого в любом месте в Интернете, поэтому здесь мое собственное творение:

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Win32;
using System.ComponentModel;
using System.Security.Cryptography;

namespace nedprod
{
    abstract public class WindowSettings
    {
        private Form form;

        public FormWindowState state;
        public Point location;
        public Size size;

        public WindowSettings(Form _form)
        {
            this.form = _form;
        }
        internal class MD5Sum
        {
            static MD5CryptoServiceProvider engine = new MD5CryptoServiceProvider();
            private byte[] sum = engine.ComputeHash(BitConverter.GetBytes(0));
            public MD5Sum() { }
            public MD5Sum(string s)
            {
                for (var i = 0; i < sum.Length; i++)
                    sum[i] = byte.Parse(s.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            }
            public void Add(byte[] data)
            {
                byte[] temp = new byte[sum.Length + data.Length];
                var i=0;
                for (; i < sum.Length; i++)
                    temp[i] = sum[i];
                for (; i < temp.Length; i++)
                    temp[i] = data[i - sum.Length];
                sum=engine.ComputeHash(temp);
            }
            public void Add(int data)
            {
                Add(BitConverter.GetBytes(data));
            }
            public void Add(string data)
            {
                Add(Encoding.UTF8.GetBytes(data));
            }
            public static bool operator ==(MD5Sum a, MD5Sum b)
            {
                if (a.sum == b.sum) return true;
                if (a.sum.Length != b.sum.Length) return false;
                for (var i = 0; i < a.sum.Length; i++)
                    if (a.sum[i] != b.sum[i]) return false;
                return true;
            }
            public static bool operator !=(MD5Sum a, MD5Sum b)
            {
                return !(a == b);
            }
            public override bool Equals(object obj)
            {
                try
                {
                    return (bool)(this == (MD5Sum)obj);
                }
                catch
                {
                    return false;
                }
            }
            public override int GetHashCode()
            {
                return ToString().GetHashCode();
            }
            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                for (var i = 0; i < sum.Length; i++)
                    sb.Append(sum[i].ToString("x2"));
                return sb.ToString();
            }
        }
        private MD5Sum screenconfig()
        {
            MD5Sum md5=new MD5Sum();
            md5.Add(Screen.AllScreens.Length); // Hash the number of screens
            for(var i=0; i<Screen.AllScreens.Length; i++)
            {
                md5.Add(Screen.AllScreens[i].Bounds.ToString()); // Hash the dimensions of this screen
            }
            return md5;
        }
        public void load()
        {
            using (RegistryKey r = Registry.CurrentUser.OpenSubKey(@"Software\" + CompanyId() + @"\" + AppId() + @"\Window State\" + form.Name))
            {
                if (r != null)
                {
                    try
                    {
                        string _location = (string)r.GetValue("location"), _size = (string)r.GetValue("size");
                        state = (FormWindowState)r.GetValue("state");
                        location = (Point)TypeDescriptor.GetConverter(typeof(Point)).ConvertFromInvariantString(_location);
                        size = (Size)TypeDescriptor.GetConverter(typeof(Size)).ConvertFromInvariantString(_size);

                        // Don't do anything if the screen config has since changed (otherwise windows vanish off the side)
                        if (screenconfig() == new MD5Sum((string) r.GetValue("screenconfig")))
                        {
                            form.Location = location;
                            form.Size = size;
                            // Don't restore if miminised (it unhelpful as the user misses the fact it opened)
                            if (state != FormWindowState.Minimized)
                                form.WindowState = state;
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }
        public void save()
        {
            state = form.WindowState;
            if (form.WindowState == FormWindowState.Normal)
            {
                size = form.Size;
                location = form.Location;
            }
            else
            {
                size = form.RestoreBounds.Size;
                location = form.RestoreBounds.Location;
            }
            using (RegistryKey r = Registry.CurrentUser.CreateSubKey(@"Software\" + CompanyId()[email protected]"\"+AppId() + @"\Window State\" + form.Name, RegistryKeyPermissionCheck.ReadWriteSubTree))
            {
                r.SetValue("state", (int) state, RegistryValueKind.DWord);
                r.SetValue("location", location.X.ToString() + "," + location.Y.ToString(), RegistryValueKind.String);
                r.SetValue("size", size.Width.ToString()+","+size.Height.ToString(), RegistryValueKind.String);
                r.SetValue("screenconfig", screenconfig().ToString(), RegistryValueKind.String);
            }
        }
        abstract protected string CompanyId();
        abstract protected string AppId();
    }
}

Эта реализация сохраняет позицию и размер формы в HKCU/Software/< CompanyId() > /< AppId() > /Состояние окна /< имя формы > . Он не будет восстанавливать настройки, если конфигурация монитора изменяется так, чтобы предотвратить восстановление окон за окном.

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

Вышеописанное предназначено для того, чтобы его можно было сбросить в собственный файл .cs и никогда не трогать. Вам нужно создать экземпляр локальной копии пространства имен, как это (в Program.cs или в главном файле .cs файла плагина или где угодно):

namespace <your app/plugin namespace name>
{
    public class WindowSettings : nedprod.WindowSettings
    {
        public WindowSettings(Form form) : base(form) { }
        protected override string CompanyId() { return "<your company name>"; }
        protected override string AppId() { return "<your app name>"; }
    }
    ....

Теперь у вас есть не абстрактное создание экземпляра в главном пространстве имен. Итак, чтобы использовать, добавьте это в формы, которые хотите сохранить и восстановить:

    private void IssuesForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        new WindowSettings(this).save();
    }

    private void IssuesForm_Load(object sender, EventArgs e)
    {
        new WindowSettings(this).load();
    }

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

Найл

Ответ 4

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

public class myForm : Form {
protected override void OnLoad(){
    //load the settings and apply them
    base.OnLoad();
}

protected override void OnClose(){
    //save the settings
    base.OnClose();
}
}
then for the other forms:

public class frmMainScreen : myForm {
// you get the settings for free ;)
}

Ну, что-то в этом роде;)

Ответ 5

Я нахожусь в той же лодке, что и у вас, поскольку у меня есть несколько форм (в моем случае - дети MDI), которые я хочу сохранить для каждого пользователя. Из моих исследований создание настроек приложения во время выполнения не поддерживается. (см. эту запись в блоге) Однако вам не нужно вставлять все в основной файл настроек. Вы можете добавить файл настроек в свой проект (описанный здесь в MSDN) и использовать его через объект Properties.Settings. Это не облегчит боль необходимости запоминать новые настройки для каждой формы, но, по крайней мере, это будет держать их вместе, а не загромождать основные настройки приложения.

Что касается использования базового класса для извлечения настроек... Я не знаю, можете ли вы это сделать. То, что я хотел бы (и, вероятно, будет) делать, это назвать каждый атрибут, а затем использовать Me.GetType(). ToString() (я работаю в VB), чтобы скомпоновать имена атрибутов, которые я хочу получить в событии Load() каждой формы.

Ответ 6

Я просто передаю его в отдельный XML файл - быстро и грязно и, вероятно, не то, что вы после:

Dim winRect As String() = util.ConfigFile.GetUserConfigInstance().GetValue("appWindow.rect").Split(",")
Dim winState As String = util.ConfigFile.GetUserConfigInstance().GetValue("appWindow.state")

Me.WindowState = FormWindowState.Normal

Me.Left = CType(winRect(0), Integer)
Me.Top = CType(winRect(1), Integer)
Me.Width = CType(winRect(2), Integer)
Me.Height = CType(winRect(3), Integer)

If winState = "maximised" Then
    Me.WindowState = FormWindowState.Maximized
End If

а также

Dim winState As String = "normal"
If Me.WindowState = FormWindowState.Maximized Then
    winState = "maximised"
ElseIf Me.WindowState = FormWindowState.Minimized Then
    winState = "minimised"
End If

If Me.WindowState = FormWindowState.Normal Then

    Dim winRect As String = CType(Me.Left, String) & "," & CType(Me.Top, String) & "," & CType(Me.Width, String) & "," & CType(Me.Height, String)
    ' only save window rectangle if its not maximised/minimised
    util.ConfigFile.GetUserConfigInstance().SetValue("appWindow.rect", winRect)
End If

util.ConfigFile.GetUserConfigInstance().SetValue("appWindow.state", winState)

Ответ 8

Вот код, который я использовал.

private void SaveWindowPosition()
{
    Rectangle rect = (WindowState == FormWindowState.Normal) ?
        new Rectangle(DesktopBounds.Left, DesktopBounds.Top, DesktopBounds.Width, DesktopBounds.Height) :
        new Rectangle(RestoreBounds.Left, RestoreBounds.Top, RestoreBounds.Width, RestoreBounds.Height);
    RegistrySettings.SetSetting("WindowPosition", String.Format("{0},{1},{2},{3},{4}",
        (int)this.WindowState,
        rect.Left, rect.Top, rect.Width, rect.Height));
}

private void RestoreWindowPosition()
{
    try
    {
        string s = RegistrySettings.GetSetting("WindowPosition", String.Empty) as string;
        if (s != null)
        {
            List<int> settings = s.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                  .Select(v => int.Parse(v)).ToList();
            if (settings.Count == 5)
            {
                this.SetBounds(
                    settings[1],
                    settings[2],
                    settings[3],
                    settings[4]);
                this.WindowState = (FormWindowState)settings[0];
            }
        }
    }
    catch { /* Just leave current position if error */ }
}

Я также представил этот код в своей статье Сохранение и восстановление позиции окна формы.