Может ли OpenFileDialog автоматически выбирать файл со значением, заданным в FileName, если также установлен InitialDirectory? - PullRequest
25 голосов
/ 02 декабря 2011

Это не придирчиво, но почему файл не выбирается автоматически, если он существует и оба параметра FileName и InitialDirectory установлены правильно?

У меня есть OpenFileDialog с FileName и InitialDirectory, установленными правильно, и файлы находятся в этой папке. Почему файл не выбирается при запуске метода ShowDialog()?

Файл не выбран, но было бы неплохо, если бы он был выбран, поэтому мне не пришлось бы прокручивать вниз, чтобы выбрать следующий файл рядом с ним.

Есть предложения?

Ответы [ 2 ]

11 голосов
/ 30 сентября 2016

Может быть, он не идеален, но как-то оправдывает ожидания.

У меня есть 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
    }

Результат;

openfiledialog

Редактировать 1;

Хорошо, для .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
        }

Надежда помогает,

8 голосов
/ 30 сентября 2016

SendKeys

SendKeys выглядит как хак, но это самый простой способ, и по причинам, описанным ниже, вероятно, более устойчивый в долгосрочной перспективе, чем использование Win32 API.

Использование await Task.Delay(250); рискованно .Время ожидания может быть слишком коротким, прежде чем в медленной системе появится диалоговое окно, с другой стороны, если время ожидания увеличено, быстрый пользователь может взаимодействовать, конкурируя с автоматизацией 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;
}

Примечание: Знак плюс (+), каретка (^), знак процента (%), тильда (~) и скобки () имеют специальные значения для SendKeys.Чтобы указать один из этих символов, заключите его в фигурные скобки ({}).

Так, например, если в имени вашего файла есть круглые скобки, вам нужно экранировать их фигурными скобками:

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

Win32 (попытка - неполная и не работает)

Вы можете попробовать перебрать дочерние окна 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;
 }

Помощник класса 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();
    }
}

Когда у вас есть Hwnd для ListView, вы можете попытаться установить его элемент, но вам нужно будет использовать NativeWindow, и он не будет красивым,см. эту статью, чтобы понять, что я имею в виду: http://www.codeproject.com/Articles/2890/Using-ListView-control-under-Win-API

Как бы мне не хотелось это признавать, это один из редких случаев, когда я предлагал SendKeys вместо Win32 API.Win32 Api могут даже не работать в конце, и SendKeys с большей вероятностью продолжит работать, если / когда API изменятся.Например, мы видели, что с XP до Vista API FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle) теперь практически бесполезен, поскольку Aero скрывает заголовки Windows, необходимые для параметра windowTitle.Одним из решений является использование Classic Theme, но не многие примут это, поэтому это одна из причин, по которой я бы не стал рассчитывать, что Win32 apis сделает эту конкретную вещь и просто использует SendKeys.См. Здесь:

enter image description here

Я больше не могу заставить работать этот материал: 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...