windows 如果还设置了 InitialDirectory,OpenFileDialog 是否可以自动选择具有 FileName 中设置的值的文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8347585/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 18:34:15  来源:igfitidea点击:

Can OpenFileDialog automatically select the file with the value set in FileName if InitialDirectory is set as well?

c#windowswinforms

提问by Doug Barense

This is nit picky but why doesn't the file get automatically selected if it exists and both FileNameand InitialDirectoryare set correctly?

这是NIT挑剔,但为什么不将文件都会自动如果它存在,并都选择FileNameInitialDirectory设置是否正确?

I have an OpenFileDialogwith both FileNameand InitialDirectoryset correctly and the files exists in this folder. Why isn't the file selected when I run the ShowDialog()method?

我有一个OpenFileDialogFileName并且InitialDirectory设置正确,文件存在于这个文件夹中。为什么我运行该ShowDialog()方法时没有选择文件?

No file is selected but it would be nice if it was selected so I wouldn't have to scroll down to select the next file adjacent to it.

没有选择文件,但如果选择了它会很好,这样我就不必向下滚动来选择与它相邻的下一个文件。

Any suggestions?

有什么建议?

回答by Berkay

Maybe it is not perfect but it meets the expectation somehow.

也许它并不完美,但它以某种方式满足了期望。

I have a Buttonthat Shows OpenFileDialogon click event. And async method that will SendKeys to OpenFileDialog.

我有一个Button显示OpenFileDialog点击事件。以及将 SendKeys 发送到 OpenFileDialog 的异步方法。

    private async void button1_Click(object sender, EventArgs e){
                string initialDir = "directory\";
                string FileName = "filename.smthng";
                string combinedDir = initialDir + FileName;
                if (File.Exists(combinedDir)) // if there is a file with that name at that directory
                {
                    openFileDialog1.InitialDirectory = initialDir; // setting directory name
                    openFileDialog1.FileName = FileName; // filename
                    BeginInvoke((Action)(() => openFileDialog1.ShowDialog())); // we need to use BeginInvoke to continue to the following code.
                    await SendKey(FileName); // Sends Key to Dialog 
                }
                else // if there is not file with that name works here because no keys need to send.
                {
                    openFileDialog1.InitialDirectory = initialDir;
                    openFileDialog1.FileName = FileName;
                    openFileDialog1.ShowDialog();
                }

    }

    private async Task SendKey(string FileName){
            await Task.Delay(250); // Wait for the Dialog shown at the screen
            SendKeys.SendWait("+{TAB}"); // First Shift + Tab moves to Header of DataGridView of OpenFileDialog
            SendKeys.SendWait("+{TAB}"); // Second Shift + Tab moves to first item of list
            SendKeys.SendWait(FileName); // after sending filename will directly moves it to the file that we are looking for
    }

Result;

结果;

openfiledialog

openfiledialog

Edit 1;

编辑 1;

Okay, For .Net 3.5there is also TaskParalelLibrarybut using Thread will be much easier.

好的,因为.Net 3.5也有,TaskParalelLibrary但使用 Thread 会容易得多。

 Thread t;
 private const string initialDir = "C:\";
 private const string FileName = "test.txt";
 private void button1_Click(object sender, EventArgs e){
       string combinedDir = initialDir + FileName;
       if (File.Exists(combinedDir)) // if there is a file with that name at that directory
            {
                openFileDialog1.InitialDirectory = initialDir; // setting directory name
                openFileDialog1.FileName = FileName; // filename
                BeginInvoke((Action)(() => openFileDialog1.ShowDialog())); // we need to use BeginInvoke to continue to the following code.
                t = new Thread(new ThreadStart(SendKey)); // Sends Key to Dialog with an seperate Thread.
                t.Start(); // Thread starts.
            }
            else // if there is not file with that name works here because no keys need to send.
            {
                openFileDialog1.InitialDirectory = initialDir;
                openFileDialog1.FileName = FileName;
                openFileDialog1.ShowDialog();
            }
        }

        private void SendKey()
        {
            Thread.Sleep(100); // Wait for the Dialog shown at the screen
            SendKeys.SendWait("+{TAB}"); // First Shift + Tab moves to Header of DataGridView of OpenFileDialog
            SendKeys.SendWait("+{TAB}"); // Second Shift + Tab moves to first item of list
            SendKeys.SendWait(FileName); // after sending filename will directly moves it to the file that we are looking for
        }

Hope helps,

