Save and Restore Form Position and Size
在WinForms 2.0 C#应用程序中,用于在应用程序中保存和还原表单位置和大小的典型方法是什么?
相关,是否可以在运行时添加新的用户范围的应用程序设置?我完全知道如何在设计时添加设置,这不是问题。但是,如果我想在运行时创建一个呢?
更多详细信息:
我的应用程序是现有Visual FoxPro应用程序的转换。我一直在尝试尽可能多地阅读有关应用程序设置,用户设置等的内容,并让自己清楚地了解.Net的工作方式,但是仍然有些事情让我感到困惑。
在Fox应用程序中,保存的设置存储在注册表中。我的表单是子类的,并且我有基类代码,可以自动将表单位置和大小保存在以表单名称为关键字的注册表中。每当我创建一个新表单时,我不必做任何特殊的事情就可以得到这种行为。它内置于基类中。我的.Net表单也被子类化,该部分运行良好。
在.Net中,我得到的印象是应该将用户范围设置用于用户首选项。表单的大小和位置绝对看起来像是用户的首选项。但是,我看不到任何将这些设置自动添加到项目中的方法。换句话说,每次我向项目中添加一个新表单(它们是100个表单)时,我都必须记住要添加一个用户范围的应用程序设置,并确保为其赋予与表单相同的名称,即" FormMySpecialSizePosition"保存大小和位置。我宁愿不必记得这样做。这只是艰难的运气吗?还是我试图通过使用用户范围的设置来完全树错了树?我是否需要创建自己的XML文件来保存设置,以便我可以做我想做的任何事情(即在运行时添加新设置)?还是其他?
当然这是很常见的,有人可以告诉"正确"的方法。在此先感谢!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 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's 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();
} |
实际上,在Internet上的任何地方实际上都没有一个单一的,"可行的"解决方案,因此这是我自己创建的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
| 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's unhelpful as the user misses the fact it's 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()+@""+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 /软件/ / /窗口状态/ <表单名称>中存储表单的位置和大小。如果显示器配置发生更改,它将无法还原设置,以防止Windows在屏幕外还原。
很明显,这不能处理相同形式的多个实例。我还专门禁用了最小化还原,但这是对源代码的简单修复。
以上内容旨在放入其自己的.cs文件中,并且再也不会触及。您必须实例化这样的本地名称空间副本(在Program.cs或插件主.cs文件中或任何地方):
1 2 3 4 5 6 7 8 9
| 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>"; }
}
.... |
现在,您在主命名空间中具有非抽象的实例化。因此,要使用此方法,请将其添加到要保存和还原的表单中:
1 2 3 4 5 6 7 8 9
| private void IssuesForm_FormClosing(object sender, FormClosingEventArgs e)
{
new WindowSettings(this).save();
}
private void IssuesForm_Load(object sender, EventArgs e)
{
new WindowSettings(this).load();
} |
显然,可以根据自己的目的进行自定义。不作任何明示或暗示的保证。使用后果自负-我拒绝任何版权。
Niall
我从某个地方获得了这段代码,但是不幸的是(很久以前)当时没有评论我从哪里获得的代码。
这会将表单信息保存到用户的HKCU注册表中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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's 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();
}
}
} |
这是我使用的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| 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 */ }
} |
我也在我的文章"保存和还原表单的窗口位置"中介绍了此代码。
这里有一些相关的链接可以签出:
使用"应用程序设置"功能保存表单的大小和位置
有关如何使用"应用程序"设置的任何好的示例
探索持久性应用程序设置的秘密
我只是将其流式传输到一个单独的XML文件中-快速又肮脏,可能不是您所追求的:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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 |
和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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) |
我和您在同一条船上,因为我要保留许多表格(以我的情况为例,MDI子代),我想保留每个用户的位置和大小。根据我的研究,不支持在运行时创建应用程序设置。 (请参阅此博客条目)
但是,您不必将所有内容都粘贴在主设置文件中。您可以将设置文件添加到您的项目中(在MSDN中进行了说明),并可以通过Properties.Settings对象使用它。这不会减轻必须记住为每种表单创建新的settigns的痛苦,但是至少它将使它们保持在一起,并且不会弄乱您的主要应用程序设置。
至于使用基类来检索设置...我不知道您是否可以在那里进行。我将(可能会)做的是为每个属性命名,然后使用Me.GetType()。ToString()(我在VB中工作)来组合要在Load()事件中检索的属性的名称每种形式的
您可以创建具有通用功能的基础表单类,例如记住位置和大小并从该基础类继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 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 ;)
} |
好吧,类似的东西;)
|