为什么不能将 Windows 窗体的大小绑定到 ApplicationSettings?
-
09-06-2019 - |
题
更新:已解决,有代码
原帖
正如 Tundey 指出的那样 他的回答 给我的 最后一个问题, ,您可以毫不费力地将有关 Windows 窗体控件的几乎所有内容绑定到 ApplicationSettings。那么真的没有办法用form Size来做到这一点吗? 本教程 表示您需要显式处理 Size,以便在窗口最大化或最小化时可以保存 RestoreBounds 而不是 size。但是,我希望我可以使用如下属性:
public Size RestoreSize
{
get
{
if (this.WindowState == FormWindowState.Normal)
{
return this.Size;
}
else
{
return this.RestoreBounds.Size;
}
}
set
{
...
}
}
但我看不到在设计器中绑定它的方法(PropertyBinding 列表中明显缺少 Size)。
解决方案
我最终想出了一个 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);
}
}
}
设置绑定参考
其他提示
Form.Size 属性在设置绑定 UI 中不可用的原因是因为该属性被标记为 DesignerSerializationVisibility.Hidden. 。这意味着设计者不知道如何序列化它,更不用说为其生成数据绑定了。相反, 表单.ClientSize property 是被序列化的属性。
如果你尝试通过绑定变得聪明 地点 和 客户规模, ,你会看到另一个问题。当您尝试从左侧或顶部边缘调整表单大小时,您会看到奇怪的行为。这显然与双向数据绑定在相互影响的属性集上下文中的工作方式有关。两个都 地点 和 客户规模 最终调用一个公共方法, 设置边界核心().
此外,数据绑定到属性,例如 地点 和 尺寸 只是效率不高。每次用户移动窗体或调整窗体大小时,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;
}
为什么这是一个非常简化的版本?因为正确地这样做是 更棘手 比看起来更:-)
好吧,我已经快速玩过这个,你是对的,但没有办法直接 绑定 将表单的大小添加到 AppSettings,您可以添加自己的值并更改加载时的大小。
我也许会建议,如果这是一个常见功能,您可以对 Form 进行子类化,并使其自动探测 App.Config 的表单大小设置。
(或者你可以滚动你自己的文件..让它查询 Xml 文件“formname.settings.xml”或其他什么?- 把想法大声说出来!)..
这就是我所拥有的(非常粗糙,没有错误检查等)。
应用程序配置
<?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);
}
我认为不允许尺寸绑定的原因之一是因为屏幕可能会在会话之间发生变化。
当分辨率降低时重新加载尺寸可能会导致标题栏超出屏幕的限制。
您还需要警惕多个监视器设置,当您下次运行应用程序时,监视器可能不再可用。
我同意罗布·库珀的回答。但我认为马丁的观点非常好。没有什么比让用户打开您的应用程序而应用程序在屏幕外更好的了!
因此,实际上,您需要将两个答案结合起来,并在设置表单大小之前牢记当前的屏幕尺寸。