OpenFileDialog:按照用户选择文件的顺序加载文件

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

我已将

Multiselect
设置为 true,以便能够一次加载多个文件。问题是它忽略了用户选择文件的顺序,
FileNames
的列表始终相同(如果我以不同的顺序选择同一组文件)。

我的问题是:是否有可能实现预期的行为? (按照所选顺序存储路径)。

   List<string> filePaths = new List<string>();
        Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
        dlg.Multiselect = true;
        dlg.DefaultExt = ".txt";
        // dlg.Filter = my filter //


        Nullable<bool> result = dlg.ShowDialog();
        if (result == true)
        {
                foreach (string file in dlg.FileNames)
            {

            }

       }
c# wpf openfiledialog
2个回答
1
投票

0
投票

由于

OpenFileDialog
是一个密封类,因此可能需要以某种形式进行黑客攻击。我个人破解它的方法是有一个轮询循环,该循环对 P/Invoke 进行调用,以便在选择文件名时抓取保存文件名的子窗口,并且当更改时维护一个我们可以使用的外部列表。将为新选择的任何文件名调用
NamesInOrder
,即 FIFO。

在本例中,我首先选择了 B,然后选择了 C 和 A。

public MainForm()
{
    InitializeComponent();
    StartPosition = FormStartPosition.CenterScreen;
    buttonOpen.Click += (sender, e) =>
    {
        ExecOpenInOrder(sender, e);
        MessageBox.Show(string.Join(Environment.NewLine, NamesInOrder), caption: "Names in Order");
    }; 
}


显示打开文件对话框的方法

private void ExecOpenInOrder(object? sender, EventArgs e)
{
    NamesInOrder.Clear();
    openFileDialog.Title = OPEN_FILE_TITLE;
    openFileDialog.InitialDirectory =
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            Assembly.GetEntryAssembly().GetName().Name
    );
    Directory.CreateDirectory(openFileDialog.InitialDirectory);
    openFileDialog.Multiselect = true;
    var dialogResult = DialogResult.None;
    localStartPollForChanges();
    openFileDialog.ShowDialog();
    .
    .
    .
}
List<string> NamesInOrder { get; } = new List<string>();

检测 OpenFileDialog 的 hWnd

虽然 OpenFileDialog 的标题默认为“Open”,但调用

GetWindowText
会返回空,除非我们明确设置一个可识别的值,在本例中使用
const string OPEN_FILE_TITLE = "Open"
。一旦我们获得它,我们将枚举它的子窗口。

    async void localStartPollForChanges()
    {
        while (dialogResult == DialogResult.None)
        {
            var hWndParent = GetForegroundWindow();
            StringBuilder sb = new StringBuilder(MAX_STRING);
            if (hWndParent != IntPtr.Zero)
            {
                GetWindowText(hWndParent, sb, MAX_STRING);
            }
            Debug.WriteLine($"\nForeground window title: {sb}");
            if (sb.ToString() == OPEN_FILE_TITLE)
            {
                EnumChildWindows(hWndParent, localEnumChildWindowCallback, IntPtr.Zero);
            }
            await Task.Delay(TimeSpan.FromSeconds(0.1));
        }
    }

找到正确的子窗口并阅读其文本

我们正在寻找一个类为“”ComboBoxEx32”的子窗口。

    bool localEnumChildWindowCallback(IntPtr hWnd, IntPtr lParam)
    {
        StringBuilder className = new StringBuilder(MAX_STRING);
        GetClassName(hWnd, className, MAX_STRING);

        if (className.ToString() == "ComboBoxEx32")
        {
            StringBuilder windowText = new StringBuilder(MAX_STRING);
            GetWindowText(hWnd, windowText, MAX_STRING);

            // Detect multiselect
            var names = localGetNames(windowText.ToString());
            foreach (var name in NamesInOrder.ToArray())
            {
                if(!names.Contains(name))
                {
                    // Remove any names that aren't in new selection
                    NamesInOrder.Remove(name);
                }
            }
            foreach (var name in names.ToArray())
            {
                // If NamesInOrder doesn't already hold the name, add it to the end.
                if (!NamesInOrder.Contains(name))
                {
                    NamesInOrder.Add(name);
                }
            }
            Debug.WriteLine(string.Join(Environment.NewLine, NamesInOrder));

            if (windowText.ToString().Contains(".txt"))
            {
                return false;
            }
        }
        return true;
    }

提取文件名

检索此窗口文本时,使用

RegEx
分隔多个文件名,其中名称用引号引起来并用空格分隔。

    string[] localGetNames(string text)
    {
        string[] names =
                Regex
                .Matches(text.ToString(), pattern: @"""(.*?)\""")
                .Select(_ => _.Value.Trim(@"""".ToCharArray()))
                .ToArray();
        // But it there's only one name, the pattern
        // will never 'hit' so return the single name 
        return names.Any() ? names : new string[] { text };
    }
© www.soinside.com 2019 - 2024. All rights reserved.