« ファイルからアイコンを抽出して、ImageListへ登録 | トップページ | 7-zip32.dllのUTF-8モードをC#から使用する(1) »

2010年12月 5日 (日)

「アイコンの変更」ダイアログを使ってみる。

アイコンネタ第4弾目です。今回は、タイトルの通り、下図のアイコンの変更ダイアログを出してみます。そして最終的にはコンポーネント化してみる事にします。

実は既にアプリケーションで、「アイコンの変更」ダイアログと同種のフォームを使ったタイプのダイアログは作っています。しかし、下図の様にアイコン表示に ListView を使用しているので、アイコンの一覧表示目的としては、やけにアイコン間のスペースが大きい。それに ListView の、HideSelection プロパティはどうやら、View.Details のときしか効かないので、選択中のアイコンが判りにくいのも不満です。

昔 Delphiで、「アイコンの変更」ダイアログを出してみた事はあったのですが、その時はダイアログ自体に使い道がなくて、それっきりにしていました。今回は、前述の通り、アプリケーションで使うので、C#でまず表示テストを行ない、コンポーネント化する事にします。

さて、以前このダイアログ表示テストをしたのは Windows2000 が最新のOSだった頃です。そしてそのときは、Shell32.dll の62番目の関数(名前無し関数)でした。名前なしという事もあって、確か非公開のAPIだったと記憶しています。ですが、XPから(?) PickIconDlg という関数名を持っている様です。しかもMSDNにも PickIconDlg の説明があります。公開関数になったという事でしょうか。もっとも、この説明中に WindowsVistaやWindows Server2003 は動くけど、それ以降はダメかも...の但し書きが気になりますが、私には動作するか確認しようがないので無視する事にします。

ではまずは、動作確認も含めて、以下のコードで、「とりあえず」ダイアログを出してみます。

[DllImport("shell32.dll", CharSet=CharSet.Auto)]
private static extern bool PickIconDlg(IntPtr hwndOwner, StringBuilder lpstrFile,
    int nMaxFile, ref int lpdwIconIndex);
private void btnSelectFile_Click(object sender, System.EventArgs e)
{
    StringBuilder path = new StringBuilder("shell32.dll", 260);
    int iconIndex = 0;
    bool result = PickIconDlg(this.Handle, path, path.Capacity, ref iconIndex);
    Console.WriteLine("{0} {1} {2}", result, path, iconIndex);
}

予想通り Windows2000 では、PickIconDlg の関数名が存在しない為、EntryPointNotFoundException の例外が発生して、動作しません。しかし、WindowsXP では、ちゃんとダイアログが出ました。やはり、関数名が付けられた様です。
さて、個人的には Windows2000でも動作させたいので、ちょっと手を加えて序数62の関数を参照する様に指定してみます。

[DllImport("shell32.dll", CharSet=CharSet.Auto, EntryPoint="#62")]
private static extern bool PickIconDlg(IntPtr hwndOwner, StringBuilder lpstrFile,
    int nMaxFile, ref int lpdwIconIndex);
private void btnSelectFile_Click(object sender, System.EventArgs e)
{
    StringBuilder path = new StringBuilder("shell32.dll", 260);
    int iconIndex = 0;
    bool result = PickIconDlg(this.Handle, path, path.Capacity, ref iconIndex);
    Console.WriteLine("{0} {1} {2}", result, path, iconIndex);
}

今度は、Windows2000 でもダイアログが表示されました。見ての通り、DllImportAttribute の EntryPoint は序数指定も可能です。C/C++や、Delphiだと序数指定になるだけで、必要がなくても動的リンクの方式、つまり LoadLiblary → GetProcAddress → FreeLibrary の手続きが必要になるのですが、C#では意外と簡単にアクセス出来ます。

「アイコンの変更」ダイアログに話を戻すと、PickIconDlg を呼び出した後、戻り値 result には、OKボタンでダイアログを閉じた場合は true が、キャンセルボタンで閉じた場合は false が返ります。また、OKボタンで閉じた場合には、path に新しく選択したファイルパスが、そして iconIndex には、その選択したインデックス番号が入っている事が確認出来ます。

