DOBON.NETプログラミング道掲示板
(現在 過去ログ1 を表示中)

[ 最新記事及び返信フォームをトピックトップへ ]

■33044 / inTopicNo.1)  コントロール名の取得とコンポーネントのプロパティ値の取得
  
□投稿者/ み 一般人(7回)-(2015/07/15(Wed) 17:38:11)
  • アイコン環境/言語:[windows7pro .net4.5 VS2012pro C#] 
    分類:[.NET] 

    すみません教えて下さい。
    	自作のカレンダーコントールを作りました。
    	作ったといってもボタンをクリックすると
    	あらかじめフォームに貼ったmonthCalendar1のVisible = trueにして
    	ボタン(ボタン継承で作成)近くにmonthCalendar1を移動させて
    	クリックにより日付を年月日別のテキストボックスセットするという内容です
    
    以下質問内容です。
    ■1 ボタンクリック時に自分自身の名前を取得したい
    		ButtonCren_Clickはカレンダー用複数のボタンに関連づけています。
    ■2 継承ボタンでのプロパティに設定した
    		dataCntrol と SetTextNameから値を取得したいが参照方法がわかりません。
    	
    ■3 カレンダーのクリックで取得したe.Start.ToShortDateString();を
    	SetTextNameにセットしたいのですがインスタンスに設定されていませんとエラーになります。
    	
    	
    以上 すみませんがご教授を御願いします。
    環境:windows7pro .net4.5 VS2012pro C#
    
    
    //==== Buttonを継承したコンポーネント
    
     public partial class ButtonEx : Button
        {
            public Component1()
            {
                InitializeComponent();
            }
    
            public Component1(IContainer container)
            {
                container.Add(this);
    
                InitializeComponent();
            }
    
            /// <summary>
            /// 呼び元のコントール名1 座標取得用
            /// </summary>
             private string _dataCntrol = string.Empty;
             public string dataCntrol
             {
                 get { return this._dataCntrol; }
                 set { this._dataCntrol = value; }
             }
    
    
             /// <summary>
             /// 呼び元のコントール名2 セット先コントール名
             /// </summary>
             private string _SetTextName = string.Empty;
             public string SetTextName
             {
                 get { return this._SetTextName; }
                 set { this._SetTextName = value; }
             }
    
    }
    
    //==== ボタンのクリックイベント
    public string TarText = string.Empty;
    private void ButtonCren_Click(object sender, EventArgs e)
    {
        Point mx = new Point();
        Point my = new Point();
    
    	//座標取得用コントロールからの取得
    	//■1 BtnCren1を呼び元の名前から取得したい
        string work = BtnCren1.dataCntrol;
        TarText = BtnCren1.SetTextName;
        mx.X = this.Controls[work].Location.X;
        my.Y = this.Controls[work].Location.Y;
    
    	//カレンダー表示位置の調整
    	//■2 tableLayoutPanel2を_dataCntrolから取得したい
        this.monthCalendar1.Top = my.Y + tableLayoutPanel2.Height;
        this.monthCalendar1.Left = mx.X + (tableLayoutPanel2.Width - monthCalendar1.Width);
    
        monthCalendar1.Visible = true; //カレンダーの表示
    }
    
    //==== カレンダーのDateSelectedイベント
    private void monthCalendar1_DateSelected(object sender, DateRangeEventArgs e)
    {
        string work = e.Start.ToShortDateString();
    
    	//セット先コントール名から名前を設定にそれぞれ日付をセット
    	
    	//■3オブジェクト参照がオブジェクト インスタンスに設定されていません。
        this.Controls[TarText + "Y"].Text = work.Substring(0, 4);
        this.Controls[TarText + "M"].Text = work.Substring(5, 2);
        this.Controls[TarText + "D"].Text = work.Substring(8, 2);
    
        monthCalendar1.Visible = false;
    }
    

引用返信 削除キー/
■33045 / inTopicNo.2)  Re[1]: コントロール名の取得とコンポーネントのプロパティ値の取得
□投稿者/ み 一般人(8回)-(2015/07/15(Wed) 20:43:35)
  • アイコン
    こんばんは自己レスです。
    自分自身のコントロール情報の取得はクリックのSenderから取得出来ました。
    ButtonEx btn = (ButtonEx)sender;
    string work = btn.dataCntrol;
    TarText = btn.SetTextName;

    またインスタンスエラーになる件はControls.Findで検索して
    参照するようにしました(なぜインスタンスエラーになる件はよくわかりません)

    Control[] csy = this.Controls.Find(TarText + "Y", true);
    if (csy.Length > 0) ((TextBox)csy[0]).Text = work.Substring(0, 4);

    Control[] csm = this.Controls.Find(TarText + "M", true);
    if (csm.Length > 0) ((TextBox)csm[0]).Text = work.Substring(5, 2);

    Control[] csd = this.Controls.Find(TarText + "D", true);
    if (csd.Length > 0) ((TextBox)csd[0]).Text = work.Substring(8, 2);

    以上宜しくお願いします。
