ShowDialog() 未关闭

问题描述 投票:0回答:1

自从我向它添加了所有者以来,我就遇到了无法关闭模态表单的问题。 它只是一个非常简单的表单,上面有一个关闭按钮。根据我所在的屏幕,如果弹出表单,关闭按钮将不会执行任何操作,并且表单将锁定。

似乎每周我都会添加更多的编组(暂时修复它,然后最终再次锁定),但我不太清楚出了什么问题。我真的有点不知道在这里做什么希望有人知道!

`这是我现在调用表单的方式:

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;
    }
}
c# winforms
1个回答
0
投票

您描述了一种系统性且持续的情况,其中“每周”您都会尝试不同的编组方法,因此我将展示一个最小的模拟来进行实验和适应,并希望能解决您的问题。您的代码表明您正在名为

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();
}
© www.soinside.com 2019 - 2024. All rights reserved.