none
PropertyGridで展開可能な構造体の作成. RRS feed

  • 質問

  • System.Windows.Forms.Paddingを真似してPropertyGridで展開可能な構造体を作成しました.

    この構造体を自作のコントロールのプロパティとして持たせています.

    デザイナで,プロパティの展開等,動作するのですが,リビルドすると

    "オブジェクトがターゲットの型と一致しません。"

    と表示され,値が取得されません.

    AreaConverter.ConvertTo()内で value の型が Area なのに value is Area が False になって,

    型変換ができてないようです.

    Reflector で PaddingConverter のコードを見て書いたのですが,何がまずいのでしょうか.

    よろしくお願いします.

     

     

      [Serializable, StructLayout(LayoutKind.Sequential), TypeConverter(typeof(AreaConverter))]
     public struct Area
     {
      private double _left;
      private double _bottom;
      private double _right;
      private double _top;

      public Area(double left, double bottom, double right, double top)
      {
       this._left = left;
       this._bottom = bottom;
       this._right = right;
       this._top = top;
      }

      [RefreshProperties(RefreshProperties.All)]
      public double Left
      {
       get
       {
        return this._left;
       }
       set
       {
        if ( this._left != value )
        {
         this._left = value;
        }
       }
      }

      [RefreshProperties(RefreshProperties.All)]
      public double Bottom
      {
       get
       {
        return this._bottom;
       }
       set
       {
        if ( this._bottom != value )
        {
         this._bottom = value;
        }
       }
      }

      [RefreshProperties(RefreshProperties.All)]
      public double Right
      {
       get
       {
        return this._right;
       }
       set
       {
        if ( this._right != value )
        {
         this._right = value;
        }
       }
      }

      [RefreshProperties(RefreshProperties.All)]
      public double Top
      {
       get
       {
        return this._top;
       }
       set
       {
        if ( this._top != value )
        {
         this._top = value;
        }
       }
      }

      public override string ToString()
      {
       return ("{Left=" + this.Left.ToString(CultureInfo.CurrentCulture) + ",Bottom=" + this.Bottom.ToString(CultureInfo.CurrentCulture) + ",Right=" + this.Right.ToString(CultureInfo.CurrentCulture) + ",Top=" + this.Top.ToString(CultureInfo.CurrentCulture) + "}");
      }
     }

     public class AreaConverter : TypeConverter
     {
      public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
      {
       return ((sourceType == typeof(string)) || base.CanConvertFrom(context, sourceType));
      }

      public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
      {
       string str = value as string;
       if ( str == null )
       {
        return base.ConvertFrom(context, culture, value);
       }
       str = str.Trim();
       if ( str.Length == 0 )
       {
        return null;
       }
       if ( culture == null )
       {
        culture = CultureInfo.CurrentCulture;
       }
       char ch = culture.TextInfo.ListSeparator[0];
       string[] strArray = str.Split(new char[] { ch });
       double[] numArray = new double[strArray.Length];
       TypeConverter converter = TypeDescriptor.GetConverter(typeof(double));
       for ( int i = 0; i < numArray.Length; i++ )
       {
        numArrayIdea = (double)converter.ConvertFromString(context, culture, strArrayIdea);
       }
       if ( numArray.Length != 4 )
       {
        throw new ArgumentException();
       }
       return new Area(numArray[0], numArray[1], numArray[2], numArray[3]);
      }

      public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
      {
       return ((destinationType == typeof(InstanceDescriptor)) || base.CanConvertTo(context, destinationType));
      }

      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
      {
       if ( destinationType == null )
       {
        throw new ArgumentNullException("destinationType");
       }
       if ( value is Area )
       {
        if ( destinationType == typeof(string) )
        {
         Area area = (Area)value;
         if ( culture == null )
         {
          culture = CultureInfo.CurrentCulture;
         }
         string separator = culture.TextInfo.ListSeparator + " ";
         TypeConverter converter = TypeDescriptor.GetConverter(typeof(double));
         string[] strArray = new string[4];
         int num = 0;
         strArray[num++] = converter.ConvertToString(context, culture, area.Left);
         strArray[num++] = converter.ConvertToString(context, culture, area.Bottom);
         strArray[num++] = converter.ConvertToString(context, culture, area.Right);
         strArray[num++] = converter.ConvertToString(context, culture, area.Top);
         return string.Join(separator, strArray);
        }
        if ( destinationType == typeof(InstanceDescriptor) )
        {
         Area area2 = (Area)value;
         ConstructorInfo constructor = typeof(Area).GetConstructor(
          new Type[] { typeof(double), typeof(double), typeof(double), typeof(double) }
          );
         if ( constructor != null )
         {
          return new InstanceDescriptor(
           constructor,
           new object[] { area2.Left, area2.Bottom, area2.Right, area2.Top });
         }
        }
       }
       return base.ConvertTo(context, culture, value, destinationType);
      }

      public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
      {
       if ( propertyValues == null )
       {
        throw new ArgumentNullException("propertyValues");
       }
       return new Area((double)propertyValues["Left"], (double)propertyValues["Bottom"], (double)propertyValues["Right"], (double)propertyValues["Top"]);
      }

      public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
      {
       return true;
      }

      public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
      {
       return TypeDescriptor.GetProperties(typeof(Area), attributes)
        .Sort(new string[] { "Left", "Bottom", "Right", "Top" });
      }

      public override bool GetPropertiesSupported(ITypeDescriptorContext context)
      {
       return true;
      }
     }
     

    2008年8月10日 4:17