引用返信 削除キー/
■33046 / inTopicNo.3)  Re[1]: コントロール名の取得とコンポーネントのプロパティ値の取得
□投稿者/ 魔界の仮面弁士 大御所(981回)-(2015/07/16(Thu) 01:50:31)
  • アイコンNo33044に返信(みさんの記事)
    > ■1 ボタンクリック時に自分自身の名前を取得したい
    イベント引数 sender を使ってください。
    sender はイベントの発生元となる Control そのものですので、
    わざわざ名前として取得する必要は無いはずです。

    「呼び元のコントール名」を String 型で保持するのではなく、
    「呼び元のコントール」を Control 型(あるいは Button 型)の変数で
    保持しておいた方が融通が利くでしょう。


    あるいは単に、Tag プロパティに放り込んでおくとか。



    > ButtonCren_Clickはカレンダー用複数のボタンに関連づけています。
    Cren とは何のことですか?


    > public Component1()
    > {
    これは…コンストラクタですか?
    それにしては、クラス名と合致しませんし、
    かといって、プロパティやメソッドの構文とも一致しないような…。


    もしかして、「class Component1 : Component」な Component 継承のクラスを、
    後から Button 継承に変更したりはしていませんか?

    Button は Component ではありません(Control です)ので、クラスを追加する際には、
    「コンポーネント クラス」ではなく、「カスタムコントロール」もしくは空の「クラス」を
    使って実装することをおすすめします。


    > ■2 継承ボタンでのプロパティに設定した
    > dataCntrol と SetTextNameから値を取得したいが参照方法がわかりません。
    小文字で始まるプロパティと大文字で始まるプロパティが混在しているとか、
    スペルが dataControl ではなく dataCntrol になっている点とか、
    命名に関する突っ込みどころはいろいろありますが、それはさておき。


    まず、dataCntrol と SetTextName は、それぞれどのように使い分けられるものなのでしょうか?
    どちらも、単に string 型のメンバーを保持しているもののようですが、
    その値を設定するのが誰の役目なのか、意図が読み取れませんでした。

    これらのプロパティに値をセットする役目を担うのは、その継承ボタン自身ですか?
    それとも、継承ボタンを貼りつけたフォーム側での操作を想定していますか?
    また、その逆にプロパティ値の「取得」を行うのは、継承ボタン自身? それともフォーム側?

    もそも継承ボタン自身が、内部的にアクセスするだけであれば、
     string s = this.dataCntrol;
     this.dataCntrol = 設定したい文字列;
    などの構文になるでしょう。一方、フォーム側からアクセスする場合は、
     string s = this.buttonEx1.dataCntrol;
     this.buttonEx1.dataCntrol = 設定したい文字列;
    などと書けます。


    > ■3 カレンダーのクリックで取得したe.Start.ToShortDateString();を
    ToShortDateString は避けましょう。これが返す文字列は、現在のカルチャに左右されます。
    たとえば、コントロールパネルが和暦設定になっていれば、
    2015 年ではなく、27年という日付文字列が返されることになりますので、
    それを SubString での切り出しても、望んでいる結果は得られないでしょう。


    アプリ側(この場合は Form)が、ユーザーへの表示目的などでカルチャ依存の文字列変換を
    行うのは構いませんが、ライブラリ側(この場合は ButtonEx)が今回のような場面で
    それを行うべきではありません。

    年・月・日を取り出したいのであれば、文字列型を経由させたりせず、日付型のまま
    Year, Month, Day などのプロパティを通じて、e.Start.Month などのように取り出すことをお奨めします。



    > SetTextNameにセットしたいのですがインスタンスに設定されていませんとエラーになります。
    > this.Controls[TarText + "Y"].Text = work.Substring(0, 4);

    ここでいう this というのは、ButtonEx ではなく Form のことでしょうか。
    だとしたら、使い方に問題があります。

    たとえば、Form 上に Panel を貼り、その上に Button を配置したとしましょう。
    その場合、this.Controls["button1"] ではアクセスできません。
    ボタンが置いてあるのは Form 上では無く、Panel 上なのですから、
    たとえば this.panel1.Controls["button1"] あるいは
    this.Controls["panel1"].Controls["button1"] のように
    記述しなければならないわけです。

    もしくは、その階層構造をたどるために、Controls.Find を利用するという
    方法もありますが、そもそも「名前」を使ってコントロールを辿るような
    コードを用いること自体、あまり好ましい書き方ではありません。

    名前で毎回検索するのではなく、コントロールのインスタンスそのものを
    管理した方が良いでしょう。たとえば、ButtonEx クラス側に、
     public TextBox YearBox { get; set; }
     public TextBox MonthBox { get; set; }
     public TextBox DayBox { get; set; }
    のようなプロパティを用意するとか。フォーム側でコレクション管理しても良いですけど。


    > Point mx = new Point();
    Point はクラスでは無く構造体ですから、new する必要はありません。


    > mx.X = this.Controls[work].Location.X;
    > my.Y = this.Controls[work].Location.Y;
    これって、
     Point pos = this.Controls[work].Location;
    だけで良いと思うのですが、mx と my を分けている理由は何でしょうか?

    その後の使い方を見る限り、仮に x と y で別々の変数で管理するとしても、
    そのまま int 型で十分そうですし、Point 型の変数を 2 つ用意する必要性を
    感じられませんでした。
引用返信 削除キー/
■33047 / inTopicNo.4)  Re[2]: コントロール名の取得とコンポーネントのプロパティ値の取得
□投稿者/ み 一般人(9回)-(2015/07/20(Mon) 02:55:48)
  • アイコン
    魔界の仮面弁士さん 返信ありがとうございます。
    返信遅くなりました。

    >イベント引数 sender を使ってください。
    >sender はイベントの発生元となる Control そのものですので、

    いままで使ったことなかったので必要性がわかりました。

    わざわざ名前として取得する必要は無いはずです。


    クラス名とメソッド名が一致しないのは投稿用にいろいろ手直ししている間に
    変になってしまいました。。



    >まず、dataCntrol と SetTextName は、それぞれどのように使い分けられるものなのでしょうか?
    >どちらも、単に string 型のメンバーを保持しているもののようですが、
    >その値を設定するのが誰の役目なのか、意図が読み取れませんでした。

    ボタンを継承したユーザコントロールを作成したのは
    押されたボタンの名前と位置を取得するコントロール名を取得するためです。
    今考えると priveteで変数に保存してもよかったのかなと思います。


    >年・月・日を取り出したいのであれば、文字列型を経由させたりせず、日付型のまま
    >Year, Month, Day などのプロパティを通じて、e.Start.Month などのように取り出すことをお奨めします。
    ご指摘ありがとうございます。


    >名前で毎回検索するのではなく、コントロールのインスタンスそのものを
    >管理した方が良いでしょう。たとえば、ButtonEx クラス側に、
    > public TextBox YearBox { get; set; }
    > public TextBox MonthBox { get; set; }
    > public TextBox DayBox { get; set; }
    >のようなプロパティを用意するとか。フォーム側でコレクション管理しても良いですけど。

    画面の項目名が多数で名前に規則性を持たせて検索でコントロール名を取得していますが
    不都合な場合はどんなものかあるのでしょうか
    速度などでしょうか


    > mx.X = this.Controls[work].Location.X;
    > my.Y = this.Controls[work].Location.Y;
    Locationは意味がなかったです。
    TOPとLeftを取得に変更しました。

    しかしgroupBox、tabControl上にあると位置がそのコントール内の位置になってしまいます。
    この場合はtabControl位置を加算してTOP位置を決めないといけないのでしょうか

    以上宜しくお願い致します。

