frog.raindrop.jp.knowledge > .NET プログラミング

August 23, 2010

インストールされている .NET Framework のバージョンをチェックするスクリプト - .NET Framework 4.0 対応版

インストールされている .NET Framework のバージョンをチェックするスクリプト を .NET Framework 4.0 に対応してみました。
4.0 は Full と Client Profile の 2 種類があるんですねー。

<?xml version="1.0" encoding="Shift_JIS" standalone="yes" ?>
<package>
<job id="Registry">
<?job error="True" debug="True" ?>
<script language="JScript">
<![CDATA[
var checker = function ()
    {
        var _reader = function ()
            {
                var _shell = new ActiveXObject ("WScript.Shell");
                return { read: function (name) { try { return _shell.RegRead (name); } catch (e) {}; return null; } };
            } ();

        var _checkKey = function (key)
            {
                var install = _reader.read (key + "\\Install");
                if (install == null || "" + install == "0")
                    return null;
                var servicePack = _reader.read (key + "\\SP");
                if (servicePack == null) servicePack = 0;
                return servicePack;
            };

        var _check10 = function ()
            {
                var install = _reader.read ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\Full\\v1.0.3705\\1041\\Microsoft .NET Framework Full v1.0.3705 (1041)\\Install");
                if (install == null || "" + install == "0")
                    return null;

                var version = _reader.read ("HKLM\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{78705f0d-e8db-4b2d-8193-982bdda15ecd}\\Version");
                if (version == null)
                    version = _reader.read ("HKLM\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}\\Version");
                if (version == null)
                    return 0;

                var m = version.match (/^1,0,3705,(\d+)$/);
                var servicePack = 0;
                if (m != null && m.length > 1)
                    servicePack = m [1] - 0;

                return servicePack;
            };

        var _formatResult = function (check, label)
            {
                switch (check)
                {
                case null: return label + ": not installed.\n";
                case 0:    return label + ": installed.\n";
                default:   return label + ": SP" + check + " installed.\n";
                }
            };

        return {
            check10: _check10,
            check11: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322"); },
            check20: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727"); },
            check30: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0"); },
            check35: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5"); },
            check40Client: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Client"); },
            check40Full:   function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full"); },
            check11JP: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322\\1041"); },
            check20JP: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727\\1041"); },
            check30JP: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\1041"); },
            check35JP: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5\\1041"); },
            check40ClientJP: function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Client\\1041"); },
            check40FullJP:   function () { return _checkKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\1041"); },
            show: function ()
                {
                    WScript.echo (_formatResult (this.check10 (), ".NET Framework 1.0")
                        + _formatResult (this.check11 (), ".NET Framework 1.1")
                        + _formatResult (this.check11JP (), "    Japanese Langage Pack")
                        + _formatResult (this.check20 (), ".NET Framework 2.0")
                        + _formatResult (this.check20JP (), "    Japanese Langage Pack")
                        + _formatResult (this.check30 (), ".NET Framework 3.0")
                        + _formatResult (this.check30JP (), "    Japanese Langage Pack")
                        + _formatResult (this.check35 (), ".NET Framework 3.5")
                        + _formatResult (this.check35JP (), "    Japanese Langage Pack")
                        + _formatResult (this.check40Client (), ".NET Framework 4.0 Client Profile")
                        + _formatResult (this.check40ClientJP (), "    Japanese Langage Pack")
                        + _formatResult (this.check40Full (), ".NET Framework 4.0 Full")
                        + _formatResult (this.check40FullJP (), "    Japanese Langage Pack")
                        );
                }
        };
    } ();
checker.show ();
]]>
</script>
</job>
</package>

October 28, 2009

TreeView の SelectedItemChanged に Command をバインドしたい。

遅ればせながら MVVM パターンの理解を深めるために WPF でエクスプローラーもどきを作っています。TreeView にディレクトリの木構造が表示され、任意のディレクトリを選択すると、その中のディレクトリとファイルの一覧が ListView に表示される、単純なやつです。

WPF の TreeView って初めて使ったんですけど、SelectedItem プロパティが読み取り専用になってて、Binding を設定したり出来ないんですよね。ViewModel 側で選択されたものを知りたいんだけど、なかなか一筋縄では行かない感じ。TreeView や TreeViewItem のイベントで設定すれば望みのことは出来るだろうけど、できれば View から ViewModel にアクセスせずに実現したい。

なんかいい方法ないかなーと思って調べたら、ちょうど TextBox で Enter キーを叩いたときに Command を実行するようなことをしてるのを見つけました。
添付ビヘイビアでTextBoxにCommandを実装してみた - SharpLab.
添付プロパティ内でイベントハンドラを設定したりするの、添付ビヘイビアって言うんですね。知らなかったです。手法自体は使ったことありましたが。

続きを読む...

July 10, 2009

インストールされている .NET Framework のバージョンをチェックするスクリプト

.NET Frameworkのバージョンを確認する方法を参考に WSH スクリプトを書いた。

拡張子を .wsf で保存して実行してくださいねー。

<?xml version="1.0" encoding="Shift_JIS" standalone="yes" ?>
<package>
<job id="Registry">
<?job error="True" debug="True" ?>
<script language="JScript">
<![CDATA[
    var checker = new DotNetChecker ();
    var result = "";
    result += checker.FormatResult (checker.Check10 (), ".NET Framework 1.0");
    result += checker.FormatResult (checker.Check11 (), ".NET Framework 1.1");
    result += checker.FormatResult (checker.Check11JP (), "    Japanese Langage Pack");
    result += checker.FormatResult (checker.Check20 (), ".NET Framework 2.0");
    result += checker.FormatResult (checker.Check20JP (), "    Japanese Langage Pack");
    result += checker.FormatResult (checker.Check30 (), ".NET Framework 3.0");
    result += checker.FormatResult (checker.Check30JP (), "    Japanese Langage Pack");
    result += checker.FormatResult (checker.Check35 (), ".NET Framework 3.5");
    result += checker.FormatResult (checker.Check35JP (), "    Japanese Langage Pack");

    WScript.Echo (result);

    function DotNetChecker ()
    {
        this.reader = new RegReader ();

        this.Check10 = function ()
        {
            var install = this.reader.RegRead ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\Full\\v1.0.3705\\1041\\Microsoft .NET Framework Full v1.0.3705 (1041)\\Install");
            if (install == null || "" + install == "0")
                return null;

            var version = this.reader.RegRead ("HKLM\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{78705f0d-e8db-4b2d-8193-982bdda15ecd}\\Version");
            if (version == null)
                version = this.reader.RegRead ("HKLM\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{FDC11A6F-17D1-48f9-9EA3-9051954BAA24}\\Version");
            if (version == null)
                return 0;

            var regex = /^1,0,3705,(\d+)$/;
            var m = regex.exec (version);
            var sp = 0;
            if (m != null && m.length > 1)
                sp = m [1] - 0;

            return sp;
        }

        this.Check11 = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322");
        }

        this.Check20 = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727");
        }

        this.Check30 = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0");
        }

        this.Check35 = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5");
        }

        this.Check11JP = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v1.1.4322\\1041");
        }

        this.Check20JP = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v2.0.50727\\1041");
        }

        this.Check30JP = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.0\\1041");
        }

        this.Check35JP = function ()
        {
            return this.CheckKey ("HKLM\\SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v3.5\\1041");
        }

        this.CheckKey = function (key)
        {
            var install = this.reader.RegRead (key + "\\Install");

            if (install == null || "" + install == "0")
                return null;

            var sp = this.reader.RegRead (key + "\\SP");
            if (sp == null)
                sp = 0;
            return sp;
        }

        this.FormatResult = function (value, label)
        {
            var result = label;

            switch (value)
            {
            case null:
                result += ": not installed.\n"; break;
            case 0:
                result += ": installed.\n"; break;
            default:
                result += ": SP" + value + " installed.\n"; break;
            }
            return result;
        }

    }

    function RegReader ()
    {
        this.shell = WScript.CreateObject ('WScript.Shell');

        this.RegRead = function (name)
        {
            var ret = null;
            try
            {
                ret = this.shell.RegRead (name);
            }
            catch (e)
            {
                //WScript.Echo (e.number + "\n" + e.description);
            }
            return ret;
        }

        return this;
}
]]>
</script>
</job>
</package>

