C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: 継承コントロールのプロパティに存在しないパスが入る


(過去ログ 106 を表示中)

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■63372 / inTopicNo.1)  継承コントロールのプロパティに存在しないパスが入る
  
□投稿者/ Unripe01 (1回)-(2012/08/21(Tue) 12:46:26)

分類:[.NET 全般] 

2012/08/22(Wed) 09:07:14 編集(投稿者)
2012/08/21(Tue) 15:40:54 編集(投稿者)

お世話になっております。
Vitual Studio 2010にてTextBoxを継承したコントロールを作成しています。
しかし、デザイナで別のformに張り付けた途端、存在しないリソースファイルのパスと思われるコードが生成されたり、
DefaultValue属性に指定した値が反映されなかったりして困っております。

要因は2点と考えており、1つは、単純にDefaultValueの使い方が違っていること
1つは、プロジェクトファイルが不正?ではないか。
ということです、申し訳ないですが、以下実現可能か、ご鞭撻いただけませんでしょうか。

▼環境等▼
開発環境:Visual Studio Team Edition
利用言語:FrameWork 4.0 (C#)

▼やりたいこと▼
■継承コントロールにプロパティを指定し、その初期値をデザイナ上で反映させる。
■プロパティは、プリミティブ型として指定し、その実態は内部で保持しているオブジェクトクラスのプロパティを操作する

▼ソースコード▼
***************継承コントロールクラス***************
※魔界の仮面弁士 様からのご指摘があり、コード修正しています。
namespace TextBoxSample
{
public partial class TextBoxEX : TextBox
{
public TextBoxEX()
{
InitializeComponent();
}

private Label _Label = new Label();
/// <summary>
/// このコントロールのキャプションオブジェクトを指定または指定します
/// </summary>
public Label Label
{
get
{
return this._Label;
}
set
{
this._Label = value;
}
}

/// <summary>
/// テキストボックスのキャプションオブジェクトを指定または指定します
/// </summary>
[Category( "その他" )]
[DefaultValue( "でふぉるとばりゅ" )]
[Description( "テキストボックスのキャプションオブジェクトを指定または指定します" )]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
public string Caption
{
get
{
return this.Label.Text;
}
set
{
this.Label.Text = value;
}
}
}
}





***************デザイナ上で張り付けるとこうなる***************

//
// textBoxEX1
//
this.textBoxEX1.Caption = "";
this.textBoxEX1.Location = new System.Drawing.Point(242, 70);
this.textBoxEX1.Name = "textBoxEX1";
this.textBoxEX1.Size = new System.Drawing.Size(100, 19);
this.textBoxEX1.TabIndex = 2;


**************************************************************


▼悩んでいること1▼

this.textBoxEX1.Caption = "";
となり、Captionに"でふぉるとばりゅ"が反映されない。
DefaultValueAttribute Constructor ( String )を使う事が誤り?
DefaultValueAttribute Constructor ( Type, String )を使うべき?
⇒利用方法のサンプルを見つけることができず、どういった指定かで詰まる

▼悩んでいること2▼
上記継承コントロールは、サンプルプロジェクト1と、本番プロジェクト1に同じコードを
作成しています。サンプルプロジェクト1だと、

this.textBoxEX1.Caption = "";
となるのですが、本番プロジェクトだと
this.txtKensuEnd.Caption = global::sampleApp.app.resource.sample1.sampleValue1;
となり、global::sampleApp.app.resource.sample1.sampleValue1;
というリソースは未存在の状態で、必ずコンパイルエラーになります。

ソリューションが壊れているのか?
それとも、作成したプロパティの指定方法が悪いのか見当中。


恐れ入りますが、属性の指定方法や、こういうキーワードで検索したらいいとかあれば
教えていただけませんでしょうか。



引用返信 編集キー/
■63376 / inTopicNo.2)  Re[1]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ 魔界の仮面弁士 (26回)-(2012/08/21(Tue) 13:56:26)
No63372 (Unripe01 さん) に返信
> となり、Captionに"でふぉるとばりゅ"が反映されない。
Caption プロパティは、string ではなく Label なのですよね。
その Label の Text は、予め "でふぉるとばりゅ" にしてありますか?