回答

  • 回避策が見つかりましたので投稿いたします. 原因はConvertTo()メソッド内で,value is Area がtrueにならないというものです. value.GetType().ToString() と typeof(Area).ToString() を比較すると確かに同じ文字列になっているのですが,アセンブリが探せていないとか何とかの問題なのでしょうか. if ( value is Area ) の代わりに if ( value.GetType().ToString() == is typeof(Area).ToString() ) を使用する 値の取得は Area area2 = (Area)value; area2.Left... の代わりに value.GetType().GetProperty("Left").GetValue(value, null) を使用することで解決しました. 型が違っていますがプロパティ名は同じなので(当たり前ですが)これで問題なく動作するようになりました. デバッグビルドではこれで回避し,リリースビルドでは元のやり方に戻すことにします. 以上 ありがとうございました.
    • 回答としてマーク yamada3 2009年6月27日 22:13
    2009年6月27日 22:12

すべての返信

  • yamada3 さん こんにちは

     

    上記のTypeConverterを参考に自分の環境で確認してみました。

    1つのソリューション上で、WindowsクラスライブラリとWindowsアプリケーションの2つのプロジェクトを作成した場合で確認しました。

     

    AreaをWindowsアプリケーションプロジェクトのForm1のプロパティに設定して、Form1を継承したForm2で値を設定できるかを確認しています。

     

    (1)  AreaとAreaConverterをWindowsクラスライブラリプロジェクトに作成した場合は、最初のほうは動いていた記憶があるのですが、途中からうまく動いたり、動かなかったりしました。正直謎です。

     

    (2) そこで、以下のサイトで似たような現象になっている投稿があったので、Windowアプリケーションプロジェクトと同じプロジェクト、同じネームスペースで、AreaとAreaConverterを設定すると、今度は私の環境ではうまく動きました。ただしVisual Studio 2008ですが。

    http://thedotnet.com/nntp/136841/showpost.aspx

     

    もしかしたらVisual StudioがTypeConverterのアセンブリを探せていないため、うまく動いていないのかもしれません。

    上記のサイトにはResolveEventHandlerrを使用した解決策が載っているので、同じようにすればうまくいくかもしれません。

     

    的外れな回答なら申し訳ないです。では。

     

    #追記

    Visual Studio 2005 英語版で(2)の条件で試してみましたが、うまくいきました。TypeConverterのソースをいじったりすると、プロパティグリッドの表示がうまくいかなくなることがあったので、そのときはVisual Studioを再起動しましたが。

    2008年8月11日 14:46
  • 回答ありがとうございます.

    こちらの環境はVisual Studio 2005 Expressです.

    動作確認ありがとうございます.また,参考サイトの紹介ありがとうございます.英語は苦手ですのでぼちぼち読んでいきたいと思います.

     

    最初の投稿でも書きましたが,AreaConverter.ConvertTo()内で value の型が Area なのに value is Area が False になって,型変換ができてないようです.

    Visual Studioのデザイナから呼ばれたときはブレークポイント等,設定できないのでファイルにログを出力させてみたのですが,value の型が Area で,destinationType が InstanceDescriptorの時に ConvertTo内の

    if ( value is Area )
    のブロックが実行されていませんでした.destinationType が InstanceDescriptorの時はこのブロック内は実行されていました.何か関係あるのでしょうか.

     

    クラスライブラリとして作成する必要があるので,解決できなければ独自の型をプロパティに設定することはあきらめないといけません.また何か情報ありましたらよろしくお願いします.

    2008年8月13日 6:28
  • 回避策が見つかりましたので投稿いたします. 原因はConvertTo()メソッド内で,value is Area がtrueにならないというものです. value.GetType().ToString() と typeof(Area).ToString() を比較すると確かに同じ文字列になっているのですが,アセンブリが探せていないとか何とかの問題なのでしょうか. if ( value is Area ) の代わりに if ( value.GetType().ToString() == is typeof(Area).ToString() ) を使用する 値の取得は Area area2 = (Area)value; area2.Left... の代わりに value.GetType().GetProperty("Left").GetValue(value, null) を使用することで解決しました. 型が違っていますがプロパティ名は同じなので(当たり前ですが)これで問題なく動作するようになりました. デバッグビルドではこれで回避し,リリースビルドでは元のやり方に戻すことにします. 以上 ありがとうございました.
    • 回答としてマーク yamada3 2009年6月27日 22:13
    2009年6月27日 22:12