スポンサーリンク

2011年4月20日水曜日

CallMethodAction を継承した Behavior で描画エラー in VS2010

 Silverlight で UI を作成しているとき、Expression Blend の CallMethodAction を継承した Behavior を使用すると、VS2010 のデザイナーで例外が発生する。

 UnresolvedAssemblyException だったかな?

 グーグル先生に聞いてみたら同じような現象を記事にしている方を発見!

CallMethodActionを継承したBehaviorを指定するとVisual Studio 2010のXAMLデザイナで例外が発生する - The Road to C# Master Trapemiya

 で、そちらの記事から参照されているページを確認してみたところ、バグらしい。しかも修正プログラムが公開される予定がない、とのこと。

no title

 引用元の情報では回避策としてエラーが出ないようにコメントアウトせよ、ってことでやりたいことをするためにあーしなさいという内容にはなっていない。

 とはいえ、やはり CallMethodAction を継承した Behavior を作成する必要がある、さぁ、どうしよう。ということで、そのさらに基底クラスである TriggetAction<T> を継承して実装しました。


 まず、TriggetAction<T> を継承してクラスを作成。汎用的にするならジェネリックのところは FrameworkElement とかにするとよいかも。
public class MyCallMethodAction : TriggerAction<MyClass>
{
}
ここに CallMethodAction にあるプロパティを追加します。もちろん依存プロパティとして追加します。
public class MyCallMethodAction : TriggerAction<MyClass>
{
    public readonly static DependencyProperty TargetObjectProperty = DependencyProperty.Register("TargetObject", typeof(object), typeof(MyCallMethodAction), null);

    public object TargetObject
    {
        get
        {
            return (object)this.GetValue(TargetObjectProperty);
        }
        set
        {
            this.SetValue(TargetObjectProperty, value);
        }
    }

    public readonly static DependencyProperty MethodNameProperty = DependencyProperty.Register("MethodName", typeof(string), typeof(MyCallMethodAction), null);

    public string MethodName
    {
        get
        {
            return (string)this.GetValue(MethodNameProperty);
        }
        set
        {
            this.SetValue(MethodNameProperty, value);
        }
    }
}
あとは Invoke メソッドをオーバーライドして、TargetObject の MethodName メソッドを実行するように修正します。
public class MyCallMethodAction : TriggerAction<MyClass>
{
    public readonly static DependencyProperty TargetObjectProperty = DependencyProperty.Register("TargetObject", typeof(object), typeof(MyCallMethodAction), null);

    public object TargetObject
    {
        get
        {
            return (object)this.GetValue(TargetObjectProperty);
        }
        set
        {
            this.SetValue(TargetObjectProperty, value);
        }
    }

    public readonly static DependencyProperty MethodNameProperty = DependencyProperty.Register("MethodName", typeof(string), typeof(MyCallMethodAction), null);

    public string MethodName
    {
        get
        {
            return (string)this.GetValue(MethodNameProperty);
        }
        set
        {
            this.SetValue(MethodNameProperty, value);
        }
    }

    protected override void Invoke(object parameter)
    {
        MethodInfo method = this.TargetObject.GetType().GetMethod(this.MethodName, new Type[] { typeof(object), typeof(MyEventArgs) });

        if (method == null)
        {
            return;
        }

        if (!(parameter is object[]) && ((object[])parameter).Length != 2)
        {
            return;
        }

        object[] parameters = (object[])parameter;

        if (!(parameters[0] is object) || !(parameters[1] is MyEventArgs))
        {
            return;
        }

        method.Invoke(this.TargetObject, parameters);
    }
}
私が実装したときは特定のクラスの特定のイベントが発生したときに指定されたメソッドを実行するビヘイビアを作成したかったのでこういう Invoke メソッドの実装になってます。

 特定のイベントをトリガーに想定しているので、第一パラメータが object(sender) で第二パラメータが MyEventArgs(e) というよくあるイベントの引数であることを決め打ち、TargetObject オブジェクトの MethodName で指定されたメソッドを実行するようにしています。

 そうじゃなかった場合、ただ処理を終了させてますが、何らかの例外を発生させるようにするとそれらしくなると思います。

 MethodName に指定されるメソッドが引数を持たないメソッドかどうか、または、その引数と parameter の型が一致しているかどうかを確認するようにしたりと細かく制御すれば本家の CallMethodAction と同じ動作をさせられるようになるでしょう。

 今回は先ほども述べたように特定のイベントをトリガーとしているのでそちらも自作していますが、標準の EventTrigger でも動作します。まあ、このままだと Invoke メソッド内でコケますが。

0 件のコメント:

コメントを投稿