> DefaultValueAttribute Constructor ( String )を使う事が誤り?
そもそも「Label 型」のメンバーに "でふぉるとばりゅ" という「文字列」は渡せないと思います。

文字列で指定したいなら、内包コントロールを直接返すプロパティにはせず、
内包コントロールの Text プロパティのみを返すプロパティとするべきかと。
  
 //public Label Caption {
 //  get { return this._Label; }
 //  set { … }
 //}

   public string Caption {
     get { return this._Label.Text; }
     set { … }
   }


また、現在のコードは Caption プロパティが ReadOnly ではなく、
getter と setter の両方を兼ね備えていますが、これだと利用する側が
 this.textBoxEX1.Caption = this.label1;
などのようにして、別のコントロールに変更できてしまいます。
本当に setter まで必要なのでしょうか?

もし、Caption プロパティ(Label型)の内容を外部から書き換えるために、
 this.textBoxEX1.Caption.Text = "別の文字列";
などの表現を使いたいのだけならば、getter だけで充分ですよね。


> DefaultValueAttribute Constructor ( Type, String )を使うべき?
DefaultValueAttribute というのは、以下の 2 つの働きがあります。

(1) *.designer.cs への自動生成コードにおいて、規定値のままならば
 プロパティ設定コードを生成しない(シリアライズを省略する)。

(2) デザイン時にプロパティが変更された後(プロパティが太字で表示されているとき)に、
 その項目を右クリック→リセットされた場合のリセット値として利用される。


もしも 単純な DefaultValueAttribute だけでは 表現しきれないような
プロパティの場合(たとえば Label 型を返すプロパティなど)においては、
そのプロパティに DefaultValueAttribute 属性を付けるのではなく、その代わりに
ShouldSerialize{propName}メソッド/Reset{propName}メソッドを実装します。
(これらのメソッドは、private にしても public にしても構いません)
http://msdn.microsoft.com/ja-jp/library/53b8022e.aspx



以下、本題とは関係ないところで。

> Vitual Studio 2010にて
ここはスペルミスなので見なかったことにして…。

> 利用言語:FrameWork 4.0 (C#)
FrameWork ではなく
Framework ですね。

引用返信 編集キー/
■63377 / inTopicNo.3)  Re[2]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ Unripe01 (3回)-(2012/08/21(Tue) 14:44:55)
2012/08/22(Wed) 10:43:04 編集(投稿者)

魔界の仮面弁士 様

たくさんのご指摘、ありがとうございます。
特にCaptionのプロパティ設定に関しては、色々とコードを変更していた折、
コードのミスという質問者としては大変な過ちをおかしてしまいました。
申し訳ございません。

Captionについては、
>▼やりたいこと▼
>■継承コントロールにプロパティを指定し、その初期値をデザイナ上で反映させる。
>■プロパティは、プリミティブ型として指定し、その実態は内部で保持しているオブジェクトクラスのプロパティを操作する

にあります通り
正式なコードは以下となります。
namespace TextBoxSample
{
public partial class TextBoxEX : TextBox
{
public TextBoxEX()
{
InitializeComponent();
}

private Label _Label = new Label();
/// <summary>
/// このコントロールのキャプションオブジェクトを指定または指定します
/// </summary>
public Label Label
{
get
{
return this._Label;
}
set
{
this._Label = value;
}
}

/// <summary>
/// テキストボックスのキャプションオブジェクトを指定または指定します
/// </summary>
[Category( "その他" )]
[DefaultValue( "でふぉるとばりゅ" )]
[Description( "テキストボックスのキャプションオブジェクトを指定または指定します" )]
[DesignerSerializationVisibility( DesignerSerializationVisibility.Visible )]
public string Caption
{
get
{
return this.Label.Text;
}
set
{
this.Label.Text = value;
}
}
}
}

>そもそも「Label 型」のメンバーに "でふぉるとばりゅ" という「文字列」は渡せないと思います。
その通りです・・・・すみません。
Stringを受け取り、保持しているLabel.Textの値を変更したいと考えています。

>などのようにして、別のコントロールに変更できてしまいます。
>本当に setter まで必要なのでしょうか?

