为啥不能将窗体的大小绑定到 ApplicationSettings?

Posted

技术标签:

【中文标题】为啥不能将窗体的大小绑定到 ApplicationSettings?【英文标题】:Why can't you bind the Size of a windows form to ApplicationSettings?为什么不能将窗体的大小绑定到 ApplicationSettings? 【发布时间】:2010-09-06 07:46:31 【问题描述】:

更新:已解决,带有代码

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 列表中明显缺少大小)。

【问题讨论】:

抱歉,您的问题来得太晚了。但我确实知道答案:-) 【参考方案1】:

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

    继承自 RestorableForm 而不是 Form。 在 (ApplicationSettings) -> (PropertyBinding) 中添加绑定到 WindowRestoreState。 在窗口即将关闭时调用 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);
        
    

设置绑定参考

SettingsBindableAttribute INotifyPropertyChanged

【讨论】:

【参考方案2】:

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

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

此外,将数据绑定到 LocationSize 等属性的效率并不高。每次用户移动或调整窗体大小时,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,而不是看起来:-)

【讨论】:

【参考方案3】:

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

在分辨率降低时重新加载大小可能会导致标题栏超出屏幕的限制。

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

【讨论】:

【参考方案4】:

好吧,我快速玩了一下,你是对的,虽然没有办法直接将表单的大小绑定到 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);
    

【讨论】:

【参考方案5】:

我同意 Rob Cooper 的回答。但我认为马丁提出了一个很好的观点。没有什么比让用户打开您的应用程序并且应用程序不在屏幕上更有趣了!

因此,实际上,在设置表单大小之前,您需要结合这两个答案并牢记当前屏幕尺寸。

【讨论】:

以上是关于为啥不能将窗体的大小绑定到 ApplicationSettings?的主要内容,如果未能解决你的问题,请参考以下文章

c# WINFORM窗体如何设置才可以不能随意拖动大小

如何设置C#窗体大小不能改变

为啥 Excel 2010 VBA 用户窗体文本框字体会根据框架的大小而变化?

为啥定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值?

winform 默认的窗体边框颜色是黄色的? 而且为啥程序运行了后 窗体大小 控件间距 还有字体 感觉不一样?

如何设置禁止改变“C# WINFORM”窗体大小?