May 15, 2009

Visual Studio 2008 で、XAML デザイナを開くと異常終了する

Visual Studio 2008 で XAML ファイルを開くと VS 自体が異常終了するようになってしまった。以下イベントログ。

ソース: .NET Runtime
種類: エラー
イベント ID: 1023
説明:
.NET Runtime version 2.0.50727.3082 - 致命的な実行エンジン エラーが発生しました (7A2E1132) (0)

VS2008 crashes with "Fatal Execution Engine Error"」に、以下の投稿があった。

Thanks to Paul I found a very strange workaround (I verified it a few times)

  • open the solution
  • unload the project (context menu)
  • edit the csproj (context menu), don't make any changes
  • reload the project (contect menu)

If I do that before I start the debugger VS will not crash.

つまり

  1. ソリューションを開く
  2. コンテキストメニューより [プロジェクトのアンロード]
  3. コンテキストメニューより [編集 WpfApplication1.csproj] して、何も編集を行わない。
  4. コンテキストメニューより [プロジェクトの再読み込み]

嘘みたいだが、これで本当に解決。

March 12, 2009

DoEvents

DoEvents っていうと、VB6 時代に使ったなーとか妙に懐かしいです。C とか C++ だとこんな実装をしてました。

void DoEvents (void)
{
    MSG msg;

    while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }
}