後続の処理の都合上、必要としています。
( MVCモデルアプリケーションに手入れしているところで、Form上にTextBoxとLabelが1セットと意識している実装が元々あり、意識しているだけで、実際には、コード上、TextBoxとLabelはコードとして結びついていないので、それを解消するために、当TextBoxを作成しています。左記都合上、新しいコードにあるとおり
  本物の、public Label Labelが別途あります。View開発者は、別のコントローラから、Captionしか触れないようにしてあります。)


>そのプロパティに DefaultValueAttribute 属性を付けるのではなく、その代わりに
>ShouldSerialize{propName}メソッド/Reset{propName}メソッドを実装します。

記載誤りをしておいて申し訳ございませんが、
プロパティの型はStringなので、DefaultValueAttributeで可能と考えてます。
そもそも、2つのプロパティが、Private宣言しているLabelを操作しようとしているからおかしくなるのでしょうか?


引用返信 編集キー/
■63379 / inTopicNo.4)  Re[3]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ 魔界の仮面弁士 (27回)-(2012/08/21(Tue) 16:02:44)
No63377 (Unripe01 さん) に返信
> たくさんのご指摘、ありがとうございます。
コードを掲載するときは、投稿モードを[図表モード]に変更してください。
通常モードのまま送信すると、インデントが失われてしまいますので。


> private Label _Label = new Label();
ここで Label を生成していますが、Text や Size を設定している箇所が見当たりませんし、
そもそも Controls.Add すらしていないように見えます。
ここで new した Label を表示させるためのコードはどこにありますか?


> 特にCaptionのプロパティ設定に関しては、色々とコードを変更していた折、
Caption プロパティを string 型と Label 型のどちらにするかという話に戻りますが、
Label の生成・管理・破棄を TextBoxEX が担うのであれば、string のみの公開の方が良いですし、
Label を利用者側から提供する場合には、当初の通り、Label 型で公開した方が良いでしょう。



> Form上にTextBoxとLabelが1セットと意識している実装が元々あり、

UserControl を用意し、その中に TextBox と Label を内包させるような場合には、
Controls.Add は UserControl のデザイン時に行っておけば済みます。


一方、TextBox 継承クラスとする場合は、Label は Form上に貼ることになるので、

(案1) Label を管理するのは TextBoxEX 側とし、親フォームの特定の箇所に
 Label を Controls.Add / Remove するためのコードを用意する。

(案2) Label を管理するのは Form 側として、Label インスタンスを受け取るための
  プロパティを用意しておく。

などの対処が必要です。(レイアウトの自由度では案2の方が良いかも?)

いずれにせよ、この場合にはプリミティブ型ではなく Label 型での操作となるため、
DefaultValueAttribute の出番はありません。もしも DefaultValueAttribute を
使うのであれば、それは別のプロパティとして用意することになるでしょう。

-----------------
  // 案2 パターンで実装した場合のイメージコード

  public Label CaptionLabel {
    get { return this._CaptionLabel; }
    set {
      if (value != null) value.Text = "でふぉるとばりゅ";
      this._CaptionLabel = value;
    }
  }

  [DefaultValue("でふぉるとばりゅ")]
  public string Caption {
    get { return (CaptionLabel == null) ? null : CaptionLabel.Text; }
    set { if (CaptionLabel != null) CaptionLabel.Text = value; }
  }
-----------------


なお、上記コードは シリアライズ/デシリアライズの順を考慮していないため、
「ラベル割り当て→ テキスト割り当て」の順でデシリアライズされた場合には復元できますが、
「テキスト割り当て→ ラベル割り当て」の順でデシリアライズされると復元できません。

そのためこの場合には、ISupportInitialize インターフェイスも実装した方が良いでしょう。

引用返信 編集キー/
■63386 / inTopicNo.5)  Re[4]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ Unripe01 (5回)-(2012/08/21(Tue) 18:25:06)
2012/08/21(Tue) 18:25:32 編集(投稿者)
2012/08/21(Tue) 18:25:28 編集(投稿者)

魔界の仮面弁士 様

>コードを掲載するときは、投稿モードを[図表モード]に変更してください。
大変失礼しました。以後、気をつけます。


>ここで Label を生成していますが、Text や Size を設定している箇所が見当たりませんし、
>そもそも Controls.Add すらしていないように見えます。
>ここで new した Label を表示させるためのコードはどこにありますか?

