ラベル C# の投稿を表示しています。 すべての投稿を表示
ラベル C# の投稿を表示しています。 すべての投稿を表示

2012/05/30

BackgroundWorker 内で DataContext を使うとき

DataContext を取得するのは UI スレッドからでしか無理のようだ。例外が発生する。
なので BackgroundWorker で DataContext を取得したい場合、こんな風に UI スレッドで動かしてやらないといけない。
Action action = () =>
{
    var hoge = (HogeViewModel)this.DataContext;
    // do something ...
}
tihs.Dispatcher.Invoke(action);

WPFのドラッグアンドドロップで処理を行う場合

エクスプローラとかからディレクトリを D & D して処理を行う場合、よほど軽いすぐ終わる処理でない限りは BackgroundWorker などで別スレッドにしたほうがいいみたい。
Window_Drop ハンドラ内で処理していたら、処理が終わるまでエクスプローラがロックされたような状態になってしまう。

2012/05/29

WPFでファイルやディレクトリのドラッグアンドドロップ処理

例えば Window に対して行うなら、Window の AllowDrop を true にして、DragOver と Drop イベントにハンドラを設定すればできる。
イベント名が違うだけで、これも WinForms 時代と変わらないね。
DataFormats って文字列の定数クラスなのね。
const じゃなくて readonly なのは理由があるのだろうか?

2012/05/28

WPFで未処理の例外を一括処理

アプリケーション開発の定番の例外一括処理。
WPF も Windows Forms 時代とたいして変わらないね。
DispatcherUnhandledException はコードビハインドではなく、App.xaml に記述することもできるけど、UIスレッドで発生した時の DispatcherUnhandledException と UIスレッド以外で発生した時の UnhandledException はセットで考えるべきだと思うので、見通しが良いように一括で設定するようにしてる。

2012/05/19

特定のインターフェースを実装しているかどうかを調べる

プロパティの型が IEnumerable かどうかを判別したかった。
// 基底クラス
public class Base {}

// サブクラス
public class SubClass : Base {}

// とあるクラス
public class Foo
{
    // 実体は List だけど IEnumerable として公開
    public IEnumerable Classes
    {
        get { return new List<SubClass>(); }
    }
}
これでリフレクションを用いて、Classes プロパティが IEnumerable と互換性があるかどうかを調べたい。
var propType = typeof(Foo).GetProperty("Classes").PropertyType;
bool impl = false;

// これは false
impl = propType.IsSubclassOf(typeof(IEnumerable));

// これで IEnumerable を実装していることは判別できた
var filter = new System.Reflection.TypeFilter((t, obj) => t == (Type)obj);
impl = propType.FindInterfaces(filter, typeof(IEnumerable)) > 0;

// 最終的にはこう
impl = impl && propType.IsGenericType && propType.GetGenericParameters()[0].IsSubclassOf(typeof(Base));
かなり長ったらしくてめんどくさい。
なんか簡単な方法があるはずだと思って調べてみたら、これだけでできた。
typeof(IEnumerable<Base>).IsAssignableFrom(propType)
代入可能かどうかで調べたらよかったのか。

2012/05/15

XCData を後から追加するには

<name><![CDATA[pinzolo]]></name>
こんな XML を出力したい場合、通常ならばこう書けばいい。
var nameElement= new XElement("name", new XCData(obj.Name));
でも、obj.Name が null の場合、ArgumentNullException が発生する。
そこで、文字列が存在する場合のみ CDATA セクションを追加するようにしたい。
最初はこう書いた。
var nameElement = new XElement("name");
if (obj.Name != null)
{
    nameElement.SetValue(new XCData(obj.Name));
}
しかし、XObject を値にすることはできないという ArgumentException が発生した。
とりあえず次はこうしてみた。
var nameElement = new XElement("name");
if (obj.Name != null)
{
    nameElement.SetValue(new XCData(obj.Name).ToString());
}
こうなった。まあ、当たり前だ。
<name>&lt;![CDATA[pinzolo]]&gt;</name>
他にも正解はあるだろうが、とりあえず正解の一つはこれのようだ。
var nameElement = new XElement("name");
if (obj.Name != null)
{
    nameElement.ReplaceNodes(new XCData(obj.Name));
}
なるほど、置き換えてやるのね。

2012/05/10

C#にて空のシーケンスを返すには