要するに、メインのメッセージループの内側で、同期的にメッセージループを実行してメッセージキュー内にたまってるメッセージを捌かせるもので、主に同期処理の合間に GUI が固まらないようにする目的で使うやつです。Borland 系では Application->ProcessMessages () がおんなじ様な実装になってたんだと思います。多分。

WPF では DispatcherFrame クラスがメッセージループを表わすらしく、Dispatcher.PushFrame でそれを実行できます。たまってたメッセージを処理し終えた際にループを終了する必要がありますが、ディスパッチスレッドに優先度の低い処理としてメッセージループの終了を登録しておくことで実現します。

実際の実装方法は MSDN のあちこちにありますが、関数一個にした版をとりあえず上げときます。

/// <summary>
/// 現在メッセージ待ち行列の中にある全てのUIメッセージを処理します。
/// </summary>

public static void DoEvents ()
{
    // うちっかわの DispatcherFrame を作成
    var nestedFrame = new DispatcherFrame ();

    // DispatcherFrame (= 実行ループ) を終了させるコールバック
    DispatcherOperationCallback callback = (frame) => { ((DispatcherFrame) frame).Continue = false; return null;

    // 非同期で実行する
    // 優先度を Background にしているので、このコールバックは
    // ほかに処理するメッセージがなくなったら実行される
    var operation = Dispatcher.CurrentDispatcher.BeginInvoke (DispatcherPriority.Background, callback, nestedFra

    // うちっかわの実行ループを開始する
    Dispatcher.PushFrame (nestedFrame);

    // コールバックのオペレーションが完了していない場合、強制終了する
    if (operation.Status != DispatcherOperationStatus.Completed)
    {
        operation.Abort ();
    }
}

March 6, 2009

翻訳: XMLSerializer クラスを使用した .NET Framework ベースのアプリケーションを、アジア版の Windows XP 上で実行すると応答を停止します。

A .NET Framework-based application that uses the XMLSerializer class stops responding when the application runs on an Asian version of Windows XP

XmlSerializer って、実行時に Csc.exe 起動するってこと ? また見ておこう…。

訳してから気付いたけど、日本語あったw
http://support.microsoft.com/kb/903204/ja

XMLSerializer クラスを使用した .NET Framework ベースのアプリケーションを、アジア版の Windows XP 上で実行すると応答を停止します。

Important This article contains information about how to modify the registry. Make sure to back up the registry before you modify it. Make sure that you know how to restore the registry if a problem occurs. For more information about how to back up, restore, and modify the registry, click the following article number to view the article in the Microsoft Knowledge Base:

重要 この記事はレジストリの修正方法についての情報を含みます。レジストリの修正前には必ずバックアップを行ってください。問題が発生した場合にレジストリを回復する方法を確認しておいてください。レジストリをバックアップ、リストア、修正する方法について、詳しくは Microsoft サポート技術情報の記事を参照するために以下の記事番号をクリックしてください。

256986 Description of the Microsoft Windows registry

SYMPTOMS
現象

When a Microsoft .NET Framework-based application tries to create an instance of the XMLSerializer class, the application stops responding. This symptom occurs if you try to run the application on a computer that is running an Asian version of Microsoft Windows XP.

Microsoft .NET Framework ベースのアプリケーションで XMLSerializer クラスのインスタンスを作ろうとすると、アプリケーションが応答を停止します。この現象は、アプリケーションをアジア版の Microsoft Windows XP 上で実行した際に発生します。

Note This article pertains to a specific scenario in which this symptom occurs. There are other scenarios in which this symptom may also occur. For example, this symptom may occur when one process starts another process in non-Asian versions of Windows XP.

この記事はこの現象が発生する特定のシナリオに関するものです。この現象が発生するかもしれない別のシナリオもあります。例えば、アジア以外のバージョンの Windows XP 上で一つのプロセスが別のプロセスを開始する場合にもこの現象が起こるかもしれません。

CAUSE
原因

The XMLSerializer class performs dynamical compilation. During dynamical compilation, the Csc.exe process is started. In turn, the Csc.exe process starts the Conime.exe process on Asian versions of Windows XP.

XMLSerializer クラスは動的コンパイルを実行します。動的コンパイル中、Csc.exe プロセスが開始されます。そして、アジア版の Windows XP 上では、Csc.exe プロセスは Conime.exe プロセスを起動します。

The issue that is described in the "Symptoms" section occurs because of a deadlock condition in the Csc.exe process. Specifically, one thread (thread A) acquires a lock that is required by another thread (thread B). Therefore, thread B tries to start the Conime.exe process. By default, the Conime.exe process is loaded whenever a command prompt starts on Asian versions of Windows XP. Thread B waits 10 minutes while it tries to acquire the lock. After 10 minutes, this thread times out.

"現象" のセクションに記述した問題は、Csc.exe プロセスのデッドロック状態により発生します。具体的には、ひとつのスレッド (スレッド A) が別のスレッド (スレッド B) が必要とするロックを取得します。したがって、スレッド B は Conime.exe プロセスを開始しようとします。デフォルトでは、アジア版の Windows XP では Conime.exe プロセスはコマンドプロンプトが開始されるたびにロードされます。スレッド B はそのロックの取得を試行して 10 分間待機し、10分後、そのスレッドはタイムアウトします。

Note For more information, visit the following MSDN Web site:

詳しい情報は、以下の MSDN のウェブサイトへ:

http://msdn2.microsoft.com/en-us/library/ms971336.aspx

RESOLUTION
解決方法

Warning Serious problems might occur if you modify the registry incorrectly by using Registry Editor or by using another method. These problems might require that you reinstall your operating system. Microsoft cannot guarantee that these problems can be solved. Modify the registry at your own risk.
To resolve this issue, make sure that the Conime.exe process starts at the system startup. To do this, follow these steps:

警告 レジストリエディタやその他の方法でレジストリを誤って変更すると、深刻な問題が発生します。それによってオペレーティングシステムの再インストールが必要となる可能性があります。Microsoft はそれらの問題の解決を保障できません。あなた自身の責任でレジストリの変更を行ってください。
この件を解決するには、システムの開始時に Conime.exe プロセスを必ず開始します。そのためには、以下の手順を行ってください。

  1. Click Start, click Run, type regedit in the Open box, and then click OK.
    スタートメニューをクリックして、ファイル名を指定して実行をクリック、名前ボックスに regedit と入力して OK をクリックしてください。
  2. Locate and then click the following key in the registry:
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    レジストリ内の以下のキーを見つけてクリックしてください:
    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
  3. In the right pane, right-click the blank space, and then click New. Click String Value, type conime as the name, and then press ENTER.
    右ペイン内の空白の部分を右クリックし、新規をクリックしてください。文字列値をクリックして、名前を conime として ENTER キーを押してください。
  4. Right-click conime, and then click Modify.
    conime を右クリックして修正をクリックしてください。
  5. In the Edit String dialog box, type %windir%\system32\conime.exe under Value data, and then click OK.
    文字列の編集ダイアログボックスで、値のデータ%windir%\system32\conime.exe と入力して OK をクリックしてください。
  6. Quit Registry Editor.
    レジストリエディタを終了してください。

MORE INFORMATION
追加情報

Input Method Editors (IMEs) are DLL files that let users type complex ideographic characters by using a standard keyboard. IMEs are available in Asian versions of Windows. IMEs simplify the process by which users enter text that contains characters from Unicode and double-byte character set (DBCS) formats. IMEs monitor the user's keystrokes, anticipate the character the user may want, and present a list of character options from which to select.

Input Method Editor (IME) はユーザーに標準キーボードを使用して複雑な表意文字を入力させる DLL ファイルです。IME はアジア版 Windows で利用されます。IME はユーザーがユニコードおよび 2 バイト文字セット (DBCS) フォーマットの文字を含むテキストを入力するプロセスを単純化します。IME はユーザーのキー入力を監視して、ユーザーが希望すると思われる文字を予測し、文字を選択するためのリストを提供します。

The problem that is described in the "Symptoms" section is known to occur in the following third-party programs:

"現象" セクションに記された問題は以下のサードパーティ製プログラムで発生することが知られています。

  • Autodesk Inventor Series 10
  • Autodesk Inventor Professional 10

The third-party products that this article discusses are manufactured by companies that are independent of Microsoft. Microsoft makes no warranty, implied or otherwise, regarding the performance or reliability of these products.

この記事の論じているサードパーティ製の製品は、Microsoft と無関係な企業によって製造されます。これらの製品の性能・信頼性に関して、Microsoft は一切の保障を負いません。

APPLIES TO
該当

  • Microsoft Windows XP Home Edition
  • Microsoft Windows XP Professional
  • Microsoft Windows XP Media Center Edition
  • Microsoft Windows XP Tablet PC Edition

ItemsControl の要素を BindingGroup を使用して一括検証 & 更新する

validate.png

設定ダイアログなんかで、OK ボタンで一括チェックを行って反映、みたいなのを作る場合、個々の Binding に UpdateSourceTrigger="Explicit" を設定しておき、.NET Framework 3.5 から追加された BindingGroup で ValidationRule をまとめて走らせる実装をしてる。でも、ItemsControl の ItemTemplate で Binding を定義した場合、それらの Binding はそのままでは BindingGroup には入らないみたい。

BindingGroup が使えないなんて、項目ごとの BindingExpression を取得して更新するしかないの ? んなわけないよね ? と思って試行錯誤してみた結果、BindingGroup に Name プロパティを設定しておき、同時に更新したい Binding の BindingGroupName プロパティにその値を設定しておくと追加されることが分かった。
※ もちろん、同一のデータソースのものしか追加されないよ

一応、それなりの動作はするんだけど、なぜか最後の項目から順番に ValidationRule が走ってしまう。そんなもんだっけ ?

その他、気づいたこととか、メモとか。

  • BindingGroup と 個々の Binding の両方にうっかり Validation.Error ハンドラを設定してしまうと、両方走ってしまってえらい格好悪い (当たり前か)
  • ひょっとすると、一個の ValidationRule で引っかかった場合に以降の Validation を実行しない方法があるんじゃないか (調査中)
  • 個々の Binding に設定した ValidationRule の ValidationStep プロパティをうっかり UpdatedValue にしてしまうと、ValidationRule::Validate に渡される value は BindingExpression になる。(しかも更新後にわたってくるので意味がない)
  • 値の一意性をチェックするときなんかは、BindingGroup に ValidationStep を UpdatedValue に設定した ValidationRule を仕込んでおくとよい。
    (その場合は BindingGroup のトランザクション機能を利用する必要がある。IEditableObject を実装するんが面倒だけど ><)

というわけで、サンプル。

続きを読む...

March 5, 2009

テキストボックスのフォーカス取得時にテキストを選択する

selectongotfocus.png

こうですか !? わかりません ! w

<Window x:Class="WpfSample.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="テキストボックスのフォーカス取得時にテキストを選択する"
        SizeToContent="Height" Width="300"
        PreviewGotKeyboardFocus="Window_PreviewGotKeyboardFocus">
    <Window.Resources>
        <Style TargetType="Label">
            <Setter Property="Margin" Value="5"/>
            <Setter Property="ContentStringFormat" Value="{}{0}:"/>
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="Margin" Value="5"/>
        </Style>
    </Window.Resources>
    <Grid Margin="5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="名前"/>
        <TextBox Grid.Column="1" Grid.Row="0"/>

        <Label Grid.Column="0" Grid.Row="1" Content="住所"/>
        <TextBox Grid.Column="1" Grid.Row="1"/>

        <Label Grid.Column="0" Grid.Row="2" Content="電話番号"/>
        <TextBox Grid.Column="1" Grid.Row="2"/>

        <Label Grid.Column="0" Grid.Row="3" Content="E-Mail"/>
        <TextBox Grid.Column="1" Grid.Row="3"/>
    </Grid>
</Window>
namespace WpfSample
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1 ()
        {
            InitializeComponent ();
        }

        private void Window_PreviewGotKeyboardFocus (object sender, KeyboardFocusChangedEventArgs e)
        {
            var textBox = e.NewFocus as TextBox;
            if (textBox != null)
                textBox.SelectAll ();
        }
    }
}

