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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18585/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-01 09:01:13  来源:igfitidea点击:

Why can't you bind the Size of a windows form to ApplicationSettings?

提问by Brian Jorgensen

Update: Solved, with code

更新:已解决,有代码

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

我让它工作了,请参阅下面的答案以获取代码...

Original Post

原帖

As Tundey pointed out in his answerto my last question, you can bind nearly everything about a windows forms control to ApplicationSettings pretty effortlessly. So is there really no way to do this with form Size? This tutorialsays you need to handle Size explicitly so you can save RestoreBounds instead of size if the window is maximized or minimized. However, I hoped I could just use a property like:

正如 Tundey 在对我最后一个问题的回答中指出的那样,您几乎可以毫不费力地将 Windows 窗体控件的所有内容绑定到 ApplicationSettings。那么真的没有办法用表单大小来做到这一点吗?本教程说您需要明确处理 Size 以便在窗口最大化或最小化时保存 RestoreBounds 而不是 size 。但是,我希望我可以使用如下属性:

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

But I can't see a way to bind this in the designer (Size is notably missing from the PropertyBinding list).

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

采纳答案by Brian Jorgensen

I finally came up with a Form subclass that solves this, once and for all. To use it:

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

  1. Inherit from RestorableForm instead of Form.
  2. Add a binding in (ApplicationSettings) -> (PropertyBinding) to WindowRestoreState.
  3. Call Properties.Settings.Default.Save() when the window is about to close.
  1. 继承自 RestorableForm 而不是 Form。
  2. 在 (ApplicationSettings) -> (PropertyBinding) 中添加一个绑定到 WindowRestoreState。
  3. 当窗口即将关闭时调用 Properties.Settings.Default.Save()。

Now window position and state will be remembered between sessions. Following the suggestions from other posters below, I included a function ConstrainToScreen that makes sure the window fits nicely on the available displays when restoring itself.

现在将在会话之间记住窗口位置和状态。遵循以下其他海报的建议,我添加了一个函数 ConstrainToScreen 以确保窗口在恢复时很好地适合可用的显示器。

Code

代码

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

Settings Bindings References

设置绑定参考

回答by Rob Cooper

Well I have had a quick play with this and you are correct, while there is no way to directly bindthe size of the form to AppSettings, you can add your own values and change the size on load.

好吧,我对此进行了快速操作,您是对的,虽然无法将表单的大小直接绑定到 AppSettings,但您可以添加自己的值并在加载时更改大小。

I would perhaps recommend that if this is a common feature, you subclass Form and make it automatically prob the App.Config for the forms size settings.

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

(Or you could roll your own file.. Get it to query an Xml file "formname.settings.xml" or something? - thinking out loud!)..

(或者你可以滚动你自己的文件..让它查询一个 Xml 文件“formname.settings.xml”或其他东西? - 大声思考!)..

Heres what I had (very rough, no error checking etc).

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

App.Config

应用程序配置

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

Form Code

表格代码

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

回答by Martin

One of the reason I imagine size binding is not allowed is because the screen may change between sessions.

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

Loading the size back when the resolution has reduced could result in the title bar being beyond the limits of the screen.

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

You also need to be wary of multiple monitor setups, where monitors may no longer be available when you app next runs.

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

回答by Tundey

I agree with Rob Cooper's answer. But I think Martin makes a very good point. Nothing like having users open your application and the app is off-screen!

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

So in reality, you'll want to combine both answers and bear in mind the current screen dimensions before setting your form's size.

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

回答by HTTP 410

The reason why the Form.Size property is not available in the settings binding UI is because this property is marked DesignerSerializationVisibility.Hidden. This means that the designer doesn't know how to serialise it, let alone generate a data binding for it. Instead the Form.ClientSizeproperty is the one that gets serialised.

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

If you try and get clever by binding Locationand ClientSize, you'll see another problem. When you try to resize your form from the left or top edge, you'll see weird behaviour. This is apparently related to the way that two-way data binding works in the context of property sets that mutually affect each other. Both Locationand ClientSizeeventually call into a common method, SetBoundsCore().

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

Also, data binding to properties like Locationand Sizeis just not efficient. Each time the user moves or resizes the form, Windows sends hundreds of messages to the form, causing the data binding logic to do a lot of processing, when all you really want is to store the last position and size before the form is closed.

此外,绑定到诸如LocationSize 之类的属性的数据效率不高。每次用户移动窗体或调整窗体大小时,Windows 都会向窗体发送数百条消息,导致数据绑定逻辑进行大量处理,而您真正想要的只是存储窗体关闭前的最后位置和大小。

This is a very simplified version of what I do:

这是我所做的非常简化的版本:

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

Why is this a very simplified version? Because doing this properly is a lot trickierthan it looks :-)

为什么这是一个非常简化的版本?因为正确地执行此操作比看起来要复杂得多:-)