- PR -

TabコントロールのTab描画方法

1
投稿者投稿内容
優希
ベテラン
会議室デビュー日: 2003/08/12
投稿数: 92
投稿日時: 2003-11-27 13:38
こんにちわ。
日頃、参考にさしてもらっております。

今回は、Tabコントロールの描画方法について
質問させて頂きます。


開発環境は、Visual Studio .NET 2002(VC++)、
Windowsフォームを使って行います。


問題になっていることは、
フォーカスがあるTabの背景(見出し部分)に色をつけたいのです。
 __________
|タブA|タブB|タブC|
|    |___|___|___
|                   |

処理としましては、
タブBに移動すれば、タブBの背景色が変わり、
タブAの背景色が元の色(灰色など)に戻る、
という仕掛けをつくりたいのです。


私が作成したアプリを実行すると、
起動時にまずタブAにフォーカスがあり、
そのままキーダウン処理やマウスクリック処理で
フォーカスをタブ上で移動すると、
正常に背景色が変化します。

しかし、タブ以外のコントロールへ一回フォーカスを移し、
(例えば、タブA内の別のコントロールをクリック、など)
再度、タブへフォーカスを移すと
背景色があるタブとフォーカスが移ったタブで、
(背景色があるタブにフォーカスした場合は、このタブのみ)
背景色と表示文字列が消えてしまいます。

いろいろなイベント処理が関わってくると思うのですが、
正常に動作するようなアドバイスをお願い致します。


以下のようにコーディングしました。
(サイズ、ロケーション等は省きます)

コード:
///
/// コントロールの生成
///
void MainForm::MakeForm(){

	// タブコントロール生成
	TabControl *tabCtrl = new TabControl();
	tabCtrl->DrawMode = TabDrawMode:wnerDrawFixed; // 描画方法設定
	tabCtrl->Click += (new EventHandler(this, &MainForm::Tab_Click));
	tabCtrl->DrawItem += new DrawItemEventHandler(this, OnDrawItem);
	tabCtrl->LostFocus += new EventHandler(this,
                                    &MainForm::TabCtrl_LostFocus);
	this->Controls->Add(tabCtrl);  // Formに設定

	// タブページA生成
	TabPage *tabPageA = new TabPage();
	tabPageA->Paint += (new PaintEventHandler(this,
                              &MainForm::TabPageA_Paint));
	tabCtrl->Controls->Add(tabPageA);	// タブコントロールに設定	

	// テキストボックス生成(タブページA内へ)
	TextBox *txtBoxA = new TextBox();
	tabPageA->Controls->Add(txtBoxA);	// タブページAに設定	

	// タブページB生成
	TabPage *tabPageB = new TabPage();
	tabPageB->Paint += (new PaintEventHandler(this, 
                            &MainForm::TabPageB_Paint));
	tabCtrl->Controls->Add(tabPageB);	// タブコントロールに設定	

	// タブページC生成
	TabPage *tabPageC = new TabPage();
	tabPageC->Paint += (new PaintEventHandler(this, 
                            &MainForm::TabPageC_Paint));
	tabCtrl->Controls->Add(tabPageC);	// タブコントロールに設定
}


