情况如下,我有一个自定义的 TabControl
绘制 TabHeaders
本身,所以我可以选择颜色等。
当我有更多 TabHeaders
然后 TabControl
可以容纳并且箭头 ( spin control ) 出现时,就会出现问题。每当我然后一直向右循环(因此右侧不再有标题)时,箭头后面的制表符仍然部分存在。
图片展示:
我所知道的:
如果我手动调用 .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 );
最佳答案
我最终通过连接到 msctls_updown32
类的 WndProc
事件处理程序来修复它。我通过使用从 a codeproject tabcontrol project by Oscar Londono 获得的 Win32 类来做到这一点不过,我必须根据自己的需要对其进行一些调整。
在选项卡控件中添加代码:
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
如果有人有更好的答案或问题,请随时回答/评论!
关于c# - 移动后标签页标题保持在箭头按钮下方,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46338808/