2016-11-13 3 views
6

Есть ли способ запустить OpenFileDialog в папке ?Могу ли я запустить OpenFileDialog DotNet в C: Users Public Documents?

Я пишу приложение C#, используя структуру DotNet. Я пытаюсь запустить OpenFileDialog, с InitialDirectory от "C:\\Users\\Public\\Documents\\" и FileName от "world.txt". К сожалению, OpenFileDialog помещает меня в ярлык Documents вместо C:\Users\Public\Documents.

Ожидаемые результаты
Я ожидаю увидеть OpenFileDialog открытым, с верхней текстовое поле, показывая > This PC > Windows7_OS (C:) > Users > Public > Documents и нижней текстовое поле, показывая world.txt. Я ожидаю, что если я нажму в верхнем текстовом поле, он отобразит C:\Users\Public\Documents.

Фактические результаты
открывается OpenFileDialog. Верхнее текстовое поле показывает > This PC > Documents, а нижнее текстовое поле показывает world.txt. Если я нажимаю в верхнем текстовом поле, он показывает Documents. Отображаемое содержимое папки не так же, как и содержимое C:\Users\Public\Documents.

Вещи, которые я попробовал
я остановил свой код в Visual Studio отладчик после следующей строки кода:
OpenFileDialog dlg = new OpenFileDialog();

В окне Immediate, я выполнил код, такой как:

dlg.FileName = "world.txt" 
? dlg.FileName 
dlg.InitialDirectory = "C:\\NonExistentDirectory\\"; 
dlg.ShowDialog(); 
dlg.InitialDirectory = "C:\\"; 
dlg.ShowDialog(); 
dlg.InitialDirectory = "C:\\Users\\"; 
dlg.ShowDialog(); 
dlg.InitialDirectory = "C:\\Users\\Public\\"; 
dlg.ShowDialog(); 
dlg.InitialDirectory = "C:\\Users\\Public\\Documents\\"; 
dlg.ShowDialog(); 

Я отменяю из каждого диалога.

Я использовал C:\WINDOWS\System32\cmd.exe к cd между C:\ и C:\Users\ и C:\Users\Public и C:\Users\Public\Documents\.

Результаты вещей, которые я пытался

  • Когда dlg.InitialDirectory = "C:\\NonExistentDirectory\\", папка для диалога изначально отображаются в This PC > Documents > Visual Studio 2015 > Projects > SimpleGame > Controller > bin > Debug". Щелчок в текстовом поле вызывает его отображение C:\Users\Owner\Documents\Visual Studio 2015\Projects\SimpleGame\Controller\bin\Debug. Поэтому я предполагаю, что OpenFileDialog бесшумно обрабатывает недопустимый InitialDirectory, не меняя каталогов. В этом случае он по умолчанию совпадает с папкой Debug в сборке моего проекта.

  • Когда dlg.InitialDirectory является "C:\\" или "C:\\Users\\" или "C:\\Users\\Public\\" диалоговые ведет себя, как ожидалось. Щелчок в верхнем текстовом поле производит C:\ или C:\Users или C:\Users\Public соответственно.

  • Когда dlg.InitialDirectory = "C:\\Users\\Public\\Documents\\" диалог ведет себя некорректно. Верхнее текстовое поле показывает > This PC > Documents, а нижнее текстовое поле показывает world.txt. Если я нажимаю в верхнем текстовом поле, он показывает Documents.Отображаемое содержимое папки не так же, как и содержимое C:\Users\Public\Documents.

  • Использование cmd.exe позволяет мне cd между папками как ожидалось, в том числе в C:\Users\Public\Documents.

Моя среда
Я бегу Microsoft Visual Studio Community 2015 Версия 14.0.23107.0 D14REL, с помощью Microsoft Visual C# 2015 Моя операционная система является ОС Windows 10 Pro.

+0

может быть привилегия вещь? вы пробовали работать против администратора? – Noctis

+1

Я также использую Windows 10 Pro и могу подтвердить это поведение. Обратите внимание: если вы используете 'dlg.InitialDirectory = 'C: \\ Users \\ Public \\ Music \\"; 'или' dlg.InitialDirectory = $ "C: \\ Users \\ {Environment.UserName} \\ Documents \\ ";", тогда диалог откроется в соответствующей библиотеке. Однако, если вы укажете путь к каталогу, который является частью настраиваемой библиотеки, диалоговое окно открывается в этот физический каталог. Возможно, диалог пытается быть «умным» при открытии в каталог, который является частью библиотеки с ['KNOWNFOLDERID'] (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx). – BACON

+0

Вы пытались восстановить каталог с помощью 'Environment.GetFolderPath (Environment.SpecialFolder.CommonDocuments)'? –

ответ

1

Если вы используете System.Windows.Forms.OpenFileDialog вы можете установить:

dialog.AutoUpgradeEnabled = false; 

диалоговое окно будет выглядеть немного устаревшую/квартира/"старой школы «но он, по крайней мере, отображает правильное содержание!

1

похоже невозможно!