March 4, 2009

オブジェクトをシーケンスとして返す拡張メソッド

単一のオブジェクトからシーケンスを作成したい場合ありますよね。引数にシーケンスを取る関数に渡すとか、単一オブジェクトもコレクションも同じ処理をしたいとか…。

なので、こんな拡張メソッドを作っとくと便利かも。Enumerable のメンバとしてほしいくらい !

namespace ExtensionMethods
{
    static class EnumerableExtension
    {
        public static IEnumerable<TSource> ToSequence<TSource> (this TSource source)
        {
            yield return source;
        }
    }

    class Person
    {
        static void Main (string [] args)
        {
            var person = new Person ();
            IEnumerable<Person> people = person.ToSequence ();
        }
    }
}

標準にないのかな…。
-------------
IEnumerable<Person> people = Enumerable.Repeat (person, 1);
ならありますね。

IPv4 でマルチキャストに送出するパケットの TTL を設定する。

IPv4 でマルチキャストのパケットを送出する際、デフォルトのままでは TTL が 1 になってしまう。ルータ越えの必要がある場合は以下のようにして、送信に使用するソケットのオプションを変更する。(下記は TTL を 32 に変更した例)

// #include <Winsock2.h>
int ttl = 32;
int result = setsockopt (socket, IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof ttl);
if (SOCKET_ERROR == result)
{
    // エラー処理は省略...
}