さあ、これで基本的な動作確認が出来ました。
では、コンポーネント化して、実用的にしてみましょう。ここでは、コモンダイアログの1つでもあるので、CommonDlg クラスから派生させてみる事にします。
ここでは、過去の記事
自作コントロールを使い易くする(アイコン指定)
自作コントロールを使い易くする(説明表示系)
プロパティエディタの指定
等の事項も満たしつつ、以下のコードを書いてみました。

using System;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Security.Permissions;

namespace Acha_ya.PicIconDlg
{
    /// <summary>
    /// ユーザーがアイコンの変更をするために使用するコモン ダイアログ ボックスを表します。
    /// </summary>
    [System.Drawing.ToolboxBitmap(typeof(ChangeIconDialog), "Acha_ya.PicIconDlg.ChangeIconDialog.bmp")]
    public class ChangeIconDialog : CommonDialog
    {
        [DllImport("shell32.dll", CharSet=CharSet.Auto, EntryPoint="#62")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool PickIconDlg(IntPtr hwndOwner, StringBuilder lpstrFile,
            int nMaxFile, ref int lpdwIconIndex);
        private string selectedPath = null;
        private int iconIndex = 0;

        /// <summary>
        /// <see cref="T:Acha_ya.PicIconDlg.ChangeIconDialog" /> クラスの
        /// 新しいインスタンスを初期化します。
        /// </summary>
        public ChangeIconDialog()
        {
        }
        /// <summary>
        /// ダイアログでアイコン抽出対象となるファイルのパスを取得または設定します。
        /// </summary>
        [Category("表示")]
        [Description("ダイアログでアイコン抽出対象となるファイルのパスです。")]
        [Editor(typeof(System.Windows.Forms.Design.FileNameEditor),
             typeof(System.Drawing.Design.UITypeEditor))]
        public string SelectedPath
        {
            get { return selectedPath; }
            set { selectedPath = value; }
        }
        /// <summary>
        /// ダイアログで選択中のアイコンインデックスを取得または設定します。
        /// </summary>
        [Category("表示")]
        [Description("ダイアログで選択中のアイコンインデックスです。")]
        [DefaultValue(0)]
        public int IconIndex
        {
            get { return iconIndex; }
            set { iconIndex = value; }
        }
        /// <summary>
        /// すべてのプロパティを既定値にリセットします。
        /// </summary>
        public override void Reset()
        {
            SelectedPath = null;
            IconIndex = 0;
        }
        /// <summary>
        /// <see cref="M:System.Windows.Forms.CommonDialog.RunDialog" /> をオーバーライドして、
        /// 「アイコンの変更」ダイアログを表示できるようにします。
        /// </summary>
        /// <param name="hwndOwner">
        /// コモン ダイアログ ボックスのオーナー ウィンドウのウィンドウ ハンドルを表す値。
        /// </param>
        /// <returns>
        /// ダイアログ ボックスが正常に動作した場合は <see langword="true" />。
        /// それ以外の場合は <see langword="false" />。
        /// </returns>
        protected override bool RunDialog(IntPtr hwndOwner)
        {
            StringBuilder path = new StringBuilder(SelectedPath, 260);
            bool result = PickIconDlg(hwndOwner, path, path.Capacity, ref iconIndex);
            if (result)     // OKボタンがクリックされた
            {
                SelectedPath = path.ToString();     // SelectedPath に選択中パスを設定
            }
            return result;
        }
    }
}

プロパティを2つだけ追加して、必要なメソッドのオーバーライドをしただけの、シンプルなコンポーネントです。それ程需要はないでしょうが、必要な場面があったら使ってみて下さい。一応以下のテスト用サンプルには、上記コードも入った形でダウンロードできる用にしています。
「PicIcon.cab」をダウンロード


« ファイルからアイコンを抽出して、ImageListへ登録 | トップページ | 7-zip32.dllのUTF-8モードをC#から使用する(1) »

.NETコンポーネント」カテゴリの記事

C#研究」カテゴリの記事

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック


この記事へのトラックバック一覧です: 「アイコンの変更」ダイアログを使ってみる。:

« ファイルからアイコンを抽出して、ImageListへ登録 | トップページ | 7-zip32.dllのUTF-8モードをC#から使用する(1) »