長くなります。
少し複雑なのですが、まったく別のクラスで、定義した設定ファイルを元に、自動的に画面上のボタンやら、ラベルやら、画面上に必要なオブジェクトを生成している作りのアプリケーションがあり、その自動的に画面上のボタンやらを作っているものを、「メインコントローラ」とすると、
そこに張り付けられるのが上記の拡張コントロールです。
メインコントローラ内で、TextBoxEXに、Labelを紐づけます。
一度紐づけを行ったTextBoxEXは、そのまま別のコントローラ(サブ)に渡されたりします。
本来は
>UserControl を用意し、その中に TextBox と Label を内包
がよい対応かと思いますが、修正量とリスクの都合上、TextBoxを拡張(TextBoxEX)する対応を行った次第です。

で、これまたプロジェクトの都合上、TextBoxEX内に、入力チェックのすべてを実装することとなり、
おまけに、「●●は●●を入力してください」みたいなエラーも自動的に出すということで決定しました。

つまり、Captionはエラーメッセージ用の文字列、Labelは表示用(LabelコントロールのCaptionと、TextBoxEXのCaptionは違う場合もある)
という扱いです。(これも、設定ファイルから設定します)
そこまでならよかったのですが・・・ここから、メインコントローラ以外の画面、デザイナで作るWindows Formでも利用されることとなり、
今回、そのデザイナ上で、設定ファイルを使わずにデザイナ上で定義する

という要望があったので、上記のような作りになっています。
伝わりきらないかもですが・・・すみません。

とても端的にお伝えすると、
Labelコントロールを公開し、操作したい場合もあるし、
Captionとして、LabelコントロールのTextも操作したい場合もある。
上記2つは、利用者が異なります。ということです。
実装としては、案2と案1の混合を採用している節があります。

さて、
悩んでいること2の、
this.txtKensuEnd.Caption = global::sampleApp.app.resource.sample1.sampleValue1;
となってしまう件については、まったく解決のめどが立っていません。
リビルドを繰り返したり、TextBoxEXを再作成するなどを繰り返ししたところ、発生しなくなったので
「何かが壊れていた」と結論づけて終わりたいと思います。

最初に回答いただいた

>DefaultValueAttribute というのは、以下の 2 つの働きがあります。

>(1) *.designer.cs への自動生成コードにおいて、規定値のままならば
> プロパティ設定コードを生成しない(シリアライズを省略する)。

>(2) デザイン時にプロパティが変更された後(プロパティが太字で表示されているとき)に、
> その項目を右クリック→リセットされた場合のリセット値として利用される。

の部分ですが、大変参考になりました。リセットされた場合のリセット値で、
コントロールを張り付けた時のデフォルト値ではないということが分かっておりませんでした。

とりとめの無い質問になってしまったにも関わらず
丁寧にご回答いただきありがとうございます。

もう1日程度様子を見て、、上記回答がない場合解決済みにしたいと思います。

引用返信 編集キー/
■63387 / inTopicNo.6)  Re[4]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ Unripe01 (6回)-(2012/08/21(Tue) 18:32:06)
> なお、上記コードは シリアライズ/デシリアライズの順を考慮していないため、
> 「ラベル割り当て→ テキスト割り当て」の順でデシリアライズされた場合には復元できますが、
> 「テキスト割り当て→ ラベル割り当て」の順でデシリアライズされると復元できません。
>
> そのためこの場合には、ISupportInitialize インターフェイスも実装した方が良いでしょう。

すみませ、返信抜けておりました。
シリアライズ、デシリアライズについては考慮不要のため、問題ないです。
ありがとうございます。

引用返信 編集キー/
■63389 / inTopicNo.7)  Re[5]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ 魔界の仮面弁士 (30回)-(2012/08/21(Tue) 22:11:43)
No63372, No63386, No63387 (Unripe01 さん) に返信
> ※魔界の仮面弁士 からのご指摘があり、コード修正しています。
敬称略。

> public TextBoxEX()
> {
>     InitializeComponent();
> }
手元の環境で継承コントロールを作ってみたのですが、上記のコード部分が
自動生成されることはありませんでした。
(partial class の片割れとなる TextBoxEX.designer.cs も生成されない)

そちらでは、どのようにして生成されたのでしょうか?


なお、当方での作り方はこんな感じです。