C# の場合はこんなの。

// using System.Net.Sockets;
int ttl = 32;
socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, ttl);

February 25, 2009

ジェネリックの型推論がイマイチいけてない気がする。

class A { }
class B : A { }

class Sample
{
    static void Method1<T> (T obj) where T : A { Method2 (obj); }

    static void Method2 (B obj) { Console.WriteLine ("obj is B"); }

    static void Method2 (A obj) { Console.WriteLine ("obj is A"); }

    static void Main (string [] args)
    {
        var obj = new B ();
        Method1 (obj);
    }
}

上記の実行結果はどうなるでしょうか。

こたえ。

obj is A
続行するには何かキーを押してください . . .


うーん、イマイチ…。

February 16, 2009

空の IEnumerable<T> を返す

戻り値が IEnumerable<T> 型の関数で、null でなく、空のシーケンスを返す方法がわかりませんでした。
Enumerable.Empty<TResult> メソッドってのがあるんですね !

return Enumerable.Empty<Person> ();

February 12, 2009

明示的に定義されたインターフェース プロパティへのバインディング

バインディング ソースの概要より

バインディングのソース プロパティとして使用するプロパティは、クラスのパブリック プロパティである必要があります。明示的に定義されたインターフェイス プロパティには、基本実装を持たない保護プロパティ、プライベート プロパティ、および仮想プロパティの場合と同様に、バインディングのためにアクセスすることはできません。

