2008年9月30日火曜日

[.NET]普通のボタンにショートカットキーを割り当てる(その2)

先日ダミーのメニューを使ったボタンのショートカット機能ですが、別の方法として、ErrorProviderっぽい実装ができないか考えてみました。実装してみるとそれらしく動作しました。

コンポーネントなので、デザイナーのFormにドロップすればよいのですが、TargetForm(ComponetからFormを取得す方法がわからない)とKeyPreviewプロパティをそれらしく設定する必要があります。
FormのKeyPreviewの代わりに、このコンポーネントのKeyPreviewをTrueにすると、初期化時にTargetFormのTargetFormをそれらしく設定します。

/// <summary>
/// ショートカットキー機能を付加するExtenderProvide
/// </summary>
[ProvideProperty("ShortcutKeys"typeof(Control))]
public partial class ShortcutKeyProvider : ComponentIExtenderProviderISupportInitialize
{
    /// <summary>
    /// 情報格納エリア
    /// </summary>
    private Dictionary<KeysControlextInfo = new Dictionary<KeysControl>();
    
    private Form targetForm = null;
    /// <summary>
    /// 対象のForm
    /// </summary>
    [Description("ショートカットを有効にする対象のform")]
    public Form TargetForm
    {
        get { return targetForm; }
        set { targetForm = value; }
    }

    private bool keyPreview = true;
    /// <summary>
    /// キーイベントの設定
    /// </summary>
    /// <remarks>FormのKeyPreviewがFalseの場合、この値をFormのKeyPreviewに設定します</remarks>
    [Description("キーボードイベントがフォームとともに登録されるかを決定します")]
    public bool KeyPreview
    {
        get { return keyPreview; }
        set { keyPreview = value; }
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public ShortcutKeyProvider()
    {
        InitializeComponent();
    }

    /// <summary>
    /// コンテナ指定付コンストラクタ
    /// </summary>
    /// <param name="container">コンテナ</param>
    public ShortcutKeyProvider(IContainer container)
    {
        container.Add(this);

        InitializeComponent();
    }

    #region ISupportInitialize メンバ

    /// <summary>
    /// 初期化の開始
    /// </summary>
    public void BeginInit()
    {
    }

    /// <summary>
    /// 初期化の終了
    /// </summary>
    public void EndInit()
    {
        //キー押下イベントを設定
        if (TargetForm != null)
        {
            TargetForm.KeyDown += new KeyEventHandler(TargetForm_KeyDown);
            if (TargetForm.KeyPreview == false)
            {
                System.Diagnostics.Debug.WriteLine("****FORMのKeyPreviewを変更します!! ==> " + KeyPreview);
                TargetForm.KeyPreview = KeyPreview;
            }
        }
    }

    #endregion



    #region IExtenderProvider メンバ

    bool IExtenderProvider.CanExtend(object extendee)
    {
        //ボタンのみ対応する.対応するコントロールを増やすならここに追加
        return extendee is Button;
    }

    #endregion

    /// <summary>
    /// ショートカットキーの取得
    /// </summary>
    /// <param name="ctrl">該当のコントロール</param>
    /// <returns>キー</returns>
    [Description("このコントロールに割り当てたいショートカットキーを設定・取得します")]
    [DefaultValue(typeof(Keys), "None")]
    public Keys GetShortcutKeys(Control control)
    {
        return GetKeysByControl(control);
    }

    /// <summary>
    /// ショートカットキーの設定
    /// </summary>
    /// <param name="ctrl">該当のコントロール</param>
    /// <param name="value">割り当てられたキー</param>
    [Description("このコントロールに割り当てたいショートカットキーを設定・取得します")]
    public void SetShortcutKeys(Control controlKeys shortcutKey)
    {
        //キーがほかに割り当て済みの場合、例外とする
        Control checkCtrl;
        if (extInfo.TryGetValue(shortcutKeyout checkCtrl))
        {
            if (checkCtrl != control)
            {
                throw new ArgumentException("ショートカットキー重複 " + checkCtrl.ToString());
            }
        }
        
        //登録済みの場合削除
        Keys key = GetKeysByControl(control);
        if (key != Keys.None)
        {
            //MAPより削除する
            extInfo.Remove(key);
        }

        //指定されたキーがNone以外の場合、MAPに格納する
        if (shortcutKey != Keys.None)
        {
            extInfo.Add(shortcutKeycontrol);
        }

    }

    /// <summary>
    /// キーとコントロールの変換
    /// </summary>
    /// <param name="key">コントロール</param>
    /// <returns>対応付けられたショートカットキー</returns>
    private Keys GetKeysByControl(Control control)
    {
        Keys result = Keys.None;
        foreach (Keys key in extInfo.Keys)
        {
            //コントロールからショートカットキーを割り出す
            if (extInfo[key] == control)
            {
                result = key;
                break;
            }
        }

        return result;
    }

    /// <summary>
    /// キー押下イベント
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void TargetForm_KeyDown(object senderKeyEventArgs e)
    {
        Control control = null;
        if (extInfo.TryGetValue(e.KeyDataout control))
        {
            if (control != null && control.Enabled == true)
            {
                ((Button)control).PerformClick();
            }
        }
        
    }

}


partial class ShortcutKeyProvider
{
    /// <summary>
    /// 必要なデザイナ変数です。
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// 使用中のリソースをすべてクリーンアップします。
    /// </summary>
    /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region コンポーネント デザイナで生成されたコード