(1) 新規プロジェクトを作成(WindowsApplication1)

(2) プロジェクトに、新規クラス TextBoxEX.cs を追加する。
 この時点のコードは下記の状態。

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

namespace WindowsFormsApplication1
{
    class TextBoxEX
    {
    }
}

(3) 上記コードをバッサリ削除して、No63372 の「継承コントロールクラス」の
 コードに入れ替える。

(4) このままだとエラーになるので、先頭に以下の using を追加。

using System.Windows.Forms;
using System.ComponentModel;

(5) InitializeComponent が無いと言われるので、コンストラクタ自体を
 まるごとコメントアウトする。

  // public TextBoxEX()
  // {
  //     InitializeComponent();
  // }

(6) ここで一度ソースコードウィンドウをすべて閉じてから
 WindowsApplication1 をビルド。その後、ソリューションエクスプローラから
 TextBoxEX.cs をダブルクリックすると、TextBoxEX.cs のデザイン画面が開くので、
 [ここをクリックするとコードビューに切り替わります]をクリック。

(7) この段階では、ソースコードに変化なし。
 InitializeComponent も コンストラクタも生成されていない。

(8) TextBoxEX のデザイン画面に、ツールボックスから Label をドロップしてきて、
 label1 が貼りついた状態にする。この状態で保存すると TextBoxEX.resx が出現。

(9) あらためて TextBoxEX.cs を見ると、private void InitializeComponent() が
 生成されていた。しかし、コンストラクタは存在しないまま。

(10) TextBoxEX に張り付いた label1 を削除してみるが、やはり
 InitializeComponent を呼び出すコンストラクタが自動生成されることは無かった。



> this.txtKensuEnd.Caption = global::sampleApp.app.resource.sample1.sampleValue1;
上記の手順(1)〜(7)を踏んで作った TextBoxEX.cs (TextBoxEX.resx は無し)を
Form1 に貼り付けてみたところ、一応、
  this.textBoxEX1.Caption = global::WindowsFormsApplication1.Properties.Resources.sampleValue2;
という状態にはなりました。でも、似ているけれど全然違いますね…。
(Unripe01 さんのコードでは小文字単数形の「resource」になったと書かれているが、
 こちらは、大文字始まり複数形の「Resources」になっている)


う〜ん。何が違うのでしょうね。


> となってしまう件については、まったく解決のめどが立っていません。
> リビルドを繰り返したり、TextBoxEXを再作成するなどを繰り返ししたところ、発生しなくなったので
> 「何かが壊れていた」と結論づけて終わりたいと思います。

とりあえず、上記手順(3) の段階のコードで、
    // private Label _Label = new Label();
    private Label _Label = new Label() { Text = "でふぉるとばりゅ" };
に変更し、さらにCaption プロパティの getter を下記のように変更してみました。

  get
  {
      //return this.Label.Text;
      if (this.Label != null && !this.Label.IsDisposed
          /* && this.Label.IsHandleCreated */)
      {
          return this.Label.Text;
      }
      else
      {
          return null;
      }
  }


この場合は、「.Caption = 〜.sampleValue1;」形式のデザイナコードにはならず、
以下のような結果になりました。

・初期値(でふぉるとばりゅ)の場合は、デザイナコードに Caption 設定の行が作られない。

・Form1デザイナで Caption プロパティを変更した場合には、
 「this.textBoxEX1.Caption = "あたらしい値だよ!";」
 形式のデザイナコードが生成された。


まぁ、実際にはこんなコードではマズイでしょうけれども。




> リセットされた場合のリセット値で、
> コントロールを張り付けた時のデフォルト値ではないということが分かっておりませんでした。
デフォルト値であっていますよ。

注意しなければならないのは、DefaultValueAttribute というものは、
『既定値』をデザイナに伝えるためのものであって、
『初期値』を設定するものではない、という点でしょうか。

最初のコードは、「初期値 = ""」、「既定値 = "でふぉるとばりゅ"」ですね。


> シリアライズ、デシリアライズについては考慮不要のため、問題ないです。
Caption プロパティにデザイナがアクセスする際に、Label プロパティが
適切に生成されているのか、生成タイミングが気になるところ。