あ、ダメなんですね…笑。

Enumerable.Aggregate メソッドのオーバーロードを整理してみたい。

Enumerable.Aggregate メソッド には、オーバーロードが 3 つあって、使いどころが若干違うように思います。

public static TSource Aggregate<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, TSource, TSource> func
)

一番シンプルなやつ。

string[] beans = { "ささげ", "いんげん", "そらまめ", "えんどう", "だいず", };
Console.WriteLine (
    beans.Aggregate ((work, next) => work + "/" + next)
    );

ポイントは、func が実行される回数が、source.Count () - 1 回だということ。上の例では一発目は work が "ささげ"、next が "いんげん" となります。区切り文字を入れて文字列を連結したりするのにぴったり。

public static TAccumulate Aggregate<TSource, TAccumulate>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func
)

初期値のあるやつ。

string[] beans = { "ささげ", "いんげん", "そらまめ", "えんどう", "だいず", };
Console.WriteLine (
    beans.Aggregate (0, (work, next) => work += next.Length)
    );

func は source.Count () 回実行されます。例の一発目は work が 0、next が "ささげ" です。全要素に対して同様の評価を行う必要がある場合に向いてると思います。

public static TResult Aggregate<TSource, TAccumulate, TResult>(
    this IEnumerable<TSource> source,
    TAccumulate seed,
    Func<TAccumulate, TSource, TAccumulate> func,
    Func<TAccumulate, TResult> resultSelector
)

初期値があり、さらに結果に対してごにょごにょすることができるやつ。

string[] beans = { "ささげ", "いんげん", "そらまめ", "えんどう", "だいず", };
Console.WriteLine (
    beans.Aggregate (
        new StringBuilder (), (work, next) => work.AppendFormat ("/{0}類", next), builder => builder.ToString ())
    );

初期値のあるやつと同じで、func は source.Count () 回実行されます。例の一発目は work が StringBuilder ("")、next が "ささげ" です。例はちょっとミスってて、区切りのスラッシュが一個余分な感じです。

TODO: 依存関係プロパティ

DependencyObject.GetValue メソッド (System.Windows) DependencyProperty は DependencyObject 以外にじっそうすることは可能か ? 添付プロパティは可能だが、あれは値の保存先が DependencyObject だから。 Dependency.Object.SetValue/Dependency.Object.GetValue は内部的には何をしてるの ? たとえば、それっぽいIなんとか(インターフェース)がないかさがす。

February 5, 2009

System.Type より、型のインスタンスを生成する

System.Type から型のインスタンスを作成する方法があるはずだと思ってましたが、実際に必要になったので探してみました。System.Activator クラスCreateInstance メソッドで、型からインスタンスを作成することができます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CSharpSample
{
    class Sample
    {
        static void Main (string [] args)
        {
            var sample = new Sample ();
            var obj = Activator.CreateInstance (sample.GetType ());
            Console.Write ("{0}\n", obj);
        }
    }
}

実行結果です。

CSharpSample.Sample
続行するには何かキーを押してください . . .

CreateInstance メソッドには、なにやらいっぱいオーバーロードがあります。また読んどくことにします。

January 28, 2009

連番のプロパティを持つ Items を Grid 上に整列させるコンバータ

public class NumberGridConverter: DependencyObject, IValueConverter
{
    /// <summary>
    /// <c>BaseNumber</c> 依存関係プロパティ
    /// </summary>
    /// <value><c>NumberGridConverter.BaseNumber</c> 属性値</value>
    public int BaseNumber
    {
        get { return (int) GetValue (BaseNumberProperty); }
        set { SetValue (BaseNumberProperty, value); }
    }

    /// <summary><c>BaseNumber</c> 依存関係プロパティを識別します。</summary>
    public static readonly DependencyProperty BaseNumberProperty =
        DependencyProperty.Register ("BaseNumber", typeof (int), typeof (NumberGridConverter), new FrameworkPropertyMetadata ((int) 0, FrameworkPropertyMetadataOptions.AffectsRender));

    /// <summary>
    /// <c>Columns</c> 依存関係プロパティ
    /// </summary>
    /// <value><c>NumberGridConverter.Columns</c> 属性値</value>
    public int Columns
    {
        get { return (int) GetValue (ColumnsProperty); }
        set { SetValue (ColumnsProperty, value); }
    }

    /// <summary><c>Columns</c> 依存関係プロパティを識別します。</summary>
    public static readonly DependencyProperty ColumnsProperty =
        DependencyProperty.Register ("Columns", typeof (int), typeof (NumberGridConverter), new FrameworkPropertyMetadata ((int) 1, FrameworkPropertyMetadataOptions.AffectsRender));