希望有所帮助,

回答by Jeremy Thompson

SendKeys

发送密钥

SendKeys seems like a hack but it is the easiest and for the reasons explained below probably more sustainable long term than using Win32 APIs.

SendKeys 看起来像一个 hack 但它是最简单的,并且由于下面解释的原因可能比使用 Win32 API 更可持续。

Relying on a await Task.Delay(250);is risky. The timeout might be too quick before a dialog shows on a slow system, on the other hand if the timeout is increased a fast user might interact while competing with the SendKeys automation.

依靠一个await Task.Delay(250);是有风险的。在慢速系统上显示对话框之前超时可能太快,另一方面,如果超时增加,快速用户可能会在与 SendKeys 自动化竞争时进行交互。

I recommend you use the Message Loop to notify when to SendKeys.

我建议您使用 Message Loop 来通知何时发送 SendKeys

bool IsOpenFileDialog = false;
private void openDialog_Click(object sender, EventArgs e)
{
    IsOpenFileDialog = true;
    openFileDialog1.ShowDialog();
    IsOpenFileDialog = false;
}

uint _lastDialogHandle = 0;
protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
    if (!IsOpenFileDialog) return;
    if (m.Msg == 289) //Notify of message loop
    {
        try
        {
            uint dialogHandle = (uint)m.LParam; //handle of the file dialog
            if (dialogHandle != _lastDialogHandle) //only when not already changed
            {
                _lastDialogHandle = dialogHandle;
                SendKeys.SendWait("+{TAB}");
                SendKeys.SendWait("+{TAB}");   
                SendKeys.SendWait(EscapeSendKeySpecialCharacters("temp.xls"));

                //Or try via Win32
                //List<string> childWindows = GetDialogChildWindows(dialogHandle);
                //TODO set ListView Item
            }
        }
        catch (Exception ex) {}
    }
}

private string EscapeSendKeySpecialCharacters(string sentence)
{
    sentence = sentence.Replace("+", "{+}");
    sentence = sentence.Replace("^", "{^}");
    sentence = sentence.Replace("%", "{%}");
    sentence = sentence.Replace("~", "{~}");
    sentence = sentence.Replace("(", "{(}");
    sentence = sentence.Replace(")", "{)}");
    return sentence;
}

Note: The plus sign (+), caret (^), percent sign (%), tilde (~), and parentheses () have special meanings to SendKeys. To specify one of these characters, enclose it within braces ({}).

注意:加号 (+)、脱字符 (^)、百分号 (%)、波浪号 (~) 和括号 () 对 SendKeys 具有特殊含义。要指定这些字符之一,请将其括在大括号 ({}) 内。

So for example if your filename has parentheses you need to escape them with curly brackets:

因此,例如,如果您的文件名有括号,您需要用大括号将它们转义:

SendKeys.SendWait("New Text Document - Copy {(}8{)}.txt");


Win32 (attempt - incomplete and not working)

Win32(尝试 - 不完整且不工作)

You could try iterating through the OpenFileDialogs' Child Windows to find the listview control:

您可以尝试遍历 OpenFileDialogs 的子窗口以查找列表视图控件:

private List<string> GetDialogChildWindows(dialogHandle) {
//IEnumerable<IntPtr> allWindows = EnumWindowsAndChild.EnumAllWindows((IntPtr)dialogHandle, "Dialog");
List<IntPtr>  children = EnumWindowsAndChild.GetChildWindows((IntPtr)dialogHandle);
List<string> childWindows = new List<string>();
foreach (IntPtr ptr in children)
{
    string s = ptr.ToString() + ",  " + EnumWindowsAndChild.GetWindowsTextTitle(ptr) + ", " + EnumWindowsAndChild.GetWindowClassName(ptr);
System.Diagnostics.Debug.WriteLine(s);
        childWindows.Add(s);
   }
  return childWindows;
 }

The Enum Windows and Child class helper:

Enum Windows 和 Child 类助手:

