214 lines
6.8 KiB
C#
214 lines
6.8 KiB
C#
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Text.Json.Nodes;
|
|
using Sim.Host;
|
|
|
|
namespace System.Windows.Forms;
|
|
|
|
/// <summary>
|
|
/// 窗体。Show() 时先触发 Load,再把整个控件树发给浏览器;Close() 依次触发 FormClosing、FormClosed。
|
|
/// </summary>
|
|
public class Form : ContainerControl
|
|
{
|
|
// Windows 10 下可调边框窗体的标题栏和边框尺寸,Size = ClientSize + 边框
|
|
private static readonly Size NonClientSize = new(16, 39);
|
|
|
|
private Size _clientSize = new(284, 261);
|
|
private FormBorderStyle _formBorderStyle = FormBorderStyle.Sizable;
|
|
private bool _maximizeBox = true;
|
|
private bool _minimizeBox = true;
|
|
private bool _controlBox = true;
|
|
private bool _loaded;
|
|
private bool _closed;
|
|
private bool _modal;
|
|
private DialogResult _dialogResult;
|
|
|
|
private protected override bool SupportsTransparentBackColor => false;
|
|
|
|
public Form()
|
|
{
|
|
base.Visible = false;
|
|
}
|
|
|
|
internal bool IsOpenInBrowser { get; private set; }
|
|
|
|
public override Size ClientSize
|
|
{
|
|
get => _clientSize;
|
|
set
|
|
{
|
|
if (SetProp(ref _clientSize, value, nameof(ClientSize))) PerformLayout();
|
|
}
|
|
}
|
|
|
|
public new Size Size
|
|
{
|
|
get => _formBorderStyle == FormBorderStyle.None ? _clientSize : _clientSize + NonClientSize;
|
|
set => ClientSize = _formBorderStyle == FormBorderStyle.None ? value : value - NonClientSize;
|
|
}
|
|
|
|
public FormBorderStyle FormBorderStyle
|
|
{
|
|
get => _formBorderStyle;
|
|
set => SetProp(ref _formBorderStyle, value, nameof(FormBorderStyle));
|
|
}
|
|
|
|
public bool MaximizeBox
|
|
{
|
|
get => _maximizeBox;
|
|
set => SetProp(ref _maximizeBox, value, nameof(MaximizeBox));
|
|
}
|
|
|
|
public bool MinimizeBox
|
|
{
|
|
get => _minimizeBox;
|
|
set => SetProp(ref _minimizeBox, value, nameof(MinimizeBox));
|
|
}
|
|
|
|
public bool ControlBox
|
|
{
|
|
get => _controlBox;
|
|
set => SetProp(ref _controlBox, value, nameof(ControlBox));
|
|
}
|
|
|
|
public FormStartPosition StartPosition { get; set; } = FormStartPosition.WindowsDefaultLocation;
|
|
public FormWindowState WindowState { get; set; }
|
|
public bool ShowInTaskbar { get; set; } = true;
|
|
public bool TopMost { get; set; }
|
|
public bool KeyPreview { get; set; }
|
|
public double Opacity { get; set; } = 1;
|
|
public IButtonControl? AcceptButton { get; set; }
|
|
public IButtonControl? CancelButton { get; set; }
|
|
public Form? Owner { get; set; }
|
|
public bool Modal => _modal;
|
|
|
|
public DialogResult DialogResult
|
|
{
|
|
get => _dialogResult;
|
|
set
|
|
{
|
|
_dialogResult = value;
|
|
// 模态窗体设置 DialogResult 后自动关闭
|
|
if (_modal && value != DialogResult.None) Close();
|
|
}
|
|
}
|
|
|
|
public event EventHandler? Load;
|
|
public event EventHandler? Shown;
|
|
public event EventHandler? Activated;
|
|
public event FormClosingEventHandler? FormClosing;
|
|
public event FormClosedEventHandler? FormClosed;
|
|
|
|
protected virtual void OnLoad(EventArgs e) => Load?.Invoke(this, e);
|
|
protected virtual void OnShown(EventArgs e) => Shown?.Invoke(this, e);
|
|
protected virtual void OnActivated(EventArgs e) => Activated?.Invoke(this, e);
|
|
protected virtual void OnFormClosing(FormClosingEventArgs e) => FormClosing?.Invoke(this, e);
|
|
protected virtual void OnFormClosed(FormClosedEventArgs e) => FormClosed?.Invoke(this, e);
|
|
|
|
public new void Show()
|
|
{
|
|
if (_closed) throw new ObjectDisposedException(Name, "无法访问已释放的对象。");
|
|
if (IsOpenInBrowser)
|
|
{
|
|
Visible = true;
|
|
return;
|
|
}
|
|
SimHost.Ui.Send(() => Open(modal: false));
|
|
}
|
|
|
|
public void Show(IWin32Window? owner) => Show();
|
|
|
|
public DialogResult ShowDialog() => ShowDialog(null);
|
|
|
|
public DialogResult ShowDialog(IWin32Window? owner)
|
|
{
|
|
if (IsOpenInBrowser && Visible) throw new InvalidOperationException("窗体已可见,不能显示为模式对话框。");
|
|
// 模态窗体关闭后不释放,可以再次 ShowDialog(Load 只在第一次触发)
|
|
_closed = false;
|
|
_dialogResult = DialogResult.None;
|
|
_modal = true;
|
|
Open(modal: true);
|
|
// 嵌套消息循环:等到窗体关闭(或被隐藏)后 ShowDialog 才返回
|
|
SimHost.Ui.RunUntil(() => _closed || !Visible);
|
|
_modal = false;
|
|
return _dialogResult == DialogResult.None ? DialogResult.Cancel : _dialogResult;
|
|
}
|
|
|
|
private void Open(bool modal)
|
|
{
|
|
if (!_loaded)
|
|
{
|
|
_loaded = true;
|
|
OnLoad(EventArgs.Empty);
|
|
if (_closed) return; // Load 里调用了 Close()
|
|
RaiseUserControlLoad(this);
|
|
}
|
|
base.Visible = true;
|
|
IsOpenInBrowser = true;
|
|
Application.OpenFormsList.Add(this);
|
|
SimHost.Send(new JsonObject { ["type"] = "formShown", ["form"] = Snapshot(), ["modal"] = modal });
|
|
OnShown(EventArgs.Empty);
|
|
OnActivated(EventArgs.Empty);
|
|
}
|
|
|
|
private static void RaiseUserControlLoad(Control parent)
|
|
{
|
|
foreach (var child in parent.Controls)
|
|
{
|
|
if (child is UserControl userControl) userControl.RaiseLoadOnce();
|
|
RaiseUserControlLoad(child);
|
|
}
|
|
}
|
|
|
|
public void Close() => CloseCore(CloseReason.UserClosing);
|
|
|
|
internal void CloseCore(CloseReason reason)
|
|
{
|
|
if (_closed) return;
|
|
if (!IsOpenInBrowser)
|
|
{
|
|
_closed = true;
|
|
return;
|
|
}
|
|
var closing = new FormClosingEventArgs(reason, false);
|
|
OnFormClosing(closing);
|
|
if (closing.Cancel) return;
|
|
_closed = true;
|
|
if (_modal && _dialogResult == DialogResult.None) _dialogResult = DialogResult.Cancel;
|
|
OnFormClosed(new FormClosedEventArgs(reason));
|
|
IsOpenInBrowser = false;
|
|
Application.OpenFormsList.Remove(this);
|
|
SimHost.Send(new JsonObject { ["type"] = "formClosed", ["formId"] = SimId });
|
|
SimHost.Ui.Wake();
|
|
if (!_modal) Dispose();
|
|
}
|
|
|
|
internal bool IsClosed => _closed;
|
|
|
|
public void Activate() { }
|
|
|
|
public void CenterToScreen() { }
|
|
|
|
protected override void OnUserAction(UserAction action)
|
|
{
|
|
if (action.Action == "close")
|
|
{
|
|
CloseCore(CloseReason.UserClosing);
|
|
return;
|
|
}
|
|
base.OnUserAction(action);
|
|
}
|
|
|
|
protected override void WriteProps(JsonObject props)
|
|
{
|
|
base.WriteProps(props);
|
|
props["Size"] = PropValue.From(Size);
|
|
props["ClientSize"] = PropValue.From(_clientSize);
|
|
props["FormBorderStyle"] = _formBorderStyle.ToString();
|
|
props["MaximizeBox"] = _maximizeBox;
|
|
props["MinimizeBox"] = _minimizeBox;
|
|
props["ControlBox"] = _controlBox;
|
|
props["StartPosition"] = StartPosition.ToString();
|
|
}
|
|
}
|