我已将
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)
{
}
}
这有帮助吗?
由于
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 };
}