保存并恢复表单位置和大小

时间:2008-09-18 13:31:01

标签: .net winforms configuration settings

在WinForms 2.0 C#应用程序中,用于在应用程序中保存和恢复表单位置和大小的典型方法是什么?

相关,是否可以在RUNTIME中添加新的用户范围应用程序设置?我完全看到如何在设计时添加设置,这不是问题。但是如果我想在运行时创建一个呢?

更多详情:

我的应用程序是现有Visual FoxPro应用程序的转换。我一直在尝试尽可能多地阅读有关应用程序设置,用户设置等的内容,并明确了解.Net的做事方式,但仍有一些我感到困惑的事情。

在Fox应用程序中,保存的设置存储在注册表中。我的表单是子类,我有基类代码,自动保存表单位置和大小在表单名称的注册表中。每当我创建一个新表单时,我都不需要做任何特殊的事情来获得这种行为;它内置于基类中。我的.Net表格也是子类,这部分运作良好。

在.Net中,我得到的印象是我应该使用用户范围设置来处理用户首选项等内容。表单的大小和位置绝对看起来像用户首选项。但是,我看不到任何方法可以自动将这些设置添加到项目中。换句话说,每次我向项目添加一个新表单(并且它们是100个表单)时,我必须记住添加一个用户范围的应用程序设置,并确保给它与表单相同的名称,即“ FormMySpecialSizePosition“保持大小和位置。我宁愿不记得那样做。这只是运气吗?或者我是否通过尝试使用用户范围设置完全咆哮错误的树?我是否需要创建自己的XML文件来保存设置,以便我可以做任何我想做的事情(即在运行时添加新设置)?或其他什么?

当然这是非常普遍的,有人可以说出“正确”的方式。提前谢谢!

8 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分: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'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();
        }
    }
}

答案 2 :(得分:2)

在互联网上的任何地方实际上都缺乏单一的,“正常工作”的解决方案,所以这是我自己的创作:

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 / Software /&lt; CompanyId()&gt; /&lt; AppId()&gt; / Window State /&lt; form name&gt;中。如果监视器配置发生变化,它将不会恢复设置,以防止窗口从屏幕恢复。

显然,这不能处理同一表单的多个实例。我还特别禁用了最小化恢复,但这是一个简单的源代码修复。

以上内容旨在放入自己的.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();
    }

显然可以根据自己的目的自定义。不作任何明示或暗示的保证。使用风险由我自行承担 - 我不承担任何版权。

尼尔

答案 3 :(得分:0)

您可以创建一个具有常用功能的基本表单类,例如记住位置和大小以及从该基类继承。

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

嗯,这样的事情;)

答案 4 :(得分:0)

我和你在同一条船上,因为我有多种形式(MDI儿童,在我的情况下),我想保留每个用户的位置和大小。根据我的研究,不支持在运行时创建应用程序设置。 (见this blog entry) 但是,您不必将所有内容都粘贴在主设置文件中。您可以将设置文件添加到项目(explained here in the MSDN)并通过Properties.Settings对象使用它。这不会减轻必须记住为每个表单创建新settig的痛苦,但至少它会将它们保持在一起,而不会使主应用程序设置混乱。

至于使用基类来检索设置......我不知道你是否可以在那里做到。我想(也可能会)做的是命名每个属性,然后使用Me.GetType()。ToString()(我在VB中工作)来组合我想要在Load()事件中检索的属性的名称每种形式。

答案 5 :(得分:0)

我只是将它流式传输到一个单独的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)

答案 6 :(得分:0)

答案 7 :(得分:0)

这是我使用的代码。

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 */ }
}

我还在文章Saving and Restoring a Form's Window Position中提供了此代码。