【问题标题】:Closing OpenFileDialog/SaveFileDialog关闭 OpenFileDialog/SaveFileDialog
【发布时间】:2019-01-16 07:52:32
【问题描述】:

作为自动注销的一部分,我们需要关闭子窗体。我们可以通过从计时器线程迭代 Application.OpenForms 来关闭子表单。由于未列出 OpenFileDialog,因此我们无法使用 Application.OpenForms 关闭 OpenFileDialog/SaveFileDialog。

如何关闭 OpenFileDialog 和 CloseFileDialog?

【问题讨论】:

  • 关闭表单时。它上的所有对话都应该自动关闭,因为它们就像子窗体。不是这样吗?

标签: c# .net winforms


【解决方案1】:

这将需要 pinvoke,对话框不是表单而是原生 Windows 对话框。基本方法是枚举所有顶层窗口并检查它们的类名是否为“#32770”,即 Windows 拥有的所有对话框的类名。并通过发送 WM_CLOSE 消息强制对话框关闭。

向您的项目添加一个新类并粘贴如下所示的代码。注销计时器到期时调用 DialogCloser.Execute()。 然后关闭表单。该代码适用于 MessageBox、OpenFormDialog、FolderBrowserDialog、PrintDialog、ColorDialog、FontDialog、PageSetupDialog 和 SaveFileDialog。

using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

static class DialogCloser {
    public static void Execute() {
        // Enumerate windows to find dialogs
        EnumThreadWndProc callback = new EnumThreadWndProc(checkWindow);
        EnumThreadWindows(GetCurrentThreadId(), callback, IntPtr.Zero);
        GC.KeepAlive(callback);
    }

    private static bool checkWindow(IntPtr hWnd, IntPtr lp) {
        // Checks if <hWnd> is a Windows dialog
        StringBuilder sb = new StringBuilder(260);
        GetClassName(hWnd, sb, sb.Capacity);
        if (sb.ToString() == "#32770") {
            // Close it by sending WM_CLOSE to the window
            SendMessage(hWnd, 0x0010, IntPtr.Zero, IntPtr.Zero);
        }
        return true;
    }

    // P/Invoke declarations
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
    [DllImport("user32.dll")]
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
    [DllImport("kernel32.dll")]
    private static extern int GetCurrentThreadId();
    [DllImport("user32.dll")]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

【讨论】:

  • 这意味着我需要获取主窗口线程的线程ID。我说的对吗?
  • 在 UI 线程上调用这段代码,GetCurrentThreadId() 函数已经获得了正确的 id。无论如何,您都不能在工作线程上关闭表单,所以这段代码应该是好的。
  • 如果我使用 SendMessage,有没有可能出现死锁?
  • 不,一切都在同一个线程上运行。此时,您应该将此代码放入您的项目中,并像通常使用您自己编写的任何代码一样对其进行测试。只有这样,您才能对可靠性充满信心。我无法帮助您更快地到达那里。
  • 谢谢汉斯。我会做进一步的研究。
【解决方案2】:

我不会在一个线程中关闭所有子表单,而是会引发每个子表单都可以/必须订阅的事件。 在提高你的表格可以决定现在做什么。清理某些东西,保持状态,向服务器发送消息 在您的表单范围内,您可以访问 openfiledialog 并尝试关闭它。

【讨论】:

    【解决方案3】:

    [解决方法] 这里是示例:

    您应该定义完全透明的窗口 ex。 “运输”;

    每次需要显示对话框时,都需要显示TRANSP并将TRANSP作为参数传递给ShowDialog方法。

    当应用程序关闭时 - 调用 TRANSP 窗口的 Close() 方法。子对话框将关闭。

        public partial class MainWindow : Window
    {
        OpenFileDialog dlg;
        TranspWnd transpWnd;
    
        public MainWindow()
        {
            InitializeComponent();
    
            Timer t = new Timer();
            t.Interval = 2500;
            t.Elapsed += new ElapsedEventHandler(t_Elapsed);
            t.Start();
        }
    
        void t_Elapsed(object sender, ElapsedEventArgs e)
        {
            Dispatcher.BeginInvoke(new Action(() =>
            {
                transpWnd.Close();
            }), null);
        }
    
        private void button1_Click(object sender, RoutedEventArgs e)
        {
            transpWnd = new TranspWnd();
            transpWnd.Visibility = System.Windows.Visibility.Hidden; //doesn't works right
            transpWnd.Show();
    
            dlg = new OpenFileDialog();
            dlg.ShowDialog(transpWnd);
        }
    }
    

    【讨论】:

    • 问题被标记为 WinForms。
    【解决方案4】:

    我的回答在概念上类似于Hans Passant's answer

    但是,使用GetCurrentThreadId() 作为tid 参数到EnumThreadWindows 对我不起作用,因为我是从另一个线程调用它。如果您正在这样做,那么请枚举进程的线程 ID 并尝试每一个,直到找到所需的窗口:

    ProcessThreadCollection currentThreads = Process.GetCurrentProcess().Threads;
    foreach (ProcessThread thread in currentThreads) {
        CloseAllDialogs(thread.Id);
    }
    

    或保存执行ShowDialog 的线程ID 以打开CommonDialog

    threadId = GetCurrentThreadId();
    threadIds.Add(threadId);
    result = dialog.ShowDialog()
    threadIds.Remove(threadId);
    

    然后:

    foreach (int threadId in threadIds) {
        CloseAllDialogs(threadId);
    }
    

    CloseAllDialogs 的样子:

    public void CloseAllDialogs(int threadId) {
        EnumThreadWndProc callback = new EnumThreadWndProc(checkIfHWNDPointsToWindowsDialog);
        EnumThreadWindows(threadId, callback, IntPtr.Zero);
        GC.KeepAlive(callback);
    }
    
    private bool checkIfHWNDPointsToWindowsDialog(IntPtr hWnd, IntPtr lp) {
        StringBuilder sb = new StringBuilder(260);
        GetClassName(hWnd, sb, sb.Capacity);
        if (sb.ToString() == "#32770") {
            SendMessage(hWnd, 0x0010, IntPtr.Zero, IntPtr.Zero);
        }
        return true;
    }
    
    private delegate bool EnumThreadWndProc(IntPtr hWnd, IntPtr lp);
    
    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool EnumThreadWindows(int tid, EnumThreadWndProc callback, IntPtr lp);
    
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern int GetCurrentThreadId();
    
    [DllImport("user32.dll", SetLastError = true)]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder buffer, int buflen);
    
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多