为什么不能将Windows窗体的Size绑定到ApplicationSettings?

4

更新:已解决,附代码

我已经解决了,可以看下面我的答案的代码...

原始帖子

正如 Tundey 在 他的答案 中指出的那样,你可以相当轻松地将 Windows 窗体控件的几乎所有内容与应用程序设置绑定。那么是否没有办法对窗体大小执行此操作呢?这篇教程 指出,如果窗口被最大化或最小化,您需要明确处理 Size,以便保存 RestoreBounds 而不是 size。然而,我希望只需使用一个属性:

public Size RestoreSize
{
    get
    {
        if (this.WindowState == FormWindowState.Normal)
        {
            return this.Size;
        }
        else
        {
            return this.RestoreBounds.Size;
        }
    }
    set
    {
        ...
    }
}

但是我无法在设计器中找到绑定的方法(尺寸在PropertyBinding列表中明显缺失)。


抱歉,回答您的问题有点晚了。但我确实知道答案 :-) - HTTP 410
5个回答

12

我最终创建了一个Form子类来彻底解决这个问题。使用方式如下:

  1. 继承RestorableForm而不是Form。
  2. 在(ApplicationSettings)->(PropertyBinding)中添加一个绑定到WindowRestoreState。
  3. 当窗口即将关闭时调用Properties.Settings.Default.Save()。

现在窗口的位置和状态将在会话之间被记住。根据下面其他帖子的建议,我还包括了一个函数ConstrainToScreen,使窗口在恢复时能够适应可用的显示器。

代码

// Consider this code public domain. If you want, you can even tell
// your boss, attractive women, or the other guy in your cube that
// you wrote it. Enjoy!

using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;

namespace Utilities
{
    public class RestorableForm : Form, INotifyPropertyChanged
    {
        // We invoke this event when the binding needs to be updated.
        public event PropertyChangedEventHandler PropertyChanged;

        // This stores the last window position and state
        private WindowRestoreStateInfo windowRestoreState;

        // Now we define the property that we will bind to our settings.
        [Browsable(false)]        // Don't show it in the Properties list
        [SettingsBindable(true)]  // But do enable binding to settings
        public WindowRestoreStateInfo WindowRestoreState
        {
            get { return windowRestoreState; }
            set
            {
                windowRestoreState = value;
                if (PropertyChanged != null)
                {
                    // If anybody's listening, let them know the
                    // binding needs to be updated:
                    PropertyChanged(this,
                        new PropertyChangedEventArgs("WindowRestoreState"));
                }
            }
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            WindowRestoreState = new WindowRestoreStateInfo();
            WindowRestoreState.Bounds
                = WindowState == FormWindowState.Normal ?
                  Bounds : RestoreBounds;
            WindowRestoreState.WindowState = WindowState;

            base.OnClosing(e);
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            if (WindowRestoreState != null)
            {
                Bounds = ConstrainToScreen(WindowRestoreState.Bounds);
                WindowState = WindowRestoreState.WindowState;
            }
        }

        // This helper class stores both position and state.
        // That way, we only have to set one binding.
        public class WindowRestoreStateInfo
        {
            Rectangle bounds;
            public Rectangle Bounds
            {
                get { return bounds; }
                set { bounds = value; }
            }

            FormWindowState windowState;
            public FormWindowState WindowState
            {
                get { return windowState; }
                set { windowState = value; }
            }
        }

        private Rectangle ConstrainToScreen(Rectangle bounds)
        {
            Screen screen = Screen.FromRectangle(WindowRestoreState.Bounds);
            Rectangle workingArea = screen.WorkingArea;

            int width = Math.Min(bounds.Width, workingArea.Width);
            int height = Math.Min(bounds.Height, workingArea.Height);

            // mmm....minimax
            int left = Math.Min(workingArea.Right - width,
                                Math.Max(bounds.Left, workingArea.Left));
            int top = Math.Min(workingArea.Bottom - height,
                                Math.Max(bounds.Top, workingArea.Top));

            return new Rectangle(left, top, width, height);
        }
    }
}

设置绑定引用


7
在设置绑定UI中,Form.Size属性不可用的原因是该属性被标记为DesignerSerializationVisibility.Hidden。这意味着设计师不知道如何序列化它,更不用说生成数据绑定了。相反,Form.ClientSize属性是被序列化的属性。
如果您尝试通过绑定Location和ClientSize来实现聪明的操作,您会遇到另一个问题。当您尝试从左侧或顶部边缘调整窗体大小时,您将看到奇怪的行为。这显然与双向数据绑定在相互影响的属性集上的工作方式有关。Location和ClientSize最终都会调用一个公共方法SetBoundsCore()。
此外,对于像Location和Size这样的属性进行数据绑定并不高效。每次用户移动或调整窗体大小时,Windows都会向窗体发送数百条消息,导致数据绑定逻辑做很多处理,而您真正想要的只是在关闭窗体之前存储最后的位置和大小。
这是我所做的非常简化的版本:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
    Properties.Settings.Default.MyState = this.WindowState;
    if (this.WindowState == FormWindowState.Normal)
    {
       Properties.Settings.Default.MySize = this.Size;
       Properties.Settings.Default.MyLoc = this.Location;
    }
    else
    {
       Properties.Settings.Default.MySize = this.RestoreBounds.Size;
       Properties.Settings.Default.MyLoc = this.RestoreBounds.Location;
    }
    Properties.Settings.Default.Save();
}

private void MyForm_Load(object sender, EventArgs e)
{
    this.Size = Properties.Settings.Default.MySize;
    this.Location = Properties.Settings.Default.MyLoc;
    this.WindowState = Properties.Settings.Default.MyState;
} 

为什么这是一个非常简化的版本?因为正确地完成这个任务比看起来要困难得多:-) 点击此处了解更多信息。


2

我想不允许尺寸绑定的一个原因是屏幕在会话之间可能会改变。

当分辨率降低时重新加载大小可能导致标题栏超出屏幕范围。

您还需要注意多个监视器设置,在下一次运行应用程序时可能不再可用。


1

我已经快速尝试了一下,你是正确的,虽然没有直接将表单大小与AppSettings绑定的方法,但是您可以添加自己的值并在加载时更改大小。

如果这是一个常见的功能,我建议您子类化Form并使其自动探测App.Config以获取表单大小设置。

(或者您可以自己编写文件..让它查询一个Xml文件“formname.settings.xml”之类的东西?-随便想想!)。

这是我拥有的(非常粗糙,没有错误检查等)。

App.Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key ="FormHeight" value="500" />
        <add key ="FormWidth" value="200"/>
    </appSettings>
</configuration>

表单代码

    private void Form1_Load(object sender, EventArgs e)
    {
        string height = ConfigurationManager.AppSettings["FormHeight"];
        int h = int.Parse(height);
        string width = ConfigurationManager.AppSettings["FormWidth"];
        int w = int.Parse(width);
        this.Size = new Size(h, w);
    }

1

我同意Rob Cooper的回答。但我认为Martin提出了一个非常好的观点。没有什么比让用户打开你的应用程序,而应用程序却在屏幕外更糟糕的了!

因此,在现实中,您需要结合两个答案,并牢记当前屏幕尺寸,然后设置表单的大小。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接