    /// <summary>
    /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディタで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
        components = new System.ComponentModel.Container();
    }

    #endregion
}

2008年9月29日月曜日

[.NET]普通のボタンにショートカットキーを割り当てる

「とあるFormのメニューにある項目をボタンに変更したい。ただしショートカットは使用したい。」

という仕様変更を言い渡されました。まぁ単純に考えると、FormのKeyPreview=Trueに変更して、キーイベントをアレして実装すればよいのですが、ほかに方法がないかと調査してみました。


調べてみたものの、ショートカットのみっていう機能みつからず、あきらめてメニューから項目削除しようとしたところ、Visibleプロパティがあるではないですが。そこで、
  1. 該当のメニューの制御(Enableプロパティの制御)そのままのこす
  2. 該当のメニューのVisible=Falseにする
  3. ボタンのクリックイベントを該当のメニューのクリックイベントに同じメソッドを指定する
  4. ボタンのEnable制御は1.のタイミングで実施する

ということを実装してみました。というかほとんど実装していません。そしてToolTipにそれらしいショートカットーを表示すると、ショートカットの機能する(しているようにみえる)ボタンが完成しちゃいました。
いいやり方とはいえませんが、こんな使い方もできちゃうんですねぇ。

2008年9月27日土曜日

[.NET]クラッシックASP/ASP.NETで情報共有

レガシーASPとASP.NETを共存させたい。または移行などで共存せざるを得ない場合があります。
基本的に両者は別のサービスであるため、情報の共有も簡単ではありません。しかしStateServerというSQLServerを使ったデータ連携を使うと意外と簡単に実現することができます。

従来の ASP と ASP.NET でセッション状態を共有する方法

何かあった場合に備えてメモ。

2008年9月24日水曜日

[他]VSで#region(折りたたまれた部分)も含んで検索する

VS.NET2003/VS2005・2008では、エディタ上の#region~#endregionを折りたたんで表示することができます。
自動生成や修正が少なく目に入れたくない部分を折りたたんでおくことにより、必要な部分のみに集中してコードを見ることができます。しかし、ソースを検索するときはヒットしてほしいのですが折りたたんでいると該当部分がヒットせずイラっとくる時があります。
そのため、折りたたまれたソースをあえて展開してから修正をする。という面倒なことをしていました。

私の場合、検索はインクリメンタル検索またはF3系の機能を使うため、ほとんど検索ダイアログを出しません。ところがある日、たまたま検索ダイアログを出して検索しようとすると、

  • 非表示のテキストを検索する(VS.NET2003)
  • 検索オプション→非表示のテキストを検索(VS2005,2008)

というオプションがあるではないですか!

・・・これまでの苦労は。。。なんだったんだろう。

2008年9月19日金曜日

[.NET]プロパティ変数の直接参照(DeepCopy的な処理)

強引なやり方としてはリフレクションがありますが、通常privateフィールドは、該当のクラスからしかアクセスできません。ということから、同一クラスでも別インスタンスからはアクセスできないと信じきっていました。しかし、コピーコンストラクタを書いていると、そんなことはないってことがわかりました。
たとえば、とあるフィールドを変更するとフラグが立つといった処理系ではこういったやり方もあるというのに今日気づきました。いやー頭が固くなってますねぇ。コード例はこんなかんじ