引用返信 編集キー/
■63392 / inTopicNo.8)  Re[6]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ Unripe01 (7回)-(2012/08/22(Wed) 10:35:24)
Unripe01 さんの Web サイト
No63389 (魔界の仮面弁士 さん) に返信

> 敬称略。
大変失礼しました。修正しました。

> (partial class の片割れとなる TextBoxEX.designer.cs も生成されない)
> そちらでは、どのようにして生成されたのでしょうか?
当方では、
(1)新規プロジェクトを作成(WindowsApplication1)
(2)プロジェクトに、ユーザーコントロールを追加を選択(TextBoxEXを作成⇒この時点で InitializeComponent が作成される)
(3)2の時点ではUserControlを継承しているので、継承元をTextBoxに変更
(4)this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;となっている行を削除
 参考にしたURL
  ・チュートリアル : Visual C# による Windows フォーム コントロールからの継承
  http://msdn.microsoft.com/ja-jp/library/5h0k2e6x(v=vs.80).aspx
上記手順で作成しています。


> 上記の手順(1)〜(7)を踏んで作った TextBoxEX.cs (TextBoxEX.resx は無し)を
> Form1 に貼り付けてみたところ、一応、
>   this.textBoxEX1.Caption = global::WindowsFormsApplication1.Properties.Resources.sampleValue2;
> という状態にはなりました。でも、似ているけれど全然違いますね…。
> (Unripe01 さんのコードでは小文字単数形の「resource」になったと書かれているが、
>  こちらは、大文字始まり複数形の「Resources」になっている)

魔界の仮面弁士様と同じやり方でサンプルプロジェクトを作成しましたが・・・
>   this.textBoxEX1.Caption = global::WindowsFormsApplication1.Properties.Resources.sampleValue2;
にはならず、
this.textBoxEX1.Caption = ""
となりました。さらに謎が深まりました・・・

this.textBoxEX1.Caption = global::~~ となるプロジェクトの継承コントロールの作成方法については
申し訳ないですが不明です。
global::〜〜が再現された事については驚きです。「おかしな動き」だと思っていたので。
ですが、当方のものと似てるけど違いますね・・・
おそらく
global::WindowsFormsApplication1.Properties.Resources.
というパスは存在するでしょうし、sampleValue2も生成されてるかと思いますが、
こちらのglobal::は、存在もしないパスです。
ですから、Formに張り付けた時点でコンパイルエラーになります。

> この場合は、「.Caption = 〜.sampleValue1;」形式のデザイナコードにはならず、
> 以下のような結果になりました。
> まぁ、実際にはこんなコードではマズイでしょうけれども。
> 注意しなければならないのは、DefaultValueAttribute というものは、
> 『既定値』をデザイナに伝えるためのものであって、
> 『初期値』を設定するものではない、という点でしょうか。

同じく、確認しました。
既定値と初期値の考えを改めて理解させていただきました。
ありがとうございます。

>>シリアライズ、デシリアライズについては考慮不要のため、問題ないです。
> Caption プロパティにデザイナがアクセスする際に、Label プロパティが
> 適切に生成されているのか、生成タイミングが気になるところ。

なるほど。。。念のためLabelの宣言を上にしてましたが、寝耳に水ですね。
「適切」かどうか微妙です。
一方で、CaptionがLabelへアクセスする時点での保証は
上記new部分をコンストラクタに実装してやれば大丈夫な気もします。
しかし、プロパティの設定となると、デザイナ任せでは

>上記コードは シリアライズ/デシリアライズの順を考慮していないため、

の通り、Captionの行生成⇒Labelの行生成が十分にありえますね。
考慮できておりませんでした。ありがとうございます。

根本原因の理解⇒
たぶん、プロジェクトファイルがおかしくなってる。(仮説)
新規生成したプロジェクトでは再現しない。(理由)
でも、おかしくなっている場所が分からない(検討内容)
にはなっていませんが・・・

こういった問題が発生した時の解としては

1.継承コントロールのプロパティを
private Label _Label = new Label() { Text = "でふぉるとばりゅ" };
に変更する。

2.さらにCaption プロパティの getter を下記のように変更。
  get
  {
      //return this.Label.Text;
      if (this.Label != null && !this.Label.IsDisposed
          /* && this.Label.IsHandleCreated */)
      {
          return this.Label.Text;
      }
      else
      {
          return null;
      }
  }