///
///	タブコントロール Drawイベント
///
void MainForm:nDrawItem(Object *sender, DrawItemEventArgs* e){

	bool isShowTab = false;
	int tab_index;
	Drawing::SolidBrush *sb;	
	StringFormat *format;

	//タブのサイズ格納
	Rectangle tabRect;	    // int型
	RectangleF tabRectF;  // float型

	SuspendLayout();

	Graphics* g = e->Graphics;

	TabControl *tabCtrl = dynamic_cast<TabControl *>(sender);
	int tabIndex = tabCtrl->SelectedTab->TabIndex; // タブインデックスの取得
	
	// フォーカスがあるタブの描画
	if((tabIndex != lastTabIndex) || 	// フォーカスがあるタブが変わったとき
		(flgFormLoad)        ||	// 起動時
		(isTabCtrlLostFocus) || 	// タブコントロールにフォーカスがなくなったとき
		(isClickTab) ){		// フォーカスタブがクリックされたとき

		// 前回と今回でフォーカスがあるタブインデックスが違う場合
		if(tabIndex != lastTabIndex){

                        // 今回のフォーカスのあるタブインデックスを設定
                        tab_index = tabIndex;
                        // 前回のフォーカスタブインデックスに設定
  	               lastTabIndex = tabIndex;

                  // 前回と今回のフォーカスがあるタブインデックスが同じ場合
		}else{
		      // 前回のフォーカスありタブインデックスを設定
                        tab_index = lastTabIndex;
		}
		
                  // タブのサイズ取得(int型)
		tabRect = tabCtrl->GetTabRect(tab_index);

		// intからfloatへキャスト
		tabRectF.X = (float)tabRect.X;
		tabRectF.Y = (float)tabRect.Y;
		tabRectF.Width = (float)tabRect.Width;
		tabRectF.Height = (float)tabRect.Height;

		sb = new Drawing::SolidBrush(Color::Lime); // 背景色設定
		g->FillRectangle(sb, tabRect);					// サイズ設定
		format = new StringFormat();
		format->Alignment=StringAlignment::Center; 

		// 描画 (表示文字列, フォント, 文字色, サイズ, 文字の表示位置)
		g->DrawString(tabCtrl->SelectedTab->Text, tab->Font, 
                       new SolidBrush(Color::Black), tabRectF, format);	
        

		// フラグ更新
		flgFormLoad = false;       // 初期時フラグ OFF
		isShowTab = true;		// フォーカスタブ以外の表示 ON
		isTabCtrlLostFocus = false;	// アクティブタブ表示内へカーソル移動 OFF
		isClickTab = false;	// アクティブタブページ内のタブをクリック OFF
	}

	// フォーカスタブ以外の表示
	if(isShowTab){
   	     int tabCnt = tabCtrl->TabCount;	// タブの数
	     for(int i=0; i < tabCnt; i++){
		// フォーカスのあるタブの場合はとばす
		if(i == tab_index){
		     continue;
		}

                  // タブのサイズ取得(int型)
		tabRect = tabCtrl->GetTabRect(i);

		// intからfloatへキャスト
		tabRectF.X = (float)tabRect.X;
		tabRectF.Y = (float)tabRect.Y;
		tabRectF.Width = (float)tabRect.Width;
		tabRectF.Height = (float)tabRect.Height;

		sb = new Drawing::SolidBrush(Color:arkGray);
		g->FillRectangle(sb, tabRect);					// サイズ設定
		format = new StringFormat();
		format->Alignment=StringAlignment::Center;

		// 描画 (表示文字列, フォント, 文字色, サイズ, 文字の表示位置)
		g->DrawString(tabCtrl->TabPages->get_Item(i)->Text, 
                                 tab->Font, new SolidBrush(Color::Black), 
                                 tabRectF, format);
		}
		isShowTab = false;		
	}	
	ResumeLayout(false);
}

///
///	タブコントロール Clickイベント
///
void MainForm::Tab_Click(Object *sender, EventArgs *e){
	isClickTab = true;
}

///
///	タブコントロール LostFocusイベント
///
void MainForm::TabCtrl_LostFocus(Object *sender, EventArgs *e){
	isTabCtrlLostFocus = true;
}



分かりにくいとは思いますが、
宜しくお願い致します。

Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2003-11-27 18:40
こんにちわ。諸農です。

引用:

優希さんの書き込み (2003-11-27 13:38) より:
開発環境は、Visual Studio .NET 2002(VC++)、
Windowsフォームを使って行います。



こちらの環境が .NET 1.1 C#Builderで、同じ環境ではないので、
あまり参考にはならないような気もしますが。。

引用:

タブBに移動すれば、タブBの背景色が変わり、
タブAの背景色が元の色(灰色など)に戻る、
という仕掛けをつくりたいのです。



こちらでは単純にDrawItemイベントハンドラを使うだけで
うまくいっているようです。

コード:
private void tabControl1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
        for (int i = 0;i<tabControl1.Controls.Count;i++)
        {
                SolidBrush sb;
                Rectangle _rect = tabControl1.GetTabRect(i);
                Graphics g = e.Graphics;
                if (i == tabControl1.SelectedIndex){
                        sb = new SolidBrush(Color.Red);
                }else{
                        sb = new SolidBrush(Color.LightBlue);
                }
                g.FillRectangle(sb,_rect);
        }
}



ではでは(^^)/

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
優希
ベテラン
会議室デビュー日: 2003/08/12
投稿数: 92
投稿日時: 2003-11-27 20:25
優希です。
ありがとうございます。

早速、試してみたのですが、
フォーカスがあるタブ以外は
タブの輪郭が表示されない現象に陥ります。
本来、タブが存在するで『あろう』個所をクリックすると、
そのタブのみが表示されます。


諸農和岳さんのコーディングされた
「tabControl1」は、DrawItemイベントハンドラの
引数から生成したTabControlと考えて宜しいですか?


それと、
このタブに文字列を追加したいのですが、
何かよい方法がありますでしょうか?

私もいろいろ試してみますので、
何かあればご指摘ください。
宜しくお願い致します。
優希
ベテラン
会議室デビュー日: 2003/08/12
投稿数: 92
投稿日時: 2003-11-27 21:20
優希です。
自己レスです。

タブに文字列の表示は、
Jubeiさんのコードに以下のコードを加えると
出来ました。