    /// <summary>
    /// <c>Rows</c> 依存関係プロパティ
    /// </summary>
    /// <value><c>NumberGridConverter.Rows</c> 属性値</value>
    public int Rows
    {
        get { return (int) GetValue (RowsProperty); }
        set { SetValue (RowsProperty, value); }
    }

    /// <summary><c>Rows</c> 依存関係プロパティを識別します。</summary>
    public static readonly DependencyProperty RowsProperty =
        DependencyProperty.Register ("Rows", typeof (int), typeof (NumberGridConverter), new FrameworkPropertyMetadata ((int) 1, FrameworkPropertyMetadataOptions.AffectsRender));

    /// <summary>
    /// <c>Orientation</c> 依存関係プロパティ
    /// </summary>
    /// <value><c>NumberGridConverter.Orientation</c> 属性値</value>
    public System.Windows.Controls.Orientation Orientation
    {
        get { return (System.Windows.Controls.Orientation) GetValue (OrientationProperty); }
        set { SetValue (OrientationProperty, value); }
    }

    /// <summary><c>Orientation</c> 依存関係プロパティを識別します。</summary>
    public static readonly DependencyProperty OrientationProperty =
        DependencyProperty.Register ("Orientation", typeof (System.Windows.Controls.Orientation), typeof (NumberGridConverter), new FrameworkPropertyMetadata (System.Windows.Controls.Orientation.Horizontal, FrameworkPropertyMetadataOptions.AffectsRender));


    #region IValueConverter メンバ

    public object Convert (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var param = parameter as string;
        var number = (int) value - this.BaseNumber;
        if (param != null)
        {
            switch (param.ToUpper ())
            {
                case "COLUMN":
                    switch (this.Orientation)
                    {
                        case System.Windows.Controls.Orientation.Horizontal:
                            return (number % this.Columns);
                        case System.Windows.Controls.Orientation.Vertical:
                            return (number / this.Rows);
                        default:
                            break;
                    }
                    break;

                case "ROW":
                    switch (this.Orientation)
                    {
                        case System.Windows.Controls.Orientation.Horizontal:
                            return (number / this.Columns);
                        case System.Windows.Controls.Orientation.Vertical:
                            return (number % this.Rows);
                        default:
                            break;
                    }
                    break;

                default:
                    break;
            }
        }
        return DependencyProperty.UnsetValue;
    }

    public object ConvertBack (object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException ();
    }

    #endregion
}

January 27, 2009

Binding の StringFormat プロパティのカルチャって ?

String.Format () でカルチャに依存する書式を指定した場合、カルチャを指定しないとそのスレッドのカルチャ情報が使用されるらしいです。具体的には System.Threading.Thread.CurrentThread.CurrentCulture です。

が、BindingBase の StringFormat で指定した場合、カレントスレッドのカルチャ情報と異なるように見えますがー。なんか私間違ってますかー ? 何か指定がいるのー ?

Binding の結果
String.Format の結果

<Window
    x:Class="SampleApp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"
    xmlns:th="clr-namespace:System.Threading;assembly=mscorlib"
    Title="Binding の StringFormat のカルチャって?"
    SizeToContent="WidthAndHeight">
    <StackPanel Grid.IsSharedSizeScope="True">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="Caption"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Margin="5" Text="StringFormat なし"/>
            <TextBlock Grid.Column="1" Margin="5" Text="{Binding Source={x:Static sys:DateTime.Now}}"/>
        </Grid>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="Caption"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Margin="5" Text="StringFormat あり"/>
            <TextBlock Grid.Column="1" Margin="5" Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat={}{0:F}}"/>
        </Grid>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="Caption"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Margin="5" Text="CurrentUICulture の設定"/>
            <TextBlock Grid.Column="1" Margin="5" Text="{Binding Source={x:Static th:Thread.CurrentThread}, Path=CurrentUICulture.DateTimeFormat.FullDateTimePattern}"/>
        </Grid>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" SharedSizeGroup="Caption"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Grid.Column="0" Margin="5" Text="CurrentCulture の設定"/>
            <TextBlock Grid.Column="1" Margin="5" Text="{Binding Source={x:Static th:Thread.CurrentThread}, Path=CurrentCulture.DateTimeFormat.FullDateTimePattern}"/>
        </Grid>
        <Button Margin="5" Click="Button_Click">String.Format の結果</Button>
    </StackPanel>
</Window>
namespace SampleApp
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1 ()
        {
            InitializeComponent ();
        }
 
        private void Button_Click (object sender, RoutedEventArgs e)
        {
            MessageBox.Show (String.Format ("{0:F}", System.DateTime.Now));
        }
    }
}

January 23, 2009

続:最大化ボタン・最小化ボタン・システムメニューのないウインドウ

