C# OpenFileDialog 非模态可能

发布于 2024-07-13 03:41:53 字数 68 浏览 11 评论 0原文

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

Is it possible to create/have a non-modal .net OpenFileDialog I have a UI element in the main dialog which always need to be available for the user to press.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

握住你手 2024-07-20 03:41:53

否,OpenFileDialogSaveFileDialog 均源自 FileDialog,本质上是模态的,所以(据我所知)没有办法创建其中任何一个的非模态版本。

No, OpenFileDialog and SaveFileDialog are both derived from FileDialog, which is inherently modal, so (as far as I know) there's no way of creating a non-modal version of either of them.

辞取 2024-07-20 03:41:53

您可以创建一个线程并让该线程托管 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 线程

You can create a thread and have the thread host the OpenFileDialog. Example code is lacking any kind of synchronization but it works.

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();
    }
}

With this code you could add something to fire an event in your UI thread (be careful with invoking!) to know when they're done. You can access the result of the dialog by

DialogResult a = ofdThread.DialogResult

from your UI thread.

烟花肆意 2024-07-20 03:41:53

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

I know I am a little late but you may create a new form, borderless, transparent or outside the display bounds and show the file dialog modding that window.

留一抹残留的笑 2024-07-20 03:41:53

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

我的上下文是 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; tcs 不是 WPF UI 对象,因此可以通过另一个“单”线程访问它。

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

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

It is an old post but I spend 2 days reaching the result I want to present here ( with "context" and complete but simplified code )
@Joshua 's answer worked for me ( at last when I put true to .ConfigureAwait(true), see first code sample). Maybe I was able to write less lines based on the long article of MSDN Threading Model that I still need to read once again.

My context is WPF ( basic MVVM ) and I must choose a file in order to write some .CSV backup ( of a datagrid ). I need that the (member) function ChooseFileFromExtension() be asynchronous with a non-blocking 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
        }
    }
}

and the code for MyFileDialog class

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;
    }
}

The fetcher.BeginInvoke() launches (asynchronously) the SaveFileDialog ShowDialog() in another thread so that the main UI Thread / Window (...++) are neither blocked nor disabled as they would have been with a simple direct call to ShowDialog(). TaskCompletionSource<string> tcs is not an WPF UI Object so its access by another "single" thread is OK.

It is still a field that I need to study further. I feel there is no "ultimate" documentation/book on the subject ( maybe should have a second look to books like the one from Stephen Cleary again). This code should be improved at least with topic covered on c-sharp-asynchronous-call-without-endinvoke

It works with the FileDialog of namespace Microsoft.Win32

梦忆晨望 2024-07-20 03:41:53

问题是,所有其他形式都被禁用。 因此,解决方案是通过 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,提到 此处

The problem is, that all other forms are disabled. So the solution is to re-enable the other forms immediately after disabling them by the OpenFileDialog.ShowDialog() method.

To open the file dialog, just add one line:

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

... and then use this code as implementation:

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

The idea based on the well known DialogCenteringService, mentioned here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文