12

My (C#, .NET 3.5) application generates files and, in addition to raising events that can be caught and reacted to, I want to display the target folder to the user in a form. The file-list is being shown within the same form as other information.

I'm using an instance of the WebBrowser control (System.Windows.Forms.WebBrowser), then navigating to the folder. This shows some default view of the explorer window, with the file summary panel on the left and the files in the 'Tiles' (large icon and text) view.

For example,

wb.Navigate(@"c:\path\to\folder\");

I'd like to suppress the panel and to view the file list in the Details view. The user can get to this via a right-click, context menu, but I'd like it to come up automatically.

I'd rather not have to build my own TreeView, DataGridView or whatever; the WebBrowser control does all the updating and re-sorting and whatnot 'for free'.

Is there a better way? A different control to use or some additional arguments to pass to the control?

And if I could trap events (for example, files being selected/renamed/double-clicked, etc.) then all the better!

4

8 に答える 8

9

警告: 多くのコードを含む長い投稿。

Web ブラウザー コントロールをファイル システム フォルダーに移動すると、Web ブラウザー コントロールはシェル ビュー ウィンドウをホストし、次にエクスプローラー リスト ビューをホストします。実際、これは、ファイル ダイアログや Internet Explorer と同様に、Explorer プロセスが行うこととまったく同じです。このシェル ウィンドウはコントロールではないため、呼び出すことができるメソッドやサブスクライブできるイベントはありませんが、Windows メッセージを受信でき、サブクラス化できます。

ビューを自動的に詳細に設定する質問の部分は、実際には非常に簡単であることがわかりました。Web ブラウザー コントロールの Navigated イベントで、シェル ビュー ウィンドウへのハンドルを見つけ、特定のシェル定数 (SHVIEW_REPORT) を含む WM_COMMAND メッセージを送信します。これは文書化されていないコマンドですが、Windows 2008 までのすべての Windows プラットフォームでサポートされており、Windows 7 でもほぼ確実にサポートされます。Web ブラウザーのフォームに追加するコードは、次のとおりです。

    private delegate int EnumChildProc(IntPtr hwnd, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
        IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern int EnumChildWindows(IntPtr hWndParent,
        EnumChildProc lpEnumFunc, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName,
        int nMaxCount);

    private const int WM_COMMAND = 0x0111;
    private const int SHVIEW_REPORT = 0x702C;
    private const string SHELLVIEW_CLASS = "SHELLDLL_DefView";

    private IntPtr m_ShellView;

    void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
    {
        m_ShellView = IntPtr.Zero;
        EnumChildWindows(webBrowser1.Handle, EnumChildren, IntPtr.Zero);
        if (m_ShellView != IntPtr.Zero)
        {
            SendMessage(m_ShellView, WM_COMMAND, (IntPtr)SHVIEW_REPORT, (IntPtr)0);
        }
    }

    private int EnumChildren(IntPtr hwnd, IntPtr lParam)
    {
        int retval = 1;

        StringBuilder sb = new StringBuilder(SHELLVIEW_CLASS.Length + 1);
        int numChars = GetClassName(hwnd, sb, sb.Capacity);
        if (numChars == SHELLVIEW_CLASS.Length)
        {
            if (sb.ToString(0, numChars) == SHELLVIEW_CLASS)
            {
                m_ShellView = hwnd;
                retval = 0;
            }
        }

        return retval;
    }

Web ブラウザーが新しいウィンドウに移動するたびに (フォルダーがエクスプローラー ビュー内から開かれた場合を含む)、新しいシェル ビュー ウィンドウが作成されるため、Navigated イベントごとにメッセージを新しいウィンドウに再送信する必要があります。

質問の 2 番目の部分では、エクスプローラーのリスト ビューからイベントを受け取りたいと考えています。これは、最初の部分よりもかなり難しいです。これを行うには、リスト ビュー ウィンドウをサブクラス化し、関心のあるウィンドウ メッセージ (WM_LBUTTONDBLCLK など) を監視する必要があります。ウィンドウをサブクラス化するには、NativeWindow クラスから派生した独自のクラスを作成し、監視する必要があるウィンドウのハンドルを割り当てる必要があります。その後、そのウィンドウ プロシージャをオーバーライドして、必要に応じてさまざまなメッセージを処理できます。以下は、ダブル クリック イベントを作成する例です。これは比較的単純ですが、エクスプローラ リスト ビューに広範囲にアクセスするには、思った以上に多くの作業が必要になる場合があります。

これをフォームに追加します。

    private ExplorerListView m_Explorer;

    void OnExplorerItemExecuted(object sender, ExecuteEventArgs e)
    {
        string msg = string.Format("Item to be executed: {0}{0}{1}", 
            Environment.NewLine, e.SelectedItem);
        e.Cancel = (MessageBox.Show(msg, "", MessageBoxButtons.OKCancel) 
            == DialogResult.Cancel);
    }

そして、次の 2 行を Navigated イベント ハンドラー (SendMessage の直後) に追加します。

    m_Explorer = new ExplorerListView(m_ShellView);
    m_Explorer.ItemExecuted += OnExplorerItemExecuted;

次に、次のクラスを追加します。

class ExplorerListView : NativeWindow
{

    public event EventHandler<ExecuteEventArgs> ItemExecuted;

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
        IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern IntPtr FindWindowEx(IntPtr hwndParent,
        IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

    private const int WM_LBUTTONDBLCLK = 0x0203;

    private const int LVM_GETNEXTITEM = 0x100C;
    private const int LVM_GETITEMTEXT = 0x1073;

    private const int LVNI_SELECTED = 0x0002;

    private const string EXPLORER_LISTVIEW_CLASS = "SysListView32";

    public ExplorerListView(IntPtr shellViewHandle)
    {
        base.AssignHandle(FindWindowEx(shellViewHandle, IntPtr.Zero, 
            EXPLORER_LISTVIEW_CLASS, null));
        if (base.Handle == IntPtr.Zero)
        {
            throw new ArgumentException("Window supplied does not encapsulate an explorer window.");
        }
    }


    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case WM_LBUTTONDBLCLK:
                if (OnItemExecution() != 0) return;
                break;
            default:
                break;
        }
        base.WndProc(ref m);
    }

    private int OnItemExecution()
    {
        int cancel = 0;
        ExecuteEventArgs args = new ExecuteEventArgs(GetSelectedItem());
        EventHandler<ExecuteEventArgs> temp = ItemExecuted;
        if (temp != null)
        {
            temp(this, args);
            if (args.Cancel) cancel = 1;
        }
        return cancel;
    }

    private string GetSelectedItem()
    {
        string item = null;

        IntPtr pStringBuffer = Marshal.AllocHGlobal(2048);
        IntPtr pItemBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LVITEM)));

        int selectedItemIndex = SendMessage(base.Handle, LVM_GETNEXTITEM, (IntPtr)(-1), (IntPtr)LVNI_SELECTED).ToInt32();
        if (selectedItemIndex > -1)
        {
            LVITEM lvi = new LVITEM();
            lvi.cchTextMax = 1024;
            lvi.pszText = pStringBuffer;
            Marshal.StructureToPtr(lvi, pItemBuffer, false);
            int numChars = SendMessage(base.Handle, LVM_GETITEMTEXT, (IntPtr)selectedItemIndex, pItemBuffer).ToInt32();
            if (numChars > 0)
            {
                item = Marshal.PtrToStringUni(lvi.pszText, numChars);
            }
        }

        Marshal.FreeHGlobal(pStringBuffer);
        Marshal.FreeHGlobal(pItemBuffer);

        return item;
    }

    struct LVITEM
    {
        public int mask;
        public int iItem;
        public int iSubItem;
        public int state;
        public int stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        public IntPtr lParam;
        public int iIndent;
        public int iGroupId;
        int cColumns; // tile view columns
        public IntPtr puColumns;
        public IntPtr piColFmt;
        public int iGroup;

    }
}

