none
リストボックスの表示を行単位で変える方法について RRS feed

  • 質問

  • windows7(64bit)のパソコンで、
     Microsoft Visual Studio 2010 Express
     Microsoft .NET Framework (Version 4.0.30319 RTMRel)
    を使って、VC++フォームアプリケーションを自習しています。

    リストボックスに表示する項目の文字色を、行ごとに変えて表示していきたいと思っております。その時のソースコードは、下記に引用しました。これは、普通にクラスのメンバとして定義した関数で、メイン処理の中で読んでいます。つまり、
    void Form1::listBox1_DrawItem(System::Object^  sender, System::Windows::Forms::DrawItemEventArgs^  e)
    の中の処理ではないという意味です。そのせいかどうか、
     1.本来、リストボックスの右端に出てくるべきスクロールバーが出て来ず、下の方が切れてしまっている。
     2.どこかの行を選択するためにクリックすると、文字が消えてしまい真っ白になってしまう
    という不具合があり、目的が達成できていません。

    今実験中のフォームでは、
      同じフォームに配置されたコマンドボタンをクリックしたときに、リストボックスの中身を表示する、
    という動きをするように作ろうと思っていたのですが、どうもDrawItemを呼ぶイベントがうまく発生できず、引用したような関数を作ってメインループで書くような実験をしてみたろころ文字色を変えることができるようになり、一見うまくいったように見えたのですが、現実は上記、1,2のように意図しない動きになってしまい、うまくはいっていませんでした。

     やっぱり、listBox1::Items::Addか何かで項目を追加しないと何かのきっかけで表示が消えてしまう(上記の2番)のかなと思い、直さないといけないと思っています。

    a. フォーム起動時や、Addで項目を追加した後のDrawItemの呼び方(=イベントの発生方法)

    b. Addで追加したものを後から色やフォントを変える方法

    がわかりません。どうしたらよいのでしょうか?

    int Form1::DrawString_on_ListBox1( String ^Arg_DrawText, int Atg_Tab )
    {
    
     static float Y_Axis = 0.0;
     
     switch( Atg_Tab )
     {
     case TAB0:
      SetClrVal = Color::FromName("Black");
      break;
     case TAB1:
      SetClrVal = Color::FromName("Blue");
      break;
     case TAB2:
      SetClrVal = Color::FromName("Red");
      break;
     case TAB3:
      SetClrVal = Color::FromName("Green");
      break;
     default:
      SetClrVal = Color::FromName("Ywellow");
     };
    
     StringFormat^ sf = gcnew StringFormat();
     SolidBrush^ br = gcnew SolidBrush(SetClrVal);
     Graphics^ LBGrpics = this->listBox1->CreateGraphics();
     LBGrpics->DrawString( Arg_DrawText, this->Font, br, (float)Atg_Tab, Y_Axis, sf );
     Y_Axis = Y_Axis + 12;
    
     return 0;
    }

    2013年10月4日 16:05