上記で解消することが分かりましたので
解決とさせていただきたいと思います。

色々と検証いただきありがとうございます。
何か進展ありましたが、別途ご報告させていただきます。



解決済み
引用返信 編集キー/
■63396 / inTopicNo.9)  Re[7]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ 魔界の仮面弁士 (32回)-(2012/08/22(Wed) 14:22:58)
No63392 (Unripe01 さん) に返信
>(2)プロジェクトに、ユーザーコントロールを追加を選択
> (TextBoxEXを作成⇒この時点で InitializeComponent が作成される)
UserControl は、中に複数のコントロール等が内包されることを
前提にしているため、Form 等と同様に *.designer.cs が生成されますね。
TextBoxEX に InitializeComponent が必要かどうかは別として。


> this.textBoxEX1.Caption = ""
> となりました。さらに謎が深まりました・・・
実の所、その書式になる場合もありました。
何が分岐点なのかは不明です。不思議。


> global::WindowsFormsApplication1.Properties.Resources.
> というパスは存在するでしょうし、sampleValue2も生成されてるかと思いますが、
上記コードが生成された段階で、sampleValue2 リソースが生成される様子はありませんでした。
そのため、貼り付けた後でコンパイルしようとすると、Form1.designer.cs でエラーが生じています。

コードは多少異なりますが、最初の質問コードに掲載された問題と、
根っこは同じものであるような気はしますね。

ただ、global::sampleApp.app.resource.sample1.sampleValue1; が、何を表して
いるのかは分かりませんでした(リソースなら Properties.Resources.だと思っていた)。

プロジェクト名前空間は "sampleApp.app" なのでしょうか。
TextBoxEX の名前空間は "TextBoxSample" に変更されているようですが、
Form1 側の名前空間は sampleApp.app だったりするのでしょうか。


>> Caption プロパティにデザイナがアクセスする際に、Label プロパティが
>> 適切に生成されているのか、生成タイミングが気になるところ。
> なるほど。。。念のためLabelの宣言を上にしてましたが、寝耳に水ですね。

たとえば、デザイナコードが
 this.textBoxEX1.Label = this.label1;
 this.textBoxEX1.Caption = "test";
で初期化されていた場合と
 this.textBoxEX1.Caption = "test";
 this.textBoxEX1.Label = this.label1;
の順で初期化されていた場合の 2 パターンを考えてみます。

"test" がセットされる Label コントロールの状態について考えてみると、
前者の場合は label1 の Text を書き換えることになりますが、
後者は TextBoxEX 内の new Label() という、label1 とは別物の
Text を書き換えることになってしまいます。

すなわち現在のコード実装だと、前者の label1.Text は "test" に書き換わりますが、
後者の label1.Text は書き換わらないことになるという不安定さがあります。

もちろん、事前または事後に「this.label1.Text = "test";」という
初期化コードがあれば、たとえ別インスタンスであったとしても、
結果的には同じように進みます。

とはいえ、Label 側のシリアライズに頼るのであれば、そもそも Caption プロパティを
シリアライズする必要性は感じませんし、もしもシリアライズさせるなら、
内包 Label の Text で管理するのではなく、別途、Caption 用の string フィールドを
用意して、そこで管理しておいた方が良い気がします。


もう一点。.Controls.Add されていないコントロールについては、建前上は
自前で Dispose する必要があると思います(Control は IDisposable なので)。

ゆえにインスタンス管理のことを考えると、TextBoxEX 内では new Label() せずに
Label プロパティの初期値は private Label _Label = null; の方が良い気がします。


> しかし、プロパティの設定となると、デザイナ任せでは
>> 上記コードは シリアライズ/デシリアライズの順を考慮していないため、
> の通り、Captionの行生成⇒Labelの行生成が十分にありえますね。
そのためこういったケースでは、初期化順に影響されることが無いよう、
先述した ISupportInitialize インターフェイスが使われることがありますね。

今回の場合で言うと、void ISupportInitialize.EndInit() メソッドが呼ばれた時に、
Caption プロパティが保持している値と _Label.Text が保持している値を比べ、
違っていた場合は、_Label.Text に Caption の内容を反映させるようにするとか。
解決済み
引用返信 編集キー/
■63399 / inTopicNo.10)  Re[8]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ Unripe01 (8回)-(2012/08/22(Wed) 15:57:56)
Unripe01 さんの Web サイト
No63396 (魔界の仮面弁士 さん) に返信