public class ExecuteEventArgs : EventArgs
{
    public string SelectedItem { get; private set; }
    public bool Cancel { get; set; }

    internal ExecuteEventArgs(string selectedItem)
    {
        SelectedItem = selectedItem;
    }
}

これにより、何をする必要があるかがわかります。かなり単純なイベント以上のものが必要な場合は、別のコントロールを探すことをお勧めします。ただし、無料および低コストの領域で見たところ、かなりまともなコントロールがいくつかありますが、それらにはいくつかの癖があり、シームレスなエクスプローラーは提供されません。経験。

このコードは、エラー処理やコメントを使用せず、複数のアイテムを選択するなどのいくつかの問題を無視することなく、かなり迅速にまとめられたことを覚えておいてください。そのため、ガイドラインとして、自己責任で使用してください。

于 2009-02-22T03:04:53.043 に答える
5

名前の変更、削除、およびその他のカスタマイズを処理するには、独自のファイルエクスプローラーを作成する必要があります。WebBrowserコントロールはニーズに適していません。これは、ActiveXコンポーネントの単なるラッパーです。このcodeprojectの記事
を確認する必要があります。これには、ファイルエクスプローラーの実装が含まれています。ファイルブラウザのサンプルは他にもいくつかあります。1つ2つ

于 2009-02-21T10:05:46.503 に答える
3

