Сохранение и восстановление позиции и размера формы
В приложении 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)
Ответ 7
Вот некоторые релевантные ссылки:
Сохранение размера и местоположения формы с помощью функции параметров приложения
Любые хорошие примеры использования настроек приложений
Изучение секретов постоянных настроек приложения
Ответ 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 */ }
}
Я также представил этот код в своей статье Сохранение и восстановление позиции окна формы.