Вы можете увидеть в следующей ссылке, что это ошибка в .NET Framework :( msdn link вы можете найти в последний комментарий:

Это трудно поверить, но:

это ошибка, и больше ничего.

+0

Я считаю, что это можно сделать с помощью Win32 SendMessage API с WM_SETTEXT в другом потоке, хотя, по меньшей мере, хаки, он, скорее всего, сработает. – Stavm

+0

, если вы опубликуете рабочее решение, я выберу его и удалю свой ответ ... (но если вы провалитесь, пожалуйста, поддержите мою) – silver

+0

@silver - Пожалуйста, не удаляйте свой ответ. Я рад, что у меня есть два разных полезных ответа, и вы можете обойти их обоих. – Jasper

2

, хотя, как указано на серебро, это ошибка, но может быть грубо обходится с помощью SendMessage API с WM_SETTEXT на другая нить, хотя hackish , по меньшей мере,, это, скорее всего, будет работать.

Я собрал некоторые грязные фрагменты кода, используя NSGaga's post, чтобы показать доказательство концепции, этот необработанный пример не должен использоваться как есть.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Runtime.InteropServices; 
using System.Text; 
using System.Threading; 
using System.Windows.Forms; 

namespace WindowsFormsApplication13 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void Form1_Load(object sender, EventArgs e) 
     { 
      OpenFileDialog dlg = new OpenFileDialog(); 

      //start a thread to change the dialog path just before displaying it to the user 
      Thread posThread = new Thread(setDialogPath); 
      posThread.Start(); 

      //display dialog to the user 
      DialogResult dr = dlg.ShowDialog(); 
     } 

     [DllImport("user32.dll")] 
     static extern IntPtr FindWindow(IntPtr lpClassName, string lpWindowName); 
     [DllImport("user32.dll")] 
     static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, string lParam); 
     [DllImport("user32.dll")] 
     static extern IntPtr PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam); 
     [DllImport("user32.Dll")] 
     [return: MarshalAs(UnmanagedType.Bool)] 
     static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam); 
     [DllImport("user32.dll", CharSet = CharSet.Auto)] 
     static extern IntPtr GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount); 

     private void setDialogPath() 
     { 
      const string FULL_PATH = "C:\\Users\\Public\\Documents"; 

      //messages 
      const int WM_SETTEXT = 0xC; 
      const int WM_KEYDOWN = 0x100; 
      const int WM_KEYUP = 0x101; 
      //enter key code 
      const int VK_RETURN = 0x0D; 

      //dialog box window handle 
      IntPtr _window_hwnd; 

      //how many attempts to detect the window 
      int _attempts_count = 0; 

      //get the dialog window handle 
      while ((_window_hwnd = FindWindow(IntPtr.Zero, "Open")) == IntPtr.Zero) 
       if (++_attempts_count > 100) 
        return; 
       else 
        Thread.Sleep(500); //try again 

      //in it - find the path textbox's handle. 
      var hwndChild = EnumAllWindows(_window_hwnd, "Edit").FirstOrDefault(); 

      //set the path 
      SendMessage(hwndChild, WM_SETTEXT, 0, FULL_PATH); 

      //apply the path (send 'enter' to the textbox) 
      PostMessage(hwndChild, WM_KEYDOWN, VK_RETURN, 0); 
      PostMessage(hwndChild, WM_KEYUP, VK_RETURN, 0); 
     } 


     public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam); 

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

    } 
} 

хаком, но выполнимо

+0

Я адаптировал этот код и добавил его в свою программу. Кажется, у меня проблема с кэшированием во время выполнения. (Я заметил, что другие функции моей программы очень медленные, когда я запускаю программу и становлюсь быстрее позже.) В рамках данного запуска программы, при первом открытии файла с использованием этого кода, 'OpenFileDialog' по-прежнему идет в «Документы». Последующие времена, когда я открываю файл, работают правильно, а 'OpenFileDialog' переходит в папку' C: \ Users \ Public \ Documents'. – Jasper

+0

Когда я создаю режим Release, этот код работает с перерывами. Как я упоминал ранее, этот код работает для меня в режиме отладки после первого использования. В режиме Release он не работает при первом использовании; он работает во втором использовании; и он работает с перерывами при последующем использовании. – Jasper

1

Вы могли бы подумать, что явным ответом будет использование Environment.SpecialFolder.CommonDo‌​‌​cuments, но это похоже на то, что Environment.SpecialFolder.MyDo‌​‌​cuments на Windows 10. Это должно быть ошибка в .NET!

Я столкнулся с этим вопросом сегодня и нашел решение, которое сработало для меня, и может работать для кого-то другого. Просто добавьте вложенную папку в общедоступные документы, а затем используйте ее как исходную директорию. Вероятно, лучше всего хранить ваши материалы в подпапке, зависящей от приложения, а не только от корня в любом случае. Например:

Path.Combine(Environment.SpecialFolder.CommonDocuments, "SomeSubfolder"). 
+0

Это дает вам "C: \ Users \ Public \ Documents \ SomeSubfolder \"? – Jasper

+0

Да, это именно то, что он делает! Без добавления подпапки CommonDocuments просто не работает так, как вы ожидаете. Но как только вы добавляете в подпапку, вы получаете именно то, что хотите – StalePhish

Смежные вопросы