すべての返信

  • a. フォーム起動時や、Addで項目を追加した後のDrawItemの呼び方(=イベントの発生方法)

    b. Addで追加したものを後から色やフォントを変える方法

    がわかりません。どうしたらよいのでしょうか?

    試してませんがListBox::Invalidate()を呼び出すことで描画されませんか? とりあえず引数なしバージョンを呼べば。
    2013年10月4日 23:23
  • 佐祐理さん、ありがとうございます。

    恐れ入りますが、もう少し詳しくお願いできれば幸いです。

    現状、リストボックスに単に絵(実際には、文字です)を描いているだけで、add関数を実行したときのように、追加されていない扱いになっているのではないかと疑っています。だから、リストボックスの右端にスクロールバーが表示されることなくリストボックスをはみ出すところはケラれてしまうのではないかと、思っています。

    Invaidate関数をりようすることと、「右端スクロールバーが表示されずに、リストの下の方がケラれてしまう」の関係がよく理解できないのですが、どういうことですか?また、文字色を変えたりするときに、この関数はどんな役割を果たすのでしょうか?

    よろしくお願いいたします。

    2013年10月5日 3:16
  • ListBox にアイテムを追加せずにやりたいのなら、そもそも ListBox を使わずに、枠や選択の色、スクロールも含めて自分で実装することを考えた方がよいでしょう。
    ListBox にアイテムを追加し、そのアイテムの描画方法をカスタマイズするのであれば、カスタムドローというキーワードで調べてもらった方がよいでしょう。

    一例:http://dobon.net/vb/dotnet/control/lbownerdraw.html

    2013年10月5日 4:15
    モデレータ
  • Azuleanさん、有難うございます。

    >ListBox にアイテムを追加せずにやりたいのなら、

    方法にこだわりは持っている、というよりは、Addしたあとの色やフォントを変える方法が分からずににいろいろ探しているうちに、たまたま見つかった方法が質問文に引用したようなGraphicsを使った方法にたどり着いたという次第です。私はVC++でやっていますが、ご紹介いただいたURLも参考にさせて頂きたいと思います。

    1.「ListBox を使わずに、枠や選択の色、スクロールも含めて自分で実装することを考えた方がよいでしょう。」とのことですが、今回質問させて頂いたようなオーナードローでこのようなやりかたにそもそも無理だったと言うことですうか?(ご紹介いただいたUR+では、C#でやっているようですが)

    2.参考例のURLでは、ListBox1.Items.Addが実行される都度、ListBox1_DrawItemが呼ばれると言うことでしょうか?

    3.現状、リストボックスにの項目を選択しようとする(=リストボックスのどこかをクリックする)と。、表示された文字が消えて画面が真っ白になると言うのは、どのあたりに原因があるのでしょうか?どっかでリストボックス画面の更新みたいなことが行われているとは思うのですが、リストボックスに対して特にクリックイベントなどを実装しておらず、この実験の時にはlistBox1_DrawItem関数も呼ばれていないため、どこで、誰が真っ白にしているのか理解できずにおります。

    よろしくお願いいたします。
    2013年10月5日 5:59
  • 1.「ListBox を使わずに、枠や選択の色、スクロールも含めて自分で実装することを考えた方がよいでしょう。」とのことですが、今回質問させて頂いたようなオーナードローでこのようなやりかたにそもそも無理だったと言うことですうか?(ご紹介いただいたUR+では、C#でやっているようですが)

    取り組まれている方法は「オーナードロー」と呼ばれる方法ではありません。
    ListBox の描画とは別に、横から上書きしているコードになります。

    いわゆるオーナードロー、カスタムドローというのはコントロール・ウィンドウが提供する仕組みを利用するものです。

    2.参考例のURLでは、ListBox1.Items.Addが実行される都度、ListBox1_DrawItemが呼ばれると言うことでしょうか?

    描画する必要が生じる度に呼び出されますので、Add 以外のタイミングでも頻繁に呼ばれます。
    たとえば、スクロールするだけでも呼び出されます。

    このイベントでは、引数で指定されたアイテムについて描くだけでよいのです。

    3.現状、リストボックスにの項目を選択しようとする(=リストボックスのどこかをクリックする)と。、表示された文字が消えて画面が真っ白になると言うのは、どのあたりに原因があるのでしょうか?どっかでリストボックス画面の更新みたいなことが行われているとは思うのですが、リストボックスに対して特にクリックイベントなどを実装しておらず、この実験の時にはlistBox1_DrawItem関数も呼ばれていないため、どこで、誰が真っ白にしているのか理解できずにおります。

    ListBox の内部実装が白にしています。
    Windows の描画のメカニズムを学んでいただくとわかるかもしれませんが、再描画が必要になった場合、まずは背景色で塗りつぶし、その上で必要なものを描いていきます。
    ListBox の内部では背景色を塗った後、追加されているアイテムの描画、選択されているアイテムの描画などが行われます。

    前述しましたが、あなたの CreateGraphics を使ったコードはそれらの仕組みから外れた行為なので、ListBox の本来の描画が実行される度にかき消されます。
    基本的に「CreateGraphics を使わないもの」と考えていただいた方がよいでしょう。

    2013年10月5日 12:32
    モデレータ
  • Azuleanさん、有難うございます。

    >取り組まれている方法は「オーナードロー」と呼ばれる方法ではありません。
    >ListBox の描画とは別に、横から上書きしているコードになります。

    私が見たいくつかのホームページや、ご紹介いただいたページにも「リストボックスをオーナードローする方法です。」という説明があったものですから、ああいうやり方がオーナードローだと思っていたのですが、違うんですね。

    2013年10月5日 14:41
  • 私が見たいくつかのホームページや、ご紹介いただいたページにも「リストボックスをオーナードローする方法です。」という説明があったものですから、ああいうやり方がオーナードローだと思っていたのですが、違うんですね。

    私が紹介した DOBON.NET さんの記事 の内容はオーナードローを使ったものです。
    あなたが書いたコードのように CreateGraphics は出てきませんよね?そこが違います。

    2013年10月6日 2:01
    モデレータ
  • お世話になっております。

    Azuleanさん、ありがとうございます。

    あなたが書いたコードのように CreateGraphics は出てきませんよね?

    そういう意味だったんですね。やっとわかりました。物わかりが悪くて済みません。

    そして、私の環境でも文字色が変わることを確認できました。

    2013年10月6日 11:59
  • 参考としてですが、ListBoxに拘らなければ、DataGridViewのCellPaintingイベントハンドラで、いろいろな描画のカスタマイズを行うことができます。

    (参考)
    DataGridViewのセルを自分で描画する
    http://dobon.net/vb/dotnet/datagridview/ownerdrawcell.html

    DataGridViewでセルの一部だけ色を塗りたい
    http://homepage1.nifty.com/rucio/commu/ThreadDetail_ThreadId_9416.htm


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2013年10月7日 0:20
  • trapemiyaさん、ありがとうございます。

    情報ありがとうございます。

    こんな便利なものがあったのですね。知りませんでした。

    私でも使えるかどうか実験してみます。

    2013年10月7日 10:36