私はあなたを助けることができるかもしれないライブラリを書きました。あなたはそれを見つけることができます:http://gong-shell.sourceforge.net/

探しているコントロールはShellViewです。数行で簡単なWindowsエクスプローラーのクローンを作成する方法についてのチュートリアルもあります。

.NET 4.0ユーザーへの注意:現在、4.0ではGong-shellが壊れています。フレームワークはInteropに変更を導入し、正常にビルドされますが、shell32とのインターフェース時にさまざまな問題を引き起こします(特にshellicon apiは、アンマネージヌルポインターの逆参照につながります)。

于 2009-04-28T01:41:05.653 に答える
3

LogicNP ソフトウェアには、探していることを行う 2 つのコントロール (FileView と ShComboBox) があります: http://www.ssware.com/fldrview.htm

彼らのページから試用版をダウンロードできますが、ライセンスは約 130 ドルです。

于 2009-02-22T06:44:12.797 に答える
1

ExplorerBrowser オブジェクトを参照してください。

詳細については、 http://blogs.msdn.com/ieinternals/archive/2009/12/30/Windows-7-Web-Browser-Control-will-not-browse-file-system.aspxを参照してください。

于 2009-12-30T23:45:18.837 に答える
1

こちらの記事をご覧ください。.NET および WinForms でこれを行う方法が示されています。このようにすると、ユーザーが見るものを完全に制御できます。

私は自分のアプリケーションの 1 つでそれを使用しましたが、非常にうまく機能します。アイコン/詳細/リスト ビューを表示すると、ユーザーが他のディレクトリに移動するのを防ぐことができます (これは、多くの場合、標準のファイル/ディレクトリ ダイアログを表示する際の問題です。

以下のような画面を表示するために使用します http://img7.imageshack.us/img7/7647/screenshotbaf.png :

于 2009-04-22T11:24:30.913 に答える
1

Windows Vista のみでCOMコントロールをラップすることに満足している場合は、 IExplorerBrowserが必要になる可能性があります。

The Code Project の記事では、 MFCプログラム内での使用法を示していますが、少なくとも 1 人の他の人が、何らかの努力の末に C# で動作するようになったようです。

新しい API は、単にメッセージをインターセプトするよりもはるかに多くのプログラミング可能性を公開していますが、古いプラットフォームでは (明らかに) 役に立ちません。

于 2009-02-24T00:18:56.217 に答える
0

別のウィンドウを開いてターゲットフォルダーのコンテンツを表示する場合は、System.Windows.Forms.OpenFileDialogまたはSaveFileDialogを使用するか、FileDialogから継承して拡張できます。

ユーザーがフォルダーを選択できるようにするには、FolderBrowserDialogを使用できますが、ユーザーとしてはそのコントロールは好きではありません。

これは役に立ちますか、それともフォームにコントロールを埋め込む必要がありますか?

アサフ

于 2009-02-21T10:05:23.010 に答える