お世話になります。
進展?かどうか不明ですが・・・

> 実の所、その書式になる場合もありました。
> 何が分岐点なのかは不明です。不思議。
> コードは多少異なりますが、最初の質問コードに掲載された問題と、
> 根っこは同じものであるような気はしますね。

global::~~については、以下のリンクが関連してるのではと推測します。
・MSのフィードバック
https://connect.microsoft.com/VisualStudio/feedback/details/683661/windows-forms-designer-cs-files-corrupt-after-changing-language#tabs
・上記インシデントへのリンク
http://stackoverflow.com/questions/3777519/vs-designer-replaces-empty-strings-with-resource-identifier-pointing-to-empty

これが関連しているとすれば、
結論として、VSの長年にわたるバグ?になるかと・・・

> ただ、global::sampleApp.app.resource.sample1.sampleValue1; が、何を表して
> いるのかは分かりませんでした(リソースなら Properties.Resources.だと思っていた)。
>
> プロジェクト名前空間は "sampleApp.app" なのでしょうか。
> TextBoxEX の名前空間は "TextBoxSample" に変更されているようですが、
> Form1 側の名前空間は sampleApp.app だったりするのでしょうか。

ソースコード掲載時点での名前空間は"sampleApp.app"でした。
TextBoxEXの方は"TextBoxSample"です。
global::~~が設定されていることに目がとられ、名前空間を意識しておりませんでした。

> とはいえ、Label 側のシリアライズに頼るのであれば、そもそも Caption プロパティを
> シリアライズする必要性は感じませんし、もしもシリアライズさせるなら、
> 内包 Label の Text で管理するのではなく、別途、Caption 用の string フィールドを
> 用意して、そこで管理しておいた方が良い気がします。

ありがとうございます。
デザイナ任せですと、アルファベット順かと思うので、
Captionが左記でLabelが後になる可能性大ですね・・・
今回私が携わっているプロジェクトについては、CaptionとLabelが同時に指定されることはないので(利用者が違う為)
綱渡り的にセーフな状態でした。危なかったです。

> もう一点。.Controls.Add されていないコントロールについては、建前上は
> 自前で Dispose する必要があると思います(Control は IDisposable なので)。
> ゆえにインスタンス管理のことを考えると、TextBoxEX 内では new Label() せずに
> Label プロパティの初期値は private Label _Label = null; の方が良い気がします。

おっしゃる通りです。
が、そうそう頻繁に起動〜終了が発生するアプリでもないので
Form破棄時点で参照がなくなり、いつかGCが処理してくれるという期待を込めて
恥ずかしながら未実装です。(呼び出し側のスコープも、でかすぎることもないので・・・)

> 先述した ISupportInitialize インターフェイスが使われることがありますね。
> 今回の場合で言うと、void ISupportInitialize.EndInit() メソッドが呼ばれた時に、
> Caption プロパティが保持している値と _Label.Text が保持している値を比べ、
> 違っていた場合は、_Label.Text に Caption の内容を反映させるようにするとか。

上記、考え方まで記載いただきありがとうございます。
このコードだと、どちらも利用される場合は必須ですね。

引用返信 編集キー/
■63400 / inTopicNo.11)  Re[9]: 継承コントロールのプロパティに存在しないパスが入る
□投稿者/ 魔界の仮面弁士 (33回)-(2012/08/22(Wed) 17:37:19)
一応、[解決済み]扱いで投稿しておきます。

No63399 (Unripe01 さん) に返信
> global::~~が設定されていることに目がとられ、名前空間を意識しておりませんでした。
C# の "global::" や VB の "Global." は、既定の「名前空間エイリアス修飾子」ですね。
http://www.atmarkit.co.jp/fdotnet/dotnettips/567globalalias/globalalias.html


> Form破棄時点で参照がなくなり、いつかGCが処理してくれるという期待を込めて
Contorls.Add されている Control (TextBox 等)や、
componets.Add されている Component (Timer 等)は、
Form 破棄時点で自動的に Dispose されます。

これらに Add されていなかったインスタンスについては、GC 任せですね。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -