< ネットワーク情報をいろいろ取得するやつ。 | メモ:カスタム DateTime 書式指定文字列 >

October 29, 2008

'IValueConverter' 型にはパブリック TypeConverter クラスがありません。

Converting a collection to comma seperated list : Windows Presentation Foundation (WPF) : .NET Development : MSDN Forums の翻訳。せっかく訳したけど、私の現象とは関係なかったんだけどー。

一応断っておきますが、訳の内容はあんまり信用しないで。

Ok, I just ran into that same exception:
  'IValueConverter' type does not have a public TypeConverter class.

OK, 僕もちょうど同じ例外でハマリました:
  'IValueConverter' 型にはパブリック TypeConverter クラスがありません。

Here is the explanation of why it happened and how to fix it:

それがなぜ起るのか、そしてそれをどうやって Fix すればよいかをこれより説明します:

The Converter property on a binding is not looking for a property of type "Type", but it is looking for a ValueConverter instance. This actually enables some great scenarios...

バインディングの Converter プロパティは、"Type" 型ではなく、ValueConverter のインスタンスを期待します。実際、これがいくつかの偉大なシナリオを有効にします...。

Given that the primary tool for creating bindings that most people are still using is the XML editor with XAML intellisense, this is difficult to figure out today...

多くの人が未だに使用しているバインディングを作成する主要なツールが、XAML インテリセンス付きの XML エディタであることが、理解することを難しくしています...。

My Scenario

僕の場合

I am trying to represent if a DLL is in the Gac or not.

DLL が Gac (訳注: global assembly cache の GACのこと?) かどうかを表示しようとしました。

I first tried:

最初に試したやつ:

<Ellipse Fill="{Binding GacStatus,Converter=my:YesNoViewer}" Height="20" Width="50" Margin="5" />

But that gave me the exception: 'IValueConverter' type does not have a public TypeConverter clas

しかし、これは例外を返しました:'IValueConverter' 型にはパブリック TypeConverter クラスがありません。

When I realized that the Converter property was of type IValueConverter, I realized I needed to give it an instance. In order to give it an instance in markup, I knew that I needed to create a object-resource in my Windows.Resources collection.

僕は Converter プロパティの型が IValueConverter であることに気づき、そこにはインスタンスを与えるべきであることを気づきました。マークアップ内でインスタンスを与える方法として、Windows.Resources コレクション内でオブジェクトリソースを作成する必要があることは知っていました。

Then I had an "aha" moment. A few minutes earlier I had built two converters: YesNoViewer and YesViewer. I then hardcoded the Brush (color) values that I wanted Yes and No to return. But then I realized that sometimes I wanted Yes to be Green and sometimes I wanted it to be yellow...it depended on the meaning of the data.

そこでアハ体験(訳注: 茂木健一郎氏のアレですかw)、数分前、僕が作成した二つのコンバータ: YesNoViewer と YesViewer 。それには、Yes と No で返ることを期待するブラシ(色)値をハードコーディングしました。でも時によって、Yes が Green であってほしいことも、Yellow であってほしいこともあると気づきました...それはデータの意味に依存します。

The fact that Converter takes an instance made it so I could create one Converter, and change its parameters in the resource collection.

実は、Converter プロパティがインスタンスを(値に)取ることで、ひとつのコンバータを作成して、そのパラメータを変更したものをリソースコレクションに含めることが可能になるのです。

It gave me alot more flexibility and less hardcoding.

それは大きな柔軟性をもたらし、ハードコーディングを最小限にしてくれます。

I ended up with the following IValueConverters specified in Window.Resources:

最終的に、Window.Resources 内に以下のように IValueConverters を指定することで落ち着きました。

<my:StatusViewer x:Key="yesIsGreen" Yes="Green" />
<my:StatusViewer x:Key="yesIsYellow" Yes="Yellow"/>
<my:StatusViewer x:Key="yesIsGreenNoIsYellow" Yes="Green" No="Yellow"/>

And then my Bindings became:

そして、バインディングはこのように:

<Ellipse Fill="{Binding FileStatus,Converter={StaticResource yesIsGreen}}" Height="20" Width="20" Margin="5"/>
<Ellipse Fill="{Binding BackupStatus,Converter={StaticResource yesIsYellow}}" Height="20" Width="20" Margin="5" />
<Ellipse Fill="{Binding GacStatus,Converter={StaticResource yesIsGreenNoIsYellow}}" Height="20" Width="20" Margin="5" />

And my Converter class itself was:

コンバータクラス本体は:

public enum InstallStatus
{
    No,
    Yes,
    NotApplicable,
}

public class StatusViewer : IValueConverter
{
    private Brush _Yes;
    public Brush Yes
    {
        get { return _Yes; }
        set { _Yes = value; }
    }

    private Brush _No;
    public Brush No
    {
        get { return _No; }
        set { _No = value; }
    }

    private Brush _NotApplicable;
    public Brush NotApplicable
    {
        get { return _NotApplicable; }
        set { _NotApplicable = value; }
    }

    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (targetType == typeof(Brush))
        {
            InstallStatus status = (InstallStatus)value;
            switch (status)
            {
                case InstallStatus.Yes:
                    return Yes;
                case InstallStatus.No:
                    return No;
                default:
                    return NotApplicable;
            }
        }
        else
            throw new Exception("StatusViewer converts InstallStatus values to Brushes. You attempted to convert to another type.");
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion

}

Summary

まとめ

You need to pass in an instance...not very discoverable with the current tools...but very powerful!

君はインスタンスを渡す必要があります...あまり現在のツールでは発見しやすくない...でも非常にパワフルです!

The exception is raised by the XamlReader...a better error message would be:

例外は XamlReader から発生されています...よりよいエラーメッセージはこうでしょう:

Developer: The Converter property on Binding is expecting a value of type IValueConverter. You supplied a string.
Component Author: If you'd like to be able to accept a string value for the Converter property, you must provide an appropriate TypeConverterAttribute on the Converter property or the IValueConverter type.

開発者へ: Binding の Converter プロパティは IValueConverter 型の値を期待します。あなたは文字列を渡しました。
コンポーネントの作成者へ: Converter プロパティに文字列値を許可したい場合は、Converter プロパティか IValueConverter 型に、適切な TypeConverterAttribute を提供するべきです。

Hope that helps the next people who search for that exception string...and we'll continue to improve the debuggability experience...sorry.

この例外の文字列を探す次の人たちへの助けとならんことを...そして僕らはデバッグしやすさを改善し続けます。すみません。

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/2221

コメント

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?