C# OpenFileDialog 非模态可能

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

是否可以创建/拥有非模态 .net OpenFileDialog 我在主对话框中有一个 UI 元素,始终需要可供用户按下。

c# modal-dialog openfiledialog
5个回答
7
投票

不,OpenFileDialogSaveFileDialog都派生自FileDialog,它本质上是模态的,所以(据我所知)没有办法创建它们中任何一个的非模态版本。


7
投票

您可以创建一个线程并让该线程托管 OpenFileDialog。示例代码缺乏任何类型的同步,但它可以工作。

public partial class Form1 : Form
{
    OFDThread ofdThread;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        ofdThread = new OFDThread();
        ofdThread.Show();
    }
}

public class OFDThread
{
    private Thread t;
    private DialogResult result;

    public OFDThread()
    {
        t = new Thread(new ParameterizedThreadStart(ShowOFD));
        t.SetApartmentState(ApartmentState.STA);
    }

    public DialogResult DialogResult { get { return this.result; } }

    public void Show()
    {
        t.Start(this);
    }

    private void ShowOFD(object o)
    {
        OpenFileDialog ofd = new OpenFileDialog();
        result = ofd.ShowDialog();
    }
}

使用此代码,您可以添加一些内容来在 UI 线程中触发事件(调用时要小心!)以了解它们何时完成。您可以通过

访问对话框的结果
DialogResult a = ofdThread.DialogResult

来自您的 UI 线程。


1
投票

我知道我有点晚了,但你可以创建一个新的表单,无边框、透明或在显示范围之外,并显示修改该窗口的文件对话框。


0
投票

这是一篇旧帖子,但我花了 2 天达到了我想在这里展示的结果(带有“上下文”和完整但简化的代码) @Joshua 的答案对我有用(最后当我将 true 设置为 .ConfigureAwait(true) 时,请参阅第一个代码示例)。也许根据 MSDN Threading Model 的长文,我可以少写几行,我还需要再读一遍。

我的上下文是 WPF(基本 MVVM),我必须选择一个文件才能编写一些 .CSV 备份(数据网格)。我需要(成员)函数

ChooseFileFromExtension()
与非阻塞FileDialog

异步
class MainWindowExportToExcelCSV : ICommand
{
    ...
    public async void Execute(object parameter)
    {
        var usr_ctrl = parameter as UserControl;
        MyFileDialog fd = new MyFileDialog();
        const bool WhenIComeBackIStillNeedToAccessUIObjectAndThusINeedToRetrieveMyOriginalUIContext = true;
        string filename = await fd.ChooseFileFromExtension("CSV files (*.csv)|*.csv|All files (*.*)|*.*").ConfigureAwait(
            WhenIComeBackIStillNeedToAccessUIObjectAndThusINeedToRetrieveMyOriginalUIContext);

        Visual visual = (Visual)usr_ctrl.Content;
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
        {
            //look for datagrid element
        }
    }
}

以及 MyFileDialog 类的代码

using Microsoft.Win32;
...

class MyFileDialog
{
    //https://msdn.microsoft.com/en-us/library/ms741870(v=vs.110).aspx
    //Article on Threading Model
    private delegate void OneArgStrDelegate(string str);

    private void MyExternalDialog(string extensions)
    {
        SaveFileDialog fd = new SaveFileDialog();
        fd.Filter = extensions;
        fd.ShowDialog();
        tcs.SetResult(fd.FileName);
    }

    private TaskCompletionSource<string> tcs;

    public Task<string> ChooseFileFromExtension(string file_ext)
    {
        //Cf Puppet Task in Async in C#5.0 by Alex Davies
        tcs = new TaskCompletionSource<string>();

        OneArgStrDelegate fetcher = new OneArgStrDelegate(this.MyExternalDialog);
        fetcher.BeginInvoke(file_ext, null, null);
        return tcs.Task;
    }
}

fetcher.BeginInvoke()
在另一个线程中(异步)启动
SaveFileDialog
ShowDialog()
,这样主 UI 线程/窗口 (...++) 既不会被阻止也不会被禁用,就像通过简单的直接调用一样
ShowDialog()
TaskCompletionSource<string> tcs
不是 WPF UI 对象,因此可以通过另一个“单”线程访问它。

这仍然是一个我需要进一步研究的领域。我觉得没有关于这个主题的“终极”文档/书籍(也许应该再次看一下像斯蒂芬·克利里(Stephen Cleary)这样的书籍)。这段代码至少应该改进 c-sharp-asynchronous-call-without-endinvoke

所涵盖的主题

它与命名空间 Microsoft.Win32 的 FileDialog 一起使用


0
投票

问题是,所有其他形式都被禁用。所以解决方案是通过

OpenFileDialog.ShowDialog()
方法禁用其他表单后立即重新启用它们。

要打开文件对话框,只需添加一行:

var dialog = new OpenFileDialog();
using (var service = new SystemDiaogService(this))
    dialog.ShowDialog();

...然后使用此代码作为实现:

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

namespace MyNamespace
{
    public class SystemDiaogService : IDisposable
    {
        private readonly IWin32Window _owner;
        private readonly HookProc _hookProc;
        private readonly IntPtr _hHook = IntPtr.Zero;
        private WindowsFormsSynchronizationContext _synchronizationContext;

        public SystemDiaogService(IWin32Window owner)
        {
            if (owner == null) throw new ArgumentNullException("owner");

            _synchronizationContext = new WindowsFormsSynchronizationContext();

            _owner = owner;
            _hookProc = DialogHookProc;

            _hHook = SetWindowsHookEx(WH_CALLWNDPROCRET, _hookProc, IntPtr.Zero, GetCurrentThreadId());
        }

        private IntPtr DialogHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode < 0)
            {
                return CallNextHookEx(_hHook, nCode, wParam, lParam);
            }

            CWPRETSTRUCT msg = (CWPRETSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPRETSTRUCT));
            IntPtr hook = _hHook;

            if (msg.message == HCBT_ACTIVATE)
            {
                try
                {
                    ReenableOtherForms();
                }
                finally
                {
                    UnhookWindowsHookEx(_hHook);
                }
            }

            return CallNextHookEx(hook, nCode, wParam, lParam);
        }

        private void ReenableOtherForms()
        {
            foreach (Form form in Application.OpenForms)
                if (form != _owner)
                    _synchronizationContext.Post(
                        (state) =>
                        {
                            EnableWindow(form.Handle, true);
                        },
                        null);
        }

        public void Dispose()
        {
            UnhookWindowsHookEx(_hHook);
        }

        public delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);

        public delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime);

        private const int WH_CALLWNDPROCRET = 12;
        private const int HCBT_ACTIVATE = 5;

        [DllImport("kernel32.dll")]
        static extern int GetCurrentThreadId();

        [DllImport("user32.dll")]
        public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        [DllImport("user32.dll")]
        public static extern int UnhookWindowsHookEx(IntPtr idHook);

        [DllImport("user32.dll")]
        public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool EnableWindow(IntPtr hWnd, bool bEnable);

        [StructLayout(LayoutKind.Sequential)]
        public struct CWPRETSTRUCT
        {
            public IntPtr lResult;
            public IntPtr lParam;
            public IntPtr wParam;
            public uint message;
            public IntPtr hwnd;
        };
    }
}

PS

这个想法基于众所周知的

DialogCenteringService
,提到过here

© www.soinside.com 2019 - 2024. All rights reserved.