public static class EnumWindowsAndChild
{
    public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);

    [DllImport("user32.Dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static public extern IntPtr GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);

    private static bool EnumWindow(IntPtr handle, IntPtr pointer)
    {
        GCHandle gch = GCHandle.FromIntPtr(pointer);
        List<IntPtr> list = gch.Target as List<IntPtr>;
        if (list == null)
            throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
        list.Add(handle);
        return true;
    }

    public static List<IntPtr> GetChildWindows(IntPtr parent)
    {
        List<IntPtr> result = new List<IntPtr>();
        GCHandle listHandle = GCHandle.Alloc(result);
        try
        {
            Win32Callback childProc = new Win32Callback(EnumWindow);
            EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
        }
        finally
        {
            if (listHandle.IsAllocated)
                listHandle.Free();
        }
        return result;
    }

    public static string GetWinClass(IntPtr hwnd)
    {
        if (hwnd == IntPtr.Zero)
            return null;
        StringBuilder classname = new StringBuilder(100);
        IntPtr result = GetClassName(hwnd, classname, classname.Capacity);
        if (result != IntPtr.Zero)
            return classname.ToString();
        return null;
    }

    public static IEnumerable<IntPtr> EnumAllWindows(IntPtr hwnd, string childClassName)
    {
        List<IntPtr> children = GetChildWindows(hwnd);
        if (children == null)
            yield break;
        foreach (IntPtr child in children)
        {
            if (GetWinClass(child) == childClassName)
                yield return child;
            foreach (var childchild in EnumAllWindows(child, childClassName))
                yield return childchild;
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetWindowTextLength(HandleRef hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int GetWindowText(HandleRef hWnd, StringBuilder lpString, int nMaxCount);

    public static string GetWindowsTextTitle(IntPtr hwnd)
    {
        int capacity = GetWindowTextLength(new HandleRef(null, hwnd)) * 2;
        StringBuilder stringBuilder = new StringBuilder(capacity);
        GetWindowText(new HandleRef(null, hwnd), stringBuilder, stringBuilder.Capacity);
        return stringBuilder.ToString();

    }

    public static string GetWindowClassName(IntPtr hWnd)
    {
        StringBuilder buffer = new StringBuilder(128);
        GetClassName(hWnd, buffer, buffer.Capacity);
        return buffer.ToString();
    }
}

Once you have the Hwnd to the ListView you could try to set its item, but you'd need to use a NativeWindow and its not going to be pretty, see this article to know what I mean: http://www.codeproject.com/Articles/2890/Using-ListView-control-under-Win-API

一旦你拥有了 ListView 的 Hwnd,你就可以尝试设置它的项目,但你需要使用 NativeWindow 并且它不会很漂亮,请参阅这篇文章以了解我的意思:http://www.codeproject。 com/Articles/2890/Using-ListView-control-under-Win-API

As much as I hate to admit it, this is one of the rare cases where I'd suggest SendKeys over the Win32 API. Win32 Api's might not even work in the end and SendKeys is much more likely to continue to work if/when the APIs change. For example we saw with XP to Vista, the API FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle)is now almost useless since Aero hides the Windows Captions which are needed for the windowTitle parameter. One solution is to use Classic Theme, not many people will accept that, so this is one of the reasons I wouldn't count on Win32 apis to do this particularthing and simply use SendKeys instead. See here:

尽管我不愿承认,但这是我建议通过 Win32 API 使用 SendKeys 的罕见情况之一。Win32 Api 最终甚至可能无法工作,如果/当 API 发生变化时,SendKeys 更有可能继续工作。例如,我们在 XP 到 Vista 中看到,APIFindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle)现在几乎无用,因为 Aero 隐藏了 windowTitle 参数所需的 Windows Captions。一种解决方案是使用经典主题,没有多少人会接受这一点,所以这就是我不指望 Win32 api 来做这个特定事情而只是使用 SendKeys 的原因之一。看这里:

enter image description here

enter image description here

I couldn't get any of this stuff to work anymore: https://bytes.com/topic/c-sharp/answers/262498-openfiledialog-how-select-files-coding, which is from a Microsoft email Dist.

我再也无法使用这些东西了:https: //bytes.com/topic/c-sharp/answers/262498-openfiledialog-how-select-files-coding,它来自 Microsoft 电子邮件 Dist。

// Getting the handle of the ListBox in the OpenFileDialog dialog.
uint listviewHandle = FindWindowEx(dialogHandle, 0,
"SHELLDLL_DefView", "");

// Sending message to the ListBox to set the view to Thumbnails
//Icons=0x7029, List=0x702b, Details=0x702c, Thumbnails=0x702d,
Tiles=0x702e
SendMessage(listviewHandle, 0x0111/*WM_COMMAND*/, (uint)0x702d, 0);

// Sending message to the ListBox to select all items.
SendMessage(listviewHandle, 0x0111/*WM_COMMAND*/, (uint)0x00017021,
(uint)0);