情况如下,我有一个自定义TabControl
来描绘TabHeaders
本身,所以我可以选择颜色等。
当我有更多TabHeaders
然后TabControl
可以适合并且箭头(spin control)出现时,就会出现问题。每当我然后一直向右循环(因此没有更多的标题在右边)箭头后面的tabheader仍然部分存在。
要展示的图片:
我所知道的:
如果我手动调用.Invalidate()
使其重新显示问题将消失,但是我找不到由箭头键触发的事件(它没有滚动事件)。
不起作用的事件:
I had WndProc in mind(它有get'spin'的情况,听起来像是spincontrol)
自定义标签控制代码:
class ColoredTabControl : TabControl
{
public Color ActiveTabColor
{
get
{
return _activeTabColor.Color;
}
set
{
_activeTabColor.Color = value;
this.Invalidate();
}
}
public Color DefaultTabColor
{
get
{
return _defaultTabColor.Color;
}
set
{
_defaultTabColor.Color = value;
this.Invalidate();
}
}
private SolidBrush _activeTabColor = new SolidBrush( Color.LightSteelBlue );
private SolidBrush _defaultTabColor = new SolidBrush( Color.LightGray );
private int _biggestTextHeight = 0;
private int _biggestTextWidth = 0;
public ColoredTabControl()
{
this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.UserPaint, true );
}
protected override void OnPaint( PaintEventArgs e )
{
base.OnPaint( e );
var graphics = e.Graphics;
TabPage currentTab = this.SelectedTab;
if( TabPages.Count > 0 )
{
var biggestTextHeight = TabPages?.Cast<TabPage>()?.Max( r => TextRenderer.MeasureText( r.Text, r.Font ).Height );
var biggestTextWidth = TabPages?.Cast<TabPage>()?.Max( r => TextRenderer.MeasureText( r.Text, r.Font ).Width );
if( biggestTextHeight > _biggestTextHeight || biggestTextWidth > _biggestTextWidth )
{
_biggestTextWidth = ( int ) biggestTextWidth;
_biggestTextHeight = ( int ) biggestTextHeight;
this.ItemSize = new Size( _biggestTextWidth + 5, _biggestTextHeight + 10 );
}
}
for( int i = 0; i < TabPages.Count; i++ )
{
TabPage tabPage = TabPages[ i ];
Rectangle tabRectangle = GetTabRect( i );
SolidBrush brush = ( tabPage == currentTab ? _activeTabColor : _defaultTabColor );
graphics.FillRectangle( brush, tabRectangle );
TextRenderer.DrawText( graphics, tabPage.Text, tabPage.Font, tabRectangle, tabPage.ForeColor );
}
}
protected override void OnPaintBackground( PaintEventArgs e )
{
base.OnPaintBackground( e );
}
}
重复问题的一些表单代码:
var tabControl = new ColoredTabControl() { Width = 340, Height = 300, Top = 100 };
tabControl.TabPages.Add( "text" );
tabControl.TabPages.Add( "another tab");
tabControl.TabPages.Add( "yet another" );
tabControl.TabPages.Add( "another one" );
tabControl.TabPages.Add( "another" );
tabControl.DefaultTabColor = Color.Red; //To see the issue clearly
Controls.Add( tabControl );
答案 0 :(得分:2)
我最终通过挂钩WndProc
类的msctls_updown32
事件处理程序来修复它。我通过使用从a codeproject tabcontrol project by Oscar Londono获得的Win32类来完成此操作。但我必须根据我的需要对其进行一些调整。
在tabcontrol中添加了代码:
protected override void OnSelectedIndexChanged( EventArgs e )
{
base.OnSelectedIndexChanged( e );
this.Invalidate();
}
protected override void OnControlAdded( ControlEventArgs e )
{
base.OnControlAdded( e );
FindUpDown();
}
protected override void Dispose( bool disposing )
{
if(scUpDown != null)
{
scUpDown.SubClassedWndProc -= scUpDown_SubClassedWndProc;
}
base.Dispose( disposing );
}
bool bUpDown = false;
SubClass scUpDown = null;
private void FindUpDown()
{
bool bFound = false;
// find the UpDown control
IntPtr pWnd =
Win32.GetWindow( this.Handle, Win32.GW_CHILD );
while( pWnd != IntPtr.Zero )
{
// Get the window class name
char[] fullName = new char[ 33 ];
int length = Win32.GetClassName( pWnd, fullName, 32 );
string className = new string( fullName, 0, length );
if( className == Win32.MSCTLS_UPDOWN32 )
{
bFound = true;
if( !bUpDown )
{
// Subclass it
this.scUpDown = new SubClass( pWnd, true );
this.scUpDown.SubClassedWndProc +=
new SubClass.SubClassWndProcEventHandler(
scUpDown_SubClassedWndProc );
bUpDown = true;
}
break;
}
pWnd = Win32.GetWindow( pWnd, Win32.GW_HWNDNEXT );
}
if( ( !bFound ) && ( bUpDown ) )
bUpDown = false;
}
private int scUpDown_SubClassedWndProc( ref Message m )
{
switch(m.Msg)
{
case Win32.WM_LCLICK:
//Invalidate to repaint
this.Invalidate();
return 0;
}
return 0;
}
此课程的其他部分仍然与OP中的100%相同
Win32课程:
internal class Win32
{
/*
* GetWindow() Constants
*/
public const int GW_HWNDFIRST = 0;
public const int GW_HWNDLAST = 1;
public const int GW_HWNDNEXT = 2;
public const int GW_HWNDPREV = 3;
public const int GW_OWNER = 4;
public const int GW_CHILD = 5;
public const int WM_NCCALCSIZE = 0x83;
public const int WM_WINDOWPOSCHANGING = 0x46;
public const int WM_PAINT = 0xF;
public const int WM_CREATE = 0x1;
public const int WM_NCCREATE = 0x81;
public const int WM_NCPAINT = 0x85;
public const int WM_PRINT = 0x317;
public const int WM_DESTROY = 0x2;
public const int WM_SHOWWINDOW = 0x18;
public const int WM_SHARED_MENU = 0x1E2;
public const int WM_LCLICK = 0x201;
public const int HC_ACTION = 0;
public const int WH_CALLWNDPROC = 4;
public const int GWL_WNDPROC = -4;
//Class name constant
public const string MSCTLS_UPDOWN32 = "msctls_updown32";
[ DllImport("User32.dll",CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hwnd, char[] className, int maxCount);
[DllImport("User32.dll",CharSet = CharSet.Auto)]
public static extern IntPtr GetWindow(IntPtr hwnd, int uCmd);
}
#region SubClass Classing Handler Class
internal class SubClass : System.Windows.Forms.NativeWindow
{
public delegate int SubClassWndProcEventHandler(ref System.Windows.Forms.Message m);
public event SubClassWndProcEventHandler SubClassedWndProc;
private bool IsSubClassed = false;
public SubClass(IntPtr Handle, bool _SubClass)
{
base.AssignHandle(Handle);
this.IsSubClassed = _SubClass;
}
public bool SubClassed
{
get{ return this.IsSubClassed; }
set{ this.IsSubClassed = value; }
}
protected override void WndProc(ref Message m)
{
if (this.IsSubClassed)
{
if (OnSubClassedWndProc(ref m) != 0)
return;
}
base.WndProc(ref m);
}
private int OnSubClassedWndProc(ref Message m)
{
if (SubClassedWndProc != null)
{
return this.SubClassedWndProc(ref m);
}
return 0;
}
}
#endregion
如果有人有更好的答案或问题随时回答/评论!
答案 1 :(得分:1)
如果有人对如何挂钩tabcontrol的实际滚动事件有任何想法或想法,请回答/评论。
如果您想知道在单击这些滚动按钮时如何获取标签页眉的滚动事件,则可以处理WM_HSCROLL
的{{1}}事件:
TabControl
这样,您可以在使用这些按钮滚动后使控件无效。
同样,对于用户使用键盘更改选定选项卡的情况,您可以覆盖const int WM_HSCROLL = 0x0114;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_HSCROLL)
this.Invalidate();
}
,并在调用基本方法后,使控件无效。