引用返信 削除キー/
■33049 / inTopicNo.5)  Re[3]: コントロール名の取得とコンポーネントのプロパティ値の取得
□投稿者/ 魔界の仮面弁士 大御所(982回)-(2015/07/20(Mon) 17:16:01)
  • アイコンNo33047に返信(みさんの記事)
    > 今考えると priveteで変数に保存してもよかったのかなと思います。

    ちなみに自分の所では、TextBox にフォーカスが当たったときに、
    動的にその横にカレンダー呼び出しボタンが表示されるような
    カスタムの TextBox を作成して利用しています。



    > 不都合な場合はどんなものかあるのでしょうか
    名前で検索する行為そのものに反対しているわけではなく、
    使いどころの問題ということです。


    > 速度などでしょうか
    コンパイル時の検証性を理由に挙げる方もおられますね。
    http://jeanne.wankuma.com/tips/vb.net/form/findcontrol.html

    速度面については、実際に測定してみないと分からないので、それ自体は
    必ずしも重要とは言い切れない…という前置きをした上で:


    Controls.Find は、同じ名前を持つコントロールの存在も考慮しているため、
    子・孫コントロールまでを再帰的に辿って、全コントロールが走査されます。

    そのため、画面上のコントロール数が多くなるにつれ、
     var ctrl = this.Controls.Find("button1", true).FirstOrDefault();
    などで「名前検索」する方法は、
     var ctrl = this.button1;
    のように直接参照した場合と比べ、パフォーマンス面で不利になります。

    その速度低下がどの程度なのかは、実際に測定しないと分かりませんし、
    問題になるほどでも無いかもしれませんが、今回は特に、
    「項目名が多数」とのことですし、考慮しておいて損は無いと思います。


    たとえ名前検索を行うにしても、それは form1_Load などでの
    一回限りにしておき、そこで列挙したコントロールへの参照を
    Dictionary<,> や List<> あるいは配列などで自己管理することで、
    再検索の手間を省くのが有効です。
    http://dobon.net/vb/dotnet/control/buttonarray.html
    http://dobon.net/vb/dotnet/control/findcontrolbyname.html

    これならば、コントロール名に規則性を持たせたことを活かせますし、
    実行時の速度面も問題になりません。



    > 画面の項目名が多数で名前に規則性を持たせて検索でコントロール名を取得していますが
    名前に規則性を持たせることは重要ですね。
    特定のフォーム内(あるいは UserControl の内包コントロール)に限定して、
    内部的にそのような命名制約を付ける事は、実際よく行われます。


    しかし、テキストボックスの名前に規則性を持たせるのは、
    Form 側の管理上の都合に過ぎません。将来、項目数が増えてくるなどして、
    その名前付けルールを守りづらくなる状況を考えると、命名規則だけに
    頼ったコードというのは、拡張性の点で難があるという見方もできます。



    特に最初の質問では、「ボタン・カレンダー・テキストボックス」の関連付けを、
    どのクラスに担当させるのか、私には読み取れませんでした。


    「ボタン・カレンダー・テキストボックス」の関連付けを、Form 側に
    負わせるのなら、その関連付けルールを名前の規則性に頼るのも一つの選択肢です。
    ただ、それだけならば、ButtonEx を用意するほどでも無いように感じていました。
    (フィールド変数あるいはTag プロパティでも十分に管理できそうですし)


    今回、わざわざ ButtonEx という継承コントロールを用意するからには、
    他のフォームでも使いまわせるような拡張性を持たせるためであり、
    それらの関連付け情報は、ButtonEx 自体に持たせたいのかな、と深読みしていました。
    (ButtonEx は、クリックされた時の対象が自分だと知っていますしね)

    だとすれば、ButtonEx が、他のコントロールを名前で検索するのは、
    実装としては聊か乱暴であると考え、名前ではなくインスタンスそのものを
    管理することをお奨めしたのが、先の No33046 の回答です。



    > しかしgroupBox、tabControl上にあると位置がそのコントール内の位置になってしまいます。
    ButtonEx や TextBox が、TabControl(正確には TabPage) や GroupBox の上に
    おいてあるのだとすれば、MonthCalendar も同じコンテナ(GroupBox 等)の上に
    置いておくのが、座標計算上は楽だと思います。

    それで都合が悪いのなら、座標調整で対応することになりますね。



    > この場合はtabControl位置を加算してTOP位置を決めないといけないのでしょうか
    GroupBox 内にさらに GroupBox がある場合などもありますので、
    スクリーン座標で算出するのが良いと思います。具体的にはこんな感じ。


    // 配置先(対象コントロールの下中央部)の座標を算出
    Point pos = ctrl.Location;
    pos.Offset((ctrl.Width - monthCalendar1.Width) / 2, ctrl.Height);

    // それを一度、スクリーン座標に置き換えて…
    Point posScreen = ctrl.Parent.PointToScreen(pos);

    // それをさらに、カレンダーにとっての座標に復元
    Point posCalendar = monthCalendar1.Parent.PointToClient(posScreen);

    // そしてカレンダー表示
    monthCalendar1.Location = posCalendar;
    monthCalendar1.BringToFront();
    monthCalendar1.Show();


    あるいは DataTimePicker のカレンダー表示のように、カレンダーを
    フォームの外にまではみ出して表示できるよう、MonthCalendar を載せた
    枠なしフォームを用意し、それを表示するのも良いかと思います。
引用返信 削除キー/
■33056 / inTopicNo.6)  Re[4]: コントロール名の取得とコンポーネントのプロパティ値の取得
□投稿者/ み 一般人(11回)-(2015/07/27(Mon) 08:55:37)
  • アイコン魔界の仮面弁士さんいつも詳しい回答ありがとございます。
    カレンダーは作り込みと通る道なんですかね
    付属のdateTimePickerに日付がデフォルトセットされていなければ使用できたのと
    maskedTextBox1とmonthCalendarの組み合わせで実装すればよかったのかなと
    現在htableLayoutPanelの中にテキストボックスを年月日分とカレンダーボタンの
    構成で実装しました(まだ部品化してない・・)

    みなさんのカレンダー入力付きコントロールをみたいですね
    ありがとうございました

解決み!
引用返信 削除キー/



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

このトピックに書きこむ

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

Mode/  Pass/


- Child Tree -