このスクリーンショットに示されているように、選択したフォルダーはビューに表示されません。選択したフォルダーを表示するには、下にスクロールする必要があります。
同じダイアログに、選択したフォルダが別のコンピュータに表示される
私はこれを、どちらも Windows 7 を搭載した 2 台のコンピューターで実行しました。1 台では正常に動作しますが、2 台目では動作しません。コードの問題ではなく、Windows 環境に問題があるようです。修正方法を提案してくれる人はいますか?
コードに変更はありません。異なるドライブからのより長いパスを使用しましたが、結果は同じです。
private void TestDialog_Click ( object sender, EventArgs e )
{
//Last path store the selected path, to show the same directory as selected on next application launch.
//Properties.Settings.Default.LastPath
FolderBrowserDialog dlgFolder = new FolderBrowserDialog ();
dlgFolder.RootFolder = Environment.SpecialFolder.DesktopDirectory;
dlgFolder.SelectedPath = Properties.Settings.Default.LastPath;
if (dlgFolder.ShowDialog () == System.Windows.Forms.DialogResult.OK)
{
Properties.Settings.Default.LastPath = dlgFolder.SelectedPath;
Properties.Settings.Default.Save ();
}
}
ベストアンサー1
根本的な問題は、 の設計上の決定が不適切であることですFolderBrowserDialog
。まず、 はFolderBrowserDialog
.NET コントロールではなく、 であり、Common Dialog
Windows の一部であることを認識する必要があります。このダイアログの設計者は、ダイアログが表示されて最初のフォルダが選択された後、TreeView コントロールにメッセージを送信しないことを選択しましTVM_ENSUREVISIBLE
た。このメッセージにより、TreeView コントロールがスクロールされ、現在選択されている項目がウィンドウに表示されます。
FolderBrowserDialog
したがって、この問題を解決するには、メッセージの一部である TreeView を送信するだけでTVM_ENSUREVISIBLE
、すべてうまくいきます。そうですよね? まあ、そんなに早くは行きません。確かにこれは答えですが、いくつか問題があります。
まず、 は
FolderBrowserDialog
実際には .NET コントロールではないため、内部コレクションがありませんControls
。つまり、.NET から TreeView 子コントロールを検索してアクセスすることはできません。第二に、.NET
FolderBrowserDialog
クラスの設計者は、シールこのクラス。この残念な決定により、このクラスから派生してウィンドウ メッセージ ハンドラーをオーバーライドすることができなくなりました。これが可能であれば、メッセージ ハンドラーでメッセージTVM_ENSUREVISIBLE
を受け取ったときにメッセージを投稿しようとしたかもしれませんWM_SHOWWINDOW
。TVM_ENSUREVISIBLE
3 番目の問題は、ツリー ビュー コントロールが実際のウィンドウとして実際に存在するまでメッセージを送信できず、ShowDialog
メソッドを呼び出すまで存在しないことです。ただし、このメソッドはブロックされるため、このメソッドが呼び出されると、メッセージを投稿する機会がなくなります。
これらの問題を回避するために、 を表示するために使用できる単一のメソッドを持つ静的ヘルパー クラスを作成しFolderBrowserDialog
、選択したフォルダーまでスクロールさせます。Timer
ダイアログのメソッドを呼び出す直前にショートを開始し、ハンドラーでコントロールShowDialog
のハンドルを追跡して(つまり、ダイアログが表示された後)、メッセージを送信することでこれを実現します。TreeView
Timer
TVM_ENSUREVISIBLE
このソリューションは、 に関する事前の知識に依存するため、完璧ではありません。FolderBrowserDialog
具体的には、ウィンドウ タイトルを使用してダイアログを検索します。これは、英語以外のインストールでは機能しません。タイトル テキストやクラス名ではなく、ダイアログ項目 ID を使用してダイアログ内の子コントロールを追跡します。これは、時間の経過とともに信頼性が高くなると感じたためです。
このコードは、Windows 7 (64 ビット) および Windows XP でテストされています。
コードは次のとおりです: (次のものが必要になる場合があります: using System.Runtime.InteropServices;
)
public static class FolderBrowserLauncher
{
/// <summary>
/// Using title text to look for the top level dialog window is fragile.
/// In particular, this will fail in non-English applications.
/// </summary>
const string _topLevelSearchString = "Browse For Folder";
/// <summary>
/// These should be more robust. We find the correct child controls in the dialog
/// by using the GetDlgItem method, rather than the FindWindow(Ex) method,
/// because the dialog item IDs should be constant.
/// </summary>
const int _dlgItemBrowseControl = 0;
const int _dlgItemTreeView = 100;
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
/// <summary>
/// Some of the messages that the Tree View control will respond to
/// </summary>
private const int TV_FIRST = 0x1100;
private const int TVM_SELECTITEM = (TV_FIRST + 11);
private const int TVM_GETNEXTITEM = (TV_FIRST + 10);
private const int TVM_GETITEM = (TV_FIRST + 12);
private const int TVM_ENSUREVISIBLE = (TV_FIRST + 20);
/// <summary>
/// Constants used to identity specific items in the Tree View control
/// </summary>
private const int TVGN_ROOT = 0x0;
private const int TVGN_NEXT = 0x1;
private const int TVGN_CHILD = 0x4;
private const int TVGN_FIRSTVISIBLE = 0x5;
private const int TVGN_NEXTVISIBLE = 0x6;
private const int TVGN_CARET = 0x9;
/// <summary>
/// Calling this method is identical to calling the ShowDialog method of the provided
/// FolderBrowserDialog, except that an attempt will be made to scroll the Tree View
/// to make the currently selected folder visible in the dialog window.
/// </summary>
/// <param name="dlg"></param>
/// <param name="parent"></param>
/// <returns></returns>
public static DialogResult ShowFolderBrowser( FolderBrowserDialog dlg, IWin32Window parent = null )
{
DialogResult result = DialogResult.Cancel;
int retries = 10;
using (Timer t = new Timer())
{
t.Tick += (s, a) =>
{
if (retries > 0)
{
--retries;
IntPtr hwndDlg = FindWindow((string)null, _topLevelSearchString);
if (hwndDlg != IntPtr.Zero)
{
IntPtr hwndFolderCtrl = GetDlgItem(hwndDlg, _dlgItemBrowseControl);
if (hwndFolderCtrl != IntPtr.Zero)
{
IntPtr hwndTV = GetDlgItem(hwndFolderCtrl, _dlgItemTreeView);
if (hwndTV != IntPtr.Zero)
{
IntPtr item = SendMessage(hwndTV, (uint)TVM_GETNEXTITEM, new IntPtr(TVGN_CARET), IntPtr.Zero);
if (item != IntPtr.Zero)
{
SendMessage(hwndTV, TVM_ENSUREVISIBLE, IntPtr.Zero, item);
retries = 0;
t.Stop();
}
}
}
}
}
else
{
//
// We failed to find the Tree View control.
//
// As a fall back (and this is an UberUgly hack), we will send
// some fake keystrokes to the application in an attempt to force
// the Tree View to scroll to the selected item.
//
t.Stop();
SendKeys.Send("{TAB}{TAB}{DOWN}{DOWN}{UP}{UP}");
}
};
t.Interval = 10;
t.Start();
result = dlg.ShowDialog( parent );
}
return result;
}
}