12

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

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

更多细节:

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

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

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

当然,这是一种非常普遍的做法,有人可以说出“正确”的做法。提前致谢!

4

8 回答 8

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();
}
于 2008-10-25T11:43:39.207 回答
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'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/<CompanyId()>/<AppId()>/Window State/<form name> 中。如果显示器配置发生更改,它不会恢复设置,以防止窗口在屏幕外恢复。

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

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

显然,您可以根据自己的目的随意定制。没有任何明示或暗示的保证。使用风险自负 - 我否认任何版权。

尼尔

于 2012-02-24T19:06:58.343 回答
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();
        }
    }
}
于 2012-02-19T18:26:34.670 回答
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)
于 2009-11-27T11:48:34.953 回答
0

You could create a base form class with common functionality such as remembering the position and size and inherit from that base class.

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

Well, something like that ;)

于 2008-09-18T14:45:43.963 回答
0

我和你在同一条船上,因为我有许多表单(在我的例子中是 MDI 子级),我想为每个用户保留位置和大小。根据我的研究,不支持在运行时创建应用程序设置。(请参阅此博客条目)但是,您不必将所有内容都粘贴在主设置文件中。您可以将设置文件添加到您的项目(在 MSDN 中解释)并通过 Properties.Settings 对象使用它。这不会减轻必须记住为每个表单创建新设置的痛苦,但至少可以将它们保持在一起,并且不会弄乱您的主要应用程序设置。

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

于 2008-09-19T14:53:33.100 回答
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 */ }
}

我还在我的文章保存和恢复窗体的窗口位置中介绍了此代码。

于 2014-09-21T20:57:15.507 回答
0

以下是一些相关链接可供查看:

使用应用程序设置功能保存表单的大小和位置

任何有关如何使用应用程序设置的好例子

探索持久应用程序设置的秘密

于 2010-12-17T21:36:51.837 回答