コード:
// intからfloatへキャスト
tabRectF.X = (float)tabRect.X;
tabRectF.Y = (float)tabRect.Y;
tabRectF.Width = (float)tabRect.Width;
tabRectF.Height = (float)tabRect.Height;

g->DrawString(tab->TabPages->get_Item(i)->Text, tab->Font, 
	            new SolidBrush(Color::Black), tabRectF, format);



あと、タブの輪郭の表示なのですが、
タブの外観を設定するプロパティ[Appearance]を使って、
[Normal, Buttons, FlatButtons]のNormal以外にすれば、
多少、タブの境界線が出ますが、
ボタン型になってしまい、望みどおりではありません。

できれば、Normal時で境界線が出る方法があれば、
アドバイスの程、宜しくお願いします。


また、
この部分に関係が出てくるのか分かりませんが、
下記のタブA〜C以外のDの部分(タブではない部分)の
背景色を変更することは
可能なのでしょうか?

 __________
|タブA|タブB|タブC|  D
|    |___|___|___
|                   |
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2003-11-27 22:27
こんにちわ。諸農です。

VS.NET2003/.NET v1.1の環境で行いました。

コード:
private void tabControl1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
    TabControl tb = sender as TabControl;
    if (tb == null)return;
    for (int i = 0;i<tb.TabCount;i++)
    {
        string str = ((TabPage)tb.Controls[i]).Text;
        SolidBrush sb;
        Rectangle _rect = tb.GetTabRect(i);
        Graphics g = e.Graphics;
        if (i == tb.SelectedIndex)
        {
            sb = new SolidBrush(Color.Red);
        }
        else
        {
            sb = new SolidBrush(Color.LightBlue);
        }
        g.FillRectangle(sb,_rect);
        g.DrawRectangle(new Pen(Color.Gray),_rect);
        g.DrawString(str,tb.Font,
                     new SolidBrush(Color.Black),
                     _rect.Left+2,_rect.Top+2);
    }
}



こんな感じでしょうか。。
輪郭の3Dと背景色はどうすればいいのかはちょっとわかりませんです。。

ではでは(^^)/

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
mei
大ベテラン
会議室デビュー日: 2003/04/08
投稿数: 114
投稿日時: 2003-11-27 23:23
こんばんは、meiです。

引用:

優希さんの書き込み (2003-11-27 21:20) より:
この部分に関係が出てくるのか分かりませんが、
下記のタブA〜C以外のDの部分(タブではない部分)の
背景色を変更することは
可能なのでしょうか?

 __________
|タブA|タブB|タブC|  D
|    |___|___|___
|                   |




DrawItemイベントですが、これはページ毎に呼ばれると思うので、
1回について1つのページを描画する方が良いと思います。
(3ページあれば3回呼び出される)

C#で申し訳ないのですが、サンプルコードを書いてみました。

コード:
private void tabControl1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) {
			TabControl tab = (TabControl)sender;
			Graphics g = e.Graphics;
			Brush b;

			if (e.Index == tab.SelectedIndex) {
				// イベントが選択ページに対しての場合
				b = new SolidBrush(Color.Blue);
			}
			else {
				// イベントがその他のページの場合
				b = new SolidBrush(Color.LightBlue);
			}
			g.FillRectangle(b, tab.GetTabRect(e.Index));

			if (e.Index == tab.TabCount - 1) {
				// 最後のタブの場合、タブ領域の隣からタブコントロール全体の右端まで色を塗る
				b = new SolidBrush(Color.LightGreen);
				Rectangle r = tab.GetTabRect(e.Index);
				// タブコントロールのウィンドウハンドルからGraphicsオブジェクトを生成
				g = Graphics.FromHwnd(tab.Handle);
				g.FillRectangle(b, r.Right, r.Top, tab.Width - r.Right, r.Height);
				g.Dispose();
			}
		}



優希さんの図にあったD領域ですが、
Cページを描画するタイミングで、Cページの右端からタブコントロールの右端まで、
また、高さはタブの高さと同じで描画すれば良いんじゃないかと思います。

如何でしょうか?
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2003-11-28 06:41
諸農です。

引用:

meiさんの書き込み (2003-11-27 23:23) より:

DrawItemイベントですが、これはページ毎に呼ばれると思うので、
1回について1つのページを描画する方が良いと思います。
(3ページあれば3回呼び出される)



あらぁ!これは気付きませんでした。
いい事を教えていただきました(^.^)
ありがとうございます。

ではでは(^^)/
_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
優希
ベテラン
会議室デビュー日: 2003/08/12
投稿数: 92
投稿日時: 2003-11-28 09:43
優希です。

諸農さん、meiさん
ありがとうございました。

D領域の背景色の設定が出来ました。
タブの外観が決まった形しかないのが残念ですが、
それ以外が完成して満足しております。

また何かありましたら、
宜しくお願い致します。
1

スキルアップ/キャリアアップ(JOB@IT)