自从我向它添加了所有者以来,我就遇到了无法关闭模态表单的问题。 它只是一个非常简单的表单,上面有一个关闭按钮。根据我所在的屏幕,如果弹出表单,关闭按钮将不会执行任何操作,并且表单将锁定。
似乎每周我都会添加更多的编组(暂时修复它,然后最终再次锁定),但我不太清楚出了什么问题。我真的有点不知道在这里做什么希望有人知道!
`这是我现在调用表单的方式:
FrmFaultDetected faultDetectedScreen = new FrmFaultDetected(e);
if (this.InvokeRequired)
this.Invoke(new Action(() => m_Saw.ShowDialog(faultDetectedScreen)));
else
m_Saw.ShowDialog(faultDetectedScreen);
private void SawApp_OnShowDialog(Form form)
{
Form topForm = m_OpenScreens.Count > 0 ? m_OpenScreens.Peek() : this;// the top form in my application, "this" is the main form
if (this.InvokeRequired)
this.Invoke(new Action(() => form.ShowDialog(topForm)));
else
form.ShowDialog(topForm);
}
表格中没有任何验证或任何内容。
这是我设置的关闭按钮:
private void btnClose_Click_1(object sender, EventArgs e)
{
try
{
this.Invoke((MethodInvoker)delegate
{
this.Close();
});
}
catch (Exception ex)
{
//log error;
}
}
您描述了一种系统性且持续的情况,其中“每周”您都会尝试不同的编组方法,因此我将展示一个最小的模拟来进行实验和适应,并希望能解决您的问题。您的代码表明您正在名为
m_openScreens
的堆栈中维护多个屏幕对象,并且此类表示会产生某种故障的简化非模式屏幕。
class MockScreen : Form
{
protected override void OnFormClosing(FormClosingEventArgs e)
{
if(e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
base.OnFormClosing(e);
}
public void RaiseMockFault() => Fault?.Invoke(this, EventArgs.Empty);
public event EventHandler? Fault;
}
为了模拟“带有关闭按钮的非常简单的表单”,设想一个每当出现新错误时都会以模态方式弹出的表单,其行为是,如果在任何屏幕上出现新错误时它仍然弹出,它会添加报告中的新错误已显示。
class FaultDetectedScreen : Form
{
RichTextBox _richTextBox = new RichTextBox
{
Name = nameof(RichTextBox),
Dock = DockStyle.Fill,
BackColor = Color.FromArgb(0x22, 0x22, 0x22),
};
public FaultDetectedScreen()
{
StartPosition = FormStartPosition.Manual;
Padding = new Padding(5);
Controls.Add(_richTextBox);
var button = new Button
{
Name = nameof(Button),
Text = "OK",
Dock = DockStyle.Bottom,
Height = 50,
};
button.Click += (sender, e) => DialogResult = DialogResult.OK;
Controls.Add(button);
}
private readonly Dictionary<MockScreen, int> _counts = new Dictionary<MockScreen, int>();
public void Report(MockScreen screen)
{
if (!_counts.ContainsKey(screen))
{
_counts[screen] = 1;
}
else _counts[screen]++;
var count = _counts[screen];
switch (count.CompareTo(2))
{
case -1: _richTextBox.SelectionColor = Color.LightSalmon; break;
case 0: _richTextBox.SelectionColor = Color.Yellow; break;
case 1: _richTextBox.SelectionColor = Color.Red; break;
}
_richTextBox
.AppendText(
$@"[{DateTime.Now.TimeOfDay:hh\:mm\:ss\:ff}] {screen.Name.Replace("form", string.Empty)} Errors={count}{Environment.NewLine}");
}
}
在此基础上,要处理错误,无论是否在 UI 线程上引发,都可以使用此模式。
private void Any_ScreenFault(object? sender, EventArgs e)
{
Debug.WriteLine($"InvokeRequired: {InvokeRequired}");
BeginInvoke(() =>
{
if (sender is MockScreen screen)
{
FaultDetectedScreen.Report(screen);
}
if (!(Disposing || FaultDetectedScreen.Visible))
{
FaultDetectedScreen.ShowDialog(this);
}
});
}
这是我用来测试这个答案的代码:
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Disposed += (sender, e) =>
{
_cts.Cancel();
FaultDetectedScreen.Dispose();
};
for (int i = 0; i < 3; i++)
{
var button = new Button
{
Name = $"buttonScreen{i}",
Text = $"Show Screen {i}",
Location = new Point(50, 50 + (i * 60)),
Size = new Size(200, 50),
};
button.Click += (sender, e) =>
{
if (
sender is Button button
&&
button.Name is string name
&&
Application.OpenForms[name.Replace("button", "form")] is Form form
&&
!form.Visible)
form.Show(this);
};
var screen = new MockScreen
{
Name = $"formScreen{i}",
Text = $"Screen {i}",
StartPosition = FormStartPosition.Manual,
Location = new Point(Location.X + 10 + Width, Location.Y + (i * (Height + 10))),
Size = Size,
};
screen.Show(this);
screen.VisibleChanged += (sender, e) => BringToFront(); // 'Last child closed' workaround.
screen.Fault += Any_ScreenFault;
Controls.Add(button);
}
FaultDetectedScreen.Location = new Point(Location.X + 10, Location.Y + 10);
FaultDetectedScreen.Size = new Size(Size.Width - 20, Size.Height - 20);
FaultDetectedScreen.FormBorderStyle = FormBorderStyle.FixedToolWindow;
_ = GenerateRandomFaults(_cts.Token);
}
private void Any_ScreenFault(object? sender, EventArgs e)
{
Debug.WriteLine($"InvokeRequired: {InvokeRequired}");
BeginInvoke(() =>
{
if (sender is MockScreen screen)
{
FaultDetectedScreen.Report(screen);
}
if (!(Disposing || FaultDetectedScreen.Visible))
{
FaultDetectedScreen.ShowDialog(this);
}
});
}
private readonly static Random _rando = new Random(4);
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
private async Task GenerateRandomFaults(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(
TimeSpan.FromSeconds(5 + (5 * _rando.NextDouble())),
token);
var activeScreens = Application.OpenForms.OfType<MockScreen>().ToArray();
activeScreens[_rando.Next(activeScreens.Length)]
.RaiseMockFault(_rando.Next(2) == 1);
}
}
FaultDetectedScreen FaultDetectedScreen { get; } = new FaultDetectedScreen();
}