今の C# って List よりも IEnumerable 中心のコーディングだから、null じゃなく空のシーケンスで IEnumerable を初期化したい場合がある。
そんな場合は、Enumerable.Empty を使えばいいみたいだ。
var values = Enumerable.Empty<String>();

IEnumerable.ConvertAll を作ろうかと思ったら

ConvertAll って便利ですよね。
var numbers = new List<int>() { 1, 2, 3, 4, 5 };
numbers.ConvertAll<String>(n => n.ToString() + n.ToString()).ForEach(Console.WriteLine);
結果は↓
11
22
33
44
55
でも、これはListのメソッドなので IEnumerable だと、ToList をかましてからでないと利用できない。
仕方ないので拡張メソッド書くかと思って、みんなどうしてるのか調べてみたら Select で同じ事できたのね。
IEnumerable<int> numbers = new List<int>() { 1, 2, 3, 4, 5 };
// IEnumerable<T>.ForEach は実装済み
numbers.Select<int, String>(n => n.ToString() + n.ToString()).ForEach(Console.WriteLine);

2012/05/01

Visual C# 2010 Express に NUnit を使用する

ダウンロードとインストール

NUnit - Download から msi をダウンロード(現時点で最新は NUnit-2.6.0.12051.msi)。
インストールはおなじみ、ダブルクリックしてぽちぽちぽちっと

Visual Studio に登録

[ツール]→外部ツールメニューにて下記のデータでエントリを追加。
  • タイトル : NUnit
  • コマンド : C:\Program Files\NUnit 2.6\bin\nunit.exe
  • 引数 : /run $(ProjectDir)/$(ProjectFileName)
  • 初期ディレクトリ : $(ProjectDir)
これで、ツールメニューに外部ツールとして NUnit が登録される。

Debug ビルドを可能に

デフォルトだと強制的に Release ビルドになるので、Release と Debug を選択できるようにする。
  1. [ツール]→[設定]にて基本設定から上級者設定に変更。これでソリューション構成選択などのメニューが上部に現れる。
  2. [ツール]→[オプション]のダイアログにて、左下の「すべての設定を表示」にチェック
  3. [プロジェクトおよびソリューション]→[全般]メニューにて「ビルド構成の詳細を表示」にチェック
これで、選択した構成でのビルドが可能になる。

試してみる

サンプルプロジェクト(NUnitSample) を作成、Class1 を Sample クラスに変更して、SampleMethod メソッドを登録。
using System;

namespace NUnitSample
{
    public class Sample
    {
        public int SampleMethod(string text)
        {
            return 0;
        }
    }
}
テスト用プロジェクト(NUnitSampleTest)を追加し、参照設定にプロジェクトから NUnitSample プロジェクトを、.NET から nunit.framework を追加。 テストクラス SampleTest を作成し、テストメソッドを追加。
using System;

using NUnit.Framework;
using NUnitSample;

namespace NUnitSampleTest
{
    public class SampleTest
    {
        private Sample sample = new Sample();

        [Test(Description="引数 null 時のSampleMethodテスト")]
        [ExpectedException(typeof(ArgumentNullException))]
        public void SampleMethodWithNullArgument()
        {
            this.sample.SampleMethod(null);
        }

        [Test(Description="数値でない引数のテスト")]
        [ExpectedException(typeof(ArgumentException))]
        public void SampleMethodWithInvalidArgument()
        {
            this.sample.SampleMethod("hoge");
        }

        [Test(Description="正常な引数時のテスト")]
        public void SamplemethodWithValidArgument()
        {
            int expected = 1234;
            int actual = this.sample.SampleMethod("1234");
            Assert.AreEqual(expected, actual);
        }
    }
}
[外部ツール]→[NUnit]で実行する。
当然失敗する。
ソースコードを修正する。
using System;

namespace NUnitSample
{
    public class Sample
    {
        public int SampleMethod(string text)
        {
            if (text == null)
            {
                throw new ArgumentNullException("text");
            }
            int result = 0;
            if (Int32.TryParse(text, out result))
            {
                return result;
            }
            else
            {
                throw new ArgumentException("text");
            }
        }
    }
}
実行して成功するのを確認する。

2012/04/30

ひさびさにC#

あすから一ヶ月間 C# のお仕事なので、手元の Thinkpad X200 を Ubuntu から Windows7 に設定した。
HDD 入れ替えだけだけど。

とりあえず Visual Studio 2010 C# Express だけ入れておいた。
評価版のほうがよかったかな?