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

时间:2008-08-20 18:59:35

标签: c# visual-studio data-binding .net-2.0

更新:已解决,代码为

I got it working, see my answer below for the code...

原帖

正如Tundey在his answer中向last question指出的那样,你几乎可以毫不费力地将关于Windows窗体控件的所有内容绑定到ApplicationSettings。那么表单大小真的没办法做到这一点吗? This tutorial表示您需要显式处理Size,以便在窗口最大化或最小化时保存RestoreBounds而不是size。但是,我希望我可以使用以下属性:

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

但是我看不到在设计器中绑定它的方法(在PropertyBinding列表中明显缺少Size。)

5 个答案:

答案 0 :(得分:11)

我终于想出了一个Form子类,它一劳永逸地解决了这个问题。使用它:

  1. 从RestorableForm继承而不是Form。
  2. 在(ApplicationSettings)中添加绑定 - > (PropertyBinding)到WindowRestoreState。
  3. 当窗口即将关闭时调用Properties.Settings.Default.Save()。
  4. 现在会在会话之间记住窗口位置和状态。根据下面其他海报的建议,我添加了一个功能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);
            }
        }
    }
    

    设置绑定参考

答案 1 :(得分:5)

Form.Size属性在设置绑定UI中不可用的原因是此属性标记为 DesignerSerializationVisibility.Hidden 。这意味着设计人员不知道如何序列化它,更不用说为它生成数据绑定了。相反, Form.ClientSize 属性是序列化的属性。

如果您尝试通过绑定位置 ClientSize 来变得聪明,您会看到另一个问题。当您尝试从左侧或上边缘调整表单大小时,您会看到奇怪的行为。这显然与双向数据绑定在相互影响的属性集的上下文中的工作方式有关。 位置 ClientSize 最终都会调用常用方法 SetBoundsCore()

此外,绑定到位置大小等属性的数据效率不高。每次用户移动或调整表单大小时,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;
} 

为什么这是一个非常简化的版本?因为正确地执行此操作是a lot trickier而不是它看起来: - )

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

答案 3 :(得分:1)

我认为不允许大小绑定的原因之一是因为屏幕可能会在会话之间发生变化。

当分辨率降低时加载尺寸可能导致标题栏超出屏幕限制。

您还需要警惕多个显示器设置,当您下次运行应用程序时,显示器可能不再可用。

答案 4 :(得分:1)

我同意Rob Cooper的回答。但我认为马丁提出了一个非常好的观点。没有什么比让用户打开你的应用程序和应用程序是屏幕外的!

所以实际上,在设置表单大小之前,您需要将两个答案结合起来并牢记当前的屏幕尺寸。