public class DataClass
{
    private bool udate = false;
    /// <summary>
    /// 更新フラグ
    /// </summary>
    public bool IsUpdate
    {
        get { return udate; }
    }

    private int intData = 0;
    /// <summary>
    /// 数値のプロパティ
    /// </summary>
    public int IntData
    {
        get { return intData; }
        set { intData = valueudate = true; }
    }

    private String strData = String.Empty;
    /// <summary>
    /// 文字列のプロパティ
    /// </summary>
    public String StrData
    {
        get { return strData; }
        set { strData = valueudate = true; }
    }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public DataClass()
    {
    }

    /// <summary>
    /// コピーコンストラクタ
    /// </summary>
    /// <param name="src">コピー元</param>
    public DataClass(DataClass src)
    {
        intData = src.intData;
        strData = src.strData;
        udate = src.udate;
    }
}

2008年9月13日土曜日

[.NET] システム情報の取得(SystemInformation)

.NETアプリケーションを組む際に、システム的な情報を取得したい場合があります。そういった情報を集めたクラスがSystemInformationです。たとえば以下のような情報が取得できます。
  • HorizontalScrollBarHeight 水平スクロールバーの既定の高さ
  • VerticalScrollBarWidth 垂直スクロール バーの既定の幅
  • MonitorCount モニタの数
  • MouseWheelPresent マウスホイールの有無
  • PowerStatus 電源ステータス
  • TerminalServerSession リモートデスクトップ接続かどうか(未確認)

ほかにもたくさんシステム情報が取得できます。
P/Invokeを使わなくてもここまで取得できるとは。。。

2008年9月11日木曜日

[他]いまさらVB5

とある見積もりをするために、VB5のソースを解析する必要がありました。
とりあえず、エディタ(秀丸)でソース見てたのですが、画面の雰囲気などよくわからず、「んーどうした物だろう」と考えていました。会社にVB5のライセンスはあると思いますが、ライセンス借りたりするのが異常にメンドクサイ。そこで、昔フリーで使えたVB5のエディションがあった事を思い出しました。

しかし検索してもダウンロードできるサイトにたどり着かない。で、直リンクでダウンロードできるURLを発見したので備忘録としてメモ。

Visual Basic 5.0 Control Creation Edition

しかし、いつリンク切れするかわかりませんので、その際はご容赦を。

2008年9月9日火曜日

[.NET]SqlParameter にIsNull?

先日、@itの掲示板を見ていると、Where句にSqlParameterを使用しているものの条件としてNullを指定したくて困っているとのスレがありました。私のわかる範囲では、パラメータを使用せず

 Where xxx is null;

という方式しか思いつきませんでしたが、その解決案として

 Where (xxx = @param OR (xxx IS NULL AND @param IS NULL));

というのが紹介されていました。
Orになっちゃうのでパフォーマンスが気になるところですが、すばらしい発想ですね。

2008年9月3日水曜日

[.NET]ファイル属性の取得

先日、とあるコードを見ていると、なぜかファイルを追加モードでオープンすることで、ファイルの存在チェックおよび、出力可能かを検査しているコードが書かれていました。
そしてファイルを実際に出力しているのは別のメソッドで実施。っていうかチェックすることでファイルのタイムスタンプ変わっちゃうし。。。

見ただけでイラっときてしまいましたので、Fileクラスのメソッドで置き換えました。コードはこんな感じ

if (!File.Exists(dlg.FileName))
{
    //ファイルがないときの処理
}else if ((File.GetAttributes(dlg.FileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
    //ファイルが読み取り専用のとき
}

2008年9月1日月曜日

[.NET]コントロールのフォーカス枠描画

ちょっと前に携わったプロジェクトでWindowsFormの仕事をしました。標準のコントロールではニーズに合わなかったため、なくなく独自のコントロールをいくつか作成しました。
描画は独自なのですが、フォーカス枠はボタンなどの通常のコントロールと同じ表示でよいということで、それらしく描画していたのですが、調べてみると、ControlPaintというクラスで簡単に書けることがわかりました。コードはこんな感じ。


Rectangle rect = new Rectangle(2, 2, Width - 4, Height - 4);
ControlPaint.DrawFocusRectangle(e.Graphics, rect);


ちなみにこの処理は、OnPaintの中で実施しています。