最大化ボタン・最小化ボタン・システムメニューのないウインドウの実装を考え中。

これもありか?

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace WindowStyleProject { public abstract class StyledWindow : Window { const int GWL_STYLE = -16;
/// <summary> /// ウインドウスタイルを上書きする /// 継承先でオーバーライドする /// </summary> /// <param name="windowStyle">現在のウインドウスタイル</param> /// <returns>新たに設定するウインドウスタイル</returns> protected virtual WindowStyleEnum OverrideWindowStyle (WindowStyleEnum windowStyle) { return windowStyle; // デフォルトは変更なし }
/// <summary> /// ウインドウスタイルを設定する /// </summary> /// <param name="e"></param> protected override void OnSourceInitialized (EventArgs e) { base.OnSourceInitialized (e);
// 現在のウインドウスタイルを取得 IntPtr handle = (new WindowInteropHelper (this)).Handle; var originalWindowStyle = (WindowStyleEnum) GetWindowLong (handle, GWL_STYLE);
// 継承先で更新された値を取得 var newWindowStyle = this.OverrideWindowStyle (originalWindowStyle);
// 上書き if (originalWindowStyle != newWindowStyle) SetWindowLong (handle, GWL_STYLE, (uint) newWindowStyle); }
[DllImport ("user32.dll")] static extern uint GetWindowLong (IntPtr hWnd, int nIndex);
[DllImport ("user32.dll")] static extern uint SetWindowLong (IntPtr hWnd, int nIndex, uint dwNewLong);
}
public enum WindowStyleEnum : uint { // // Window Styles // WS_OVERLAPPED = 0x00000000, WS_POPUP = 0x80000000, WS_CHILD = 0x40000000, WS_MINIMIZE = 0x20000000, WS_VISIBLE = 0x10000000, WS_DISABLED = 0x08000000, WS_CLIPSIBLINGS = 0x04000000, WS_CLIPCHILDREN = 0x02000000, WS_MAXIMIZE = 0x01000000, WS_CAPTION = 0x00C00000, // WS_BORDER | WS_DLGFRAME WS_BORDER = 0x00800000, WS_DLGFRAME = 0x00400000, WS_VSCROLL = 0x00200000, WS_HSCROLL = 0x00100000, WS_SYSMENU = 0x00080000, WS_THICKFRAME = 0x00040000, WS_GROUP = 0x00020000, WS_TABSTOP = 0x00010000,
WS_MINIMIZEBOX = 0x00020000, WS_MAXIMIZEBOX = 0x00010000,
WS_TILED = WS_OVERLAPPED, WS_ICONIC = WS_MINIMIZE, WS_SIZEBOX = WS_THICKFRAME, WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,
// // Common Window Styles // WS_OVERLAPPEDWINDOW = (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX),
WS_POPUPWINDOW = (WS_POPUP | WS_BORDER | WS_SYSMENU),
WS_CHILDWINDOW = (WS_CHILD), }
}

使い方は、StyledWindow を継承して、OverrideWindowStyle をオーバーライドする。

// Window1.xaml
namespace WindowStyleProject
{
    /// <summary>
    /// Window1.xaml の相互作用ロジック
    /// </summary>
    public partial class Window1 : StyledWindow
    {
        public Window1 ()
        {
            InitializeComponent ();
        }

protected override WindowStyleEnum OverrideWindowStyle (WindowStyleEnum windowStyle) { return windowStyle & (~WindowStyleEnum.WS_SYSMENU); } } }

あーでも、XAML のほうも書き換えなきゃな。これがうざい。

<local:StyledWindow x:Class="WindowStyleProject.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WindowStyleProject"
    Title="Window1" Height="300" Width="300">
    <Grid>
        <!-- 中略 -->
    </Grid>
</local:StyledWindow>

January 22, 2009

開くダイアログ

ファイルを開くダイアログの WPF 版は、Microsoft.Win32.OpenFileDialog クラス

private void Button_Click (object sender, RoutedEventArgs e)
{
    var openFileDialog = new Microsoft.Win32.OpenFileDialog ();
    openFileDialog.Filter = "C# ソースファイル|*.cs|すべてのファイル|*.*";
    openFileDialog.InitialDirectory = System.Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments);
    openFileDialog.CheckFileExists = true;
    openFileDialog.Multiselect = true;
    openFileDialog.ShowReadOnly = false;
    openFileDialog.Title = "ソースファイルの選択";
    var result= openFileDialog.ShowDialog ();
    if (result ?? false)
    {
        MessageBox.Show (openFileDialog.FileNames.Aggregate ((str, nextstr) => str + "\n" + nextstr));
    }
}

上の例は複数ファイルを選択できるダイアログを表示するが、選択された結果は FileNames プロパティより string[] 型として取得できる。ちゃんとフルパス。でも、このダイアログは Vista で実行しても XP スタイルになっちゃうらしい (未確認)

保存するほうは、同様に SaveFileDialog クラスというのがある。