|
Unfortunately, it does not save controls such as ToolTip, Notify or Menus. Could you give me a few ideas?
|
|
|
|
|
How to add keyboard up/down/right/left control response? Can you provide some sample code,
much appreciate!
|
|
|
|
|
Thanks for the article, it was quite interesting. I found this article cause I was looking for a runtime forms designer solution that can be embedded into a host application allowing power users to create custom workflows. This however would need power users to be able to write some code and bind the code to the eventhandlers of controls on a designed form. I could not see eventhandlers or code views exposed through your solution, is it possible in your solution?
Thanks
|
|
|
|
|
Thanks for the article. However, I am disappointed to learn (if I understood correctly) that other readers discovered that your solution doesn't persist tab control contents... I was even more disappointed when I read your reply--that you left out the tab control on purpose in order to keep the article "simple"... I think most readers would like to see a complete solution, not necessarily a simple one. Having said that, I understand that you are doing this work for free, so I want to convey my appreciation for the hard work you offer to the community.
It seems from the messages posted here that your solution won't persist controls that are designed with a container control, such as in a groupbox or tabcontrol control. Is that true? How difficult would it be to modify your code so that it can recurse into container controls in order to persist their children? And consider what if you have a tab control then has an embedded tab control and some group boxes? That's the reason the solution must be recursive...
Finally, I've not experimented with it yet but wouldn't all of this be easier if leveraging the CodeDomDesignerLoader feature already built into .NET 2.0?
|
|
|
|
|
Hey Paolo, Aymen here , just want to say how brilliant you are and how amazing of a job you did , very helpful article !
Now the fun part
i don't thing the deserialize code works great with TabControls simply because when you create TabPages and you assign them automatically to the main component which is the Form control it will give you an exeption , as you know TabPages can only be parent to TabControl , so i suggest you add a new function like this :
public Control CreateControl(Type controlType, Size controlSize, Point controlLocation , Control parent)
so that you can assign a parent when the control is created!
Hope this helps!
Keep up the good stuff
|
|
|
|
|
Hi aymenbnr,
would you be so kind to post complete function that will work?
Thank you.
Moma.
|
|
|
|
|
|
Hello Paolo,
thank you for amazing work, you just keep making things better.
While testing saving and loading design I came across some problems.
- Children inside TabContol tabs are not being saved.
- If I have TabControl on the form, and also have GroupBox there are problems
- Sometimes GroupBox don't get saved.
- Sometimes children of GroupBox load outside the GroupBox (on the main Form).
Question remains what will happened if there is TabContol with children inside another TabContol, and even more than one in hierarchy tree?
Since you made this and did us all a favor, I would like to reciprocate.
If you need any help in my fields of work, I will be glad to help.
My fields of work:
- Maya generalist.
- Game developer - Unity.
- Visual effects artist.
Regards,
Momcilo Mitrovic
|
|
|
|
|
Hi, thanks for the vote
TabControl is a very special case of control. I left out this control to let the article as easy as possible. To let the Tabcontrol operate in a correct way during designtime you need something like this (Note the 'TabControlExtDesigner' class):
[DefaultProperty( "TabPages" )]
[DefaultEvent( "SelectedIndexChanged" )]
[ToolboxBitmap( typeof( System.Windows.Forms.TabControl ) ),
Designer( typeof( TabControlExtDesigner ) )]
public class TabControlExt : TabControl {
private const string _Name_ = "TabControlExt";
private System.ComponentModel.Container components = null;
public TabControlExt() : base() {
InitializeComponent();
}
protected override void Dispose( bool disposing ) {
if( disposing ) {
if( components != null ) {
components.Dispose();
}
}
base.Dispose( disposing );
}
public event TabControlExEventHandler SelectedIndexChanging;
public TabPage HotTab = null;
#region Component Designer generated code
private void InitializeComponent() {
components = new System.ComponentModel.Container();
}
#endregion
[Editor( typeof( TabpageExCollectionEditor ), typeof( UITypeEditor ) )]
public new TabPageCollection TabPages {
get {
return base.TabPages;
}
}
#region TabpageExCollectionEditor
internal class TabpageExCollectionEditor : CollectionEditor {
public TabpageExCollectionEditor( Type type )
: base( type ) {
}
protected override Type CreateCollectionItemType() {
return typeof( TabPageExt );
}
}
#endregion
#region Interop for SelectedIndexChanging event
[StructLayout( LayoutKind.Sequential )]
private struct NMHDR {
public IntPtr HWND;
public uint idFrom;
public int code;
public override String ToString() {
return String.Format( "Hwnd: {0}, ControlID: {1}, Code: {2}", HWND, idFrom, code );
}
}
private const int TCN_FIRST = 0 - 550;
private const int TCN_SELCHANGING = (TCN_FIRST - 2);
private const int WM_USER = 0x400;
private const int WM_NOTIFY = 0x4E;
private const int WM_REFLECT = WM_USER + 0x1C00;
#endregion
#region SelectedIndexChanging event Implementation
protected override void WndProc( ref Message m ) {
if( m.Msg == (WM_REFLECT + WM_NOTIFY) ) {
NMHDR hdr = (NMHDR) (Marshal.PtrToStructure( m.LParam, typeof( NMHDR ) ));
if( hdr.code == TCN_SELCHANGING ) {
if( HotTab != null ) {
TabControlExEventArgs e = new TabControlExEventArgs( HotTab, Controls.IndexOf( HotTab ) );
if( SelectedIndexChanging != null )
SelectedIndexChanging( this, e );
if( e.Cancel || !HotTab.Enabled ) {
m.Result = new IntPtr( 1 );
return;
}
}
}
}
base.WndProc( ref m );
}
#endregion
#region HotTab Immplementation
protected override void OnMouseMove( MouseEventArgs e ) {
base.OnMouseMove( e );
HotTab = TestTab( new Point( e.X, e.Y ) );
}
#endregion
#region Custom Methods
public void InsertTabPage( TabPage tabpage, int index ) {
if( index < 0 || index > TabCount )
throw new ArgumentException( "Index out of Range." );
TabPages.Add( tabpage );
if( index < TabCount - 1 ) {
do
SwapTabPages( tabpage, (TabPages[TabPages.IndexOf( tabpage ) - 1]) );
while( TabPages.IndexOf( tabpage ) != index );
}
SelectedTab = tabpage;
}
public void SwapTabPages( TabPage tp1, TabPage tp2 ) {
if( !TabPages.Contains( tp1 ) || !TabPages.Contains( tp2 ) )
throw new ArgumentException( "TabPages must be in the TabCotrols TabPageCollection." );
int Index1 = TabPages.IndexOf( tp1 );
int Index2 = TabPages.IndexOf( tp2 );
TabPages[Index1] = tp2;
TabPages[Index2] = tp1;
}
private TabPage TestTab( Point pt ) {
for( int index = 0; index <= TabCount - 1; index++ ) {
if( GetTabRect( index ).Contains( pt.X, pt.Y ) )
return TabPages[index];
}
return null;
}
#endregion
}
public class TabControlExtDesigner : ParentControlDesigner {
#region Private Instance Variables
private DesignerVerbCollection m_verbs = new DesignerVerbCollection();
private IDesignerHost m_DesignerHost;
private ISelectionService m_SelectionService;
#endregion
public TabControlExtDesigner() : base() {
DesignerVerb verb1 = new DesignerVerb( "Add Tab", new EventHandler( OnAddPage ) );
DesignerVerb verb2 = new DesignerVerb( "Insert Tab", new EventHandler( OnInsertPage ) );
DesignerVerb verb3 = new DesignerVerb( "Remove Tab", new EventHandler( OnRemovePage ) );
m_verbs.AddRange( new DesignerVerb[] { verb1, verb2, verb3 } );
}
#region Properties
public override DesignerVerbCollection Verbs {
get {
if( m_verbs.Count == 3 ) {
Platone.TabControlExt MyControl = (Platone.TabControlExt) Control;
if( MyControl.TabCount > 0 ) {
m_verbs[1].Enabled = true;
m_verbs[2].Enabled = true;
}
else {
m_verbs[1].Enabled = false;
m_verbs[2].Enabled = false;
}
}
return m_verbs;
}
}
public IDesignerHost DesignerHost {
get {
if( m_DesignerHost == null )
m_DesignerHost = (IDesignerHost) (GetService( typeof( IDesignerHost ) ));
return m_DesignerHost;
}
}
public ISelectionService SelectionService {
get {
if( m_SelectionService == null )
m_SelectionService = (ISelectionService) (this.GetService( typeof( ISelectionService ) ));
return m_SelectionService;
}
}
#endregion
void OnAddPage( Object sender, EventArgs e ) {
TabControlExt ParentControl = (TabControlExt) Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
RaiseComponentChanging( TypeDescriptor.GetProperties( ParentControl )["TabPages"] );
TabPageExt P = (TabPageExt) (DesignerHost.CreateComponent( typeof( TabPageExt ) ));
P.Text = P.Name;
ParentControl.TabPages.Add( P );
RaiseComponentChanged( TypeDescriptor.GetProperties( ParentControl )["TabPages"], oldTabs, ParentControl.TabPages );
ParentControl.SelectedTab = P;
SetVerbs();
}
void OnInsertPage( Object sender, EventArgs e ) {
TabControlExt ParentControl = (TabControlExt) Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
int Index = ParentControl.SelectedIndex;
RaiseComponentChanging( TypeDescriptor.GetProperties( ParentControl )["TabPages"] );
TabPageExt P = (TabPageExt) (DesignerHost.CreateComponent( typeof( TabPageExt ) ));
P.Text = P.Name;
TabPageExt[] tpc = new TabPageExt[ParentControl.TabCount];
for( int i = Index; i <= tpc.Length - 1; i++ ) {
tpc[i] = (TabPageExt) ParentControl.TabPages[Index];
ParentControl.TabPages.Remove( ParentControl.TabPages[Index] );
}
ParentControl.TabPages.Add( P );
for( int i = Index; i <= tpc.Length - 1; i++ ) {
ParentControl.TabPages.Add( tpc[i] );
}
RaiseComponentChanged( TypeDescriptor.GetProperties( ParentControl )["TabPages"], oldTabs, ParentControl.TabPages );
ParentControl.SelectedTab = P;
SetVerbs();
}
void OnRemovePage( Object sender, EventArgs e ) {
TabControlExt ParentControl = (TabControlExt) Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
if( ParentControl.SelectedIndex < 0 ) return;
RaiseComponentChanging( TypeDescriptor.GetProperties( ParentControl )["TabPages"] );
DesignerHost.DestroyComponent( ParentControl.TabPages[ParentControl.SelectedIndex] );
RaiseComponentChanged( TypeDescriptor.GetProperties( ParentControl )["TabPages"], oldTabs, ParentControl.TabPages );
SelectionService.SetSelectedComponents( new IComponent[] { ParentControl }, SelectionTypes.Auto );
SetVerbs();
}
private void SetVerbs() {
TabControlExt ParentControl = (TabControlExt) Control;
switch( ParentControl.TabPages.Count ) {
case 0:
Verbs[1].Enabled = false;
Verbs[2].Enabled = false;
break;
case 1:
Verbs[1].Enabled = false;
Verbs[2].Enabled = true;
break;
default:
Verbs[1].Enabled = true;
Verbs[2].Enabled = true;
break;
}
}
private const int WM_NCHITTEST = 0x84;
private const int HTTRANSPARENT = -1;
private const int HTCLIENT = 1;
protected override void WndProc( ref Message m ) {
base.WndProc( ref m );
if( m.Msg == WM_NCHITTEST ) {
if( m.Result.ToInt32() == HTTRANSPARENT )
m.Result = (IntPtr) HTCLIENT;
}
}
private enum TabControlHitTest {
TCHT_NOWHERE = 1,
TCHT_ONITEMICON = 2,
TCHT_ONITEMLABEL = 4,
TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL
}
private const int TCM_HITTEST = 0x130D;
private struct TCHITTESTINFO {
public Point pt;
public TabControlHitTest flags;
}
protected override bool GetHitTest( Point point ) {
if( this.SelectionService.PrimarySelection == this.Control ) {
TCHITTESTINFO hti = new TCHITTESTINFO();
hti.pt = this.Control.PointToClient( point );
hti.flags = 0;
Message m = new Message();
m.HWnd = this.Control.Handle;
m.Msg = TCM_HITTEST;
IntPtr lparam = Marshal.AllocHGlobal( Marshal.SizeOf( hti ) );
Marshal.StructureToPtr( hti, lparam, false );
m.LParam = lparam;
base.WndProc( ref m );
Marshal.FreeHGlobal( lparam );
if( m.Result.ToInt32() != -1 )
return hti.flags != TabControlHitTest.TCHT_NOWHERE;
}
return false;
}
protected override void OnPaintAdornments( PaintEventArgs pe ) {
}
public override System.Windows.Forms.Design.SelectionRules SelectionRules {
get {
if( Control.Dock == DockStyle.Fill )
return System.Windows.Forms.Design.SelectionRules.Visible;
return base.SelectionRules;
}
}
}
[Designer( typeof( PTabPageDesigner ) )]
[ToolboxItemAttribute( false )]
[DesignTimeVisibleAttribute( true )]
public class TabPageExtBase : TabPage {
#region API Declares
[DllImport( "Comctl32.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern int DllGetVersion( ref DLLVERSIONINFO pdvi );
[DllImport( "uxtheme.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern bool IsAppThemed();
[DllImport( "uxtheme.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode )]
private static extern IntPtr OpenThemeData( IntPtr hwnd, String pszClassList );
[DllImport( "uxtheme.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern int GetThemePartSize( IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, ref Rectangle prc, THEMESIZE eSize, ref Size psz );
[DllImport( "uxtheme.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern int DrawThemeBackground( IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, ref Rectangle pRect, IntPtr pClipRect );
[DllImport( "uxtheme.dll", CallingConvention = CallingConvention.Cdecl )]
private static extern int CloseThemeData( IntPtr htheme );
private struct DLLVERSIONINFO {
public int cbSize;
public int dwMajorVersion;
public int dwMinorVersion;
public int dwBuildNumber;
public int dwPlatformID;
public DLLVERSIONINFO( Control ctrl ) {
cbSize = Marshal.SizeOf( typeof( DLLVERSIONINFO ) );
dwMajorVersion = 0;
dwMinorVersion = 0;
dwBuildNumber = 0;
dwPlatformID = 0;
}
}
private enum THEMESIZE {
TS_MIN,
TS_TRUE,
TS_DRAW
}
private const int TABP_BODY = 10;
private const int WM_THEMECHANGED = 0x31A;
#endregion
#region Properties
private bool bStyled = true;
private Brush m_Brush;
private bool AppIsXPThemed {
get {
DLLVERSIONINFO dllVer = new DLLVERSIONINFO( this );
DllGetVersion( ref dllVer );
if( dllVer.dwMajorVersion >= 6 ) return IsAppThemed();
return false;
}
}
[Category( "Appearance" )]
[Description( "Enables/Disables Visual Styles on the TabPage. Valid only in WidowsXP." )]
[DefaultValue( true )]
public bool EnableVisualStyles {
get {
return bStyled;
}
set {
if( bStyled == value ) return;
bStyled = value;
Invalidate( true );
}
}
#endregion
private System.ComponentModel.Container components = null;
public TabPageExtBase() {
InitializeComponent();
}
public TabPageExtBase( String Text )
: base() {
base.Text = Text;
}
protected override void Dispose( bool disposing ) {
if( disposing ) {
if( components != null ) {
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
private void InitializeComponent() {
components = new System.ComponentModel.Container();
this.Disposed += new EventHandler( TabpageEx_Disposed );
}
#endregion
protected override void OnPaintBackground( PaintEventArgs pevent ) {
if( EnableVisualStyles && AppIsXPThemed ) {
if( m_Brush == null ) SetTabBrush();
pevent.Graphics.FillRectangle( m_Brush, ClientRectangle );
}
else
base.OnPaintBackground( pevent );
}
private void SetTabBrush() {
IntPtr hdc;
IntPtr hTheme;
Size sz = new Size( 0, 0 );
Bitmap bmp;
int h = Height;
hTheme = OpenThemeData( Handle, "TAB" );
Rectangle displayrect = DisplayRectangle;
GetThemePartSize( hTheme, IntPtr.Zero, TABP_BODY, 0, ref displayrect, THEMESIZE.TS_TRUE, ref sz );
if( h > sz.Height ) sz.Height = h;
bmp = new Bitmap( sz.Width, sz.Height );
Graphics g = Graphics.FromImage( bmp );
hdc = g.GetHdc();
Rectangle bmpRect = new Rectangle( 0, 0, sz.Width, sz.Height );
DrawThemeBackground( hTheme, hdc, TABP_BODY, 0, ref bmpRect, IntPtr.Zero );
g.ReleaseHdc( hdc );
CloseThemeData( hTheme );
m_Brush = new TextureBrush( bmp );
bmp.Dispose();
g.Dispose();
}
private void TabpageEx_Disposed( Object sender, System.EventArgs e ) {
if( m_Brush != null ) m_Brush.Dispose();
}
protected override void OnResize( EventArgs e ) {
base.OnResize( e );
if( AppIsXPThemed ) SetTabBrush();
}
protected override void WndProc( ref Message m ) {
base.WndProc( ref m );
if( m.Msg == WM_THEMECHANGED )
SetTabBrush();
}
}
Paolo
|
|
|
|
|
Hello Paolo,
thank you for replay
I tried to make use of your code, but since I am not an expert in Windows Forms, I didn't manage to create valid TabControlExt control.
I stumbled on following areas:
1.
in public class TabControlExt : TabControl
public event TabControlExEventHandler SelectedIndexChanging;
...
TabControlExEventArgs e = new TabControlExEventArgs(HotTab, Controls.IndexOf(HotTab));
...
2.
in internal class TabpageExCollectionEditor : CollectionEditor
{
public TabpageExCollectionEditor(Type type)
: base(type)
{
}
protected override Type CreateCollectionItemType()
{
return typeof(TabPageExt);
}
}
...
in public class TabControlExtDesigner : ParentControlDesigner
void OnAddPage(Object sender, EventArgs e)
{
TabControlExt ParentControl = (TabControlExt)Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
RaiseComponentChanging(TypeDescriptor.GetProperties(ParentControl)["TabPages"]);
TabPageExt P = (TabPageExt)(DesignerHost.CreateComponent(typeof(TabPageExt)));
...
in void OnInsertPage(Object sender, EventArgs e)
{
TabControlExt ParentControl = (TabControlExt)Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
int Index = ParentControl.SelectedIndex;
RaiseComponentChanging(TypeDescriptor.GetProperties(ParentControl)["TabPages"]);
TabPageExt P = (TabPageExt)(DesignerHost.CreateComponent(typeof(TabPageExt)));
P.Text = P.Name;
TabPageExt[] tpc = new TabPageExt[ParentControl.TabCount];
//Starting at our Insert Position, store and remove all the tabpages.
for (int i = Index; i <= tpc.Length - 1; i++)
{
tpc[i] = (TabPageExt)ParentControl.TabPages[Index];
ParentControl.TabPages.Remove(ParentControl.TabPages[Index]);
...
I tried to extend TabPageExt from TabPageBase, but not sure if this is valid.
3.
in [Designer(typeof(PTabPageDesigner))]
[ToolboxItemAttribute(false)]
[DesignTimeVisibleAttribute(true)]
public class TabPageExtBase : TabPage
Not sure what should PTabPageDesigner look like.
I would appreciate your help.
I understand that TabControl's Tabs are their children, and the whole problem comes from it.
I also thought that there could be some simpler solution, like checking if Tabs has children, and if so,
iterate through all of them, treating Tabs as MainForm.
Sorry for the inconvenience.
Regadrs,
Moma.
|
|
|
|
|
Hello Paolo,
I have managed to find TabControl, similar to code that you proposed.
Unfortunately Tabs and their content does not load.
Only difference is in TabPageEx.
Yours is using [Designer(typeof(PTabPageDesigner))] , and this one is
using [Designer(typeof(System.Windows.Forms.Design.ScrollableControlDesigner))] .
Would you be so kind to shed some light on this problem.
Here are the TabControl classes:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Design;
using System.ComponentModel.Design;
using System.Runtime.InteropServices;
namespace Dotnetrix_Samples
{
#region TabControlEx Class
[DefaultProperty("TabPages")]
[DefaultEvent("SelectedIndexChanged")]
[ToolboxBitmap(typeof(System.Windows.Forms.TabControl)),
Designer(typeof(Designers.TabControlExDesigner))]
public class TabControlEx : System.Windows.Forms.TabControl
{
private System.ComponentModel.Container components = null;
public TabControlEx() : base()
{
InitializeComponent();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
public event TabControlExEventHandler SelectedIndexChanging;
public TabPage HotTab = null;
#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
#region Properties
[Editor(typeof(TabpageExCollectionEditor), typeof(UITypeEditor))]
public new TabPageCollection TabPages
{
get
{
return base.TabPages;
}
}
#endregion
#region TabpageExCollectionEditor
internal class TabpageExCollectionEditor : CollectionEditor
{
public TabpageExCollectionEditor(System.Type type) : base(type)
{
}
protected override Type CreateCollectionItemType()
{
return typeof(TabPageEx);
}
}
#endregion
#region Interop for SelectedIndexChanging event
[StructLayout(LayoutKind.Sequential)]
private struct NMHDR
{
public IntPtr HWND;
public uint idFrom;
public int code;
public override String ToString()
{
return String.Format("Hwnd: {0}, ControlID: {1}, Code: {2}", HWND, idFrom, code);
}
}
private const int TCN_FIRST = 0 - 550;
private const int TCN_SELCHANGING = (TCN_FIRST - 2);
private const int WM_USER = 0x400;
private const int WM_NOTIFY = 0x4E;
private const int WM_REFLECT = WM_USER + 0x1C00;
#endregion
#region SelectedIndexChanging event Implementation
protected override void WndProc(ref Message m)
{
if (m.Msg == (WM_REFLECT + WM_NOTIFY))
{
NMHDR hdr = (NMHDR)(Marshal.PtrToStructure(m.LParam, typeof(NMHDR)));
if (hdr.code == TCN_SELCHANGING)
{
if (HotTab != null)
{
TabControlExEventArgs e = new TabControlExEventArgs(HotTab, Controls.IndexOf(HotTab));
if (SelectedIndexChanging != null)
SelectedIndexChanging(this, e);
if (e.Cancel || !HotTab.Enabled)
{
m.Result = new IntPtr(1);
return;
}
}
}
}
base.WndProc(ref m);
}
#endregion
#region HotTab Immplementation
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
HotTab = TestTab(new Point(e.X, e.Y));
}
#endregion
#region Custom Methods
public void InsertTabPage(TabPage tabpage, int index)
{
if (index < 0 || index > TabCount)
throw new ArgumentException("Index out of Range.");
TabPages.Add(tabpage);
if (index < TabCount - 1)
{
do
SwapTabPages(tabpage, (TabPages[TabPages.IndexOf(tabpage) - 1]));
while (TabPages.IndexOf(tabpage) != index);
}
SelectedTab = tabpage;
}
public void SwapTabPages(TabPage tp1, TabPage tp2)
{
if (!TabPages.Contains(tp1) || !TabPages.Contains(tp2))
throw new ArgumentException("TabPages must be in the TabCotrols TabPageCollection.");
int Index1 = TabPages.IndexOf(tp1);
int Index2 = TabPages.IndexOf(tp2);
TabPages[Index1] = tp2;
TabPages[Index2] = tp1;
}
private TabPage TestTab(Point pt)
{
for (int index = 0; index <= TabCount - 1; index++)
{
if (GetTabRect(index).Contains(pt.X, pt.Y))
return TabPages[index];
}
return null;
}
#endregion
}
#region SelectedIndexChanging EventArgs Class/Delegate
public class TabControlExEventArgs : EventArgs
{
private TabPage m_TabPage = null;
private int m_TabPageIndex = -1;
public bool Cancel = false;
public TabPage tabPage
{
get
{
return m_TabPage;
}
}
public int TabPageIndex
{
get
{
return m_TabPageIndex;
}
}
public TabControlExEventArgs(TabPage tabPage, int TabPageIndex)
{
m_TabPage = tabPage;
m_TabPageIndex = TabPageIndex;
}
}
public delegate void TabControlExEventHandler(Object sender, TabControlExEventArgs e);
#endregion
#endregion
#region TabPageEx Class
[Designer(typeof(System.Windows.Forms.Design.ScrollableControlDesigner))]
[ToolboxItemAttribute(false)]
public class TabPageEx : TabPage
{
#region API Declares
[DllImport("Comctl32.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int DllGetVersion(ref DLLVERSIONINFO pdvi);
[DllImport("uxtheme.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern bool IsAppThemed();
[DllImport("uxtheme.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern IntPtr OpenThemeData(IntPtr hwnd, String pszClassList);
[DllImport("uxtheme.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetThemePartSize(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, ref Rectangle prc, THEMESIZE eSize, ref Size psz);
[DllImport("uxtheme.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int DrawThemeBackground(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, ref Rectangle pRect, IntPtr pClipRect);
[DllImport("uxtheme.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int CloseThemeData(IntPtr htheme);
private struct DLLVERSIONINFO
{
public int cbSize;
public int dwMajorVersion;
public int dwMinorVersion;
public int dwBuildNumber;
public int dwPlatformID;
public DLLVERSIONINFO(Control ctrl)
{
cbSize = Marshal.SizeOf(typeof(DLLVERSIONINFO));
dwMajorVersion = 0;
dwMinorVersion = 0;
dwBuildNumber = 0;
dwPlatformID = 0;
}
}
private enum THEMESIZE
{
TS_MIN,
TS_TRUE,
TS_DRAW
}
private const int TABP_BODY = 10;
private const int WM_THEMECHANGED = 0x31A;
#endregion
#region Properties
private bool bStyled = true;
private Brush m_Brush;
private bool AppIsXPThemed
{
get
{
DLLVERSIONINFO dllVer = new DLLVERSIONINFO(this);
DllGetVersion(ref dllVer);
if (dllVer.dwMajorVersion >= 6) return IsAppThemed();
return false;
}
}
[Category("Appearance")]
[Description("Enables/Disables Visual Styles on the TabPage. Valid only in WidowsXP.")]
[DefaultValue(true)]
public bool EnableVisualStyles
{
get
{
return bStyled;
}
set
{
if (bStyled == value) return;
bStyled = value;
Invalidate(true);
}
}
#endregion
private System.ComponentModel.Container components = null;
public TabPageEx()
{
InitializeComponent();
}
public TabPageEx(String Text) : base()
{
base.Text = Text;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#region Component Designer generated code
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
this.Disposed += new EventHandler(TabpageEx_Disposed);
}
#endregion
protected override void OnPaintBackground(PaintEventArgs pevent)
{
if (EnableVisualStyles && AppIsXPThemed)
{
if (m_Brush == null) SetTabBrush();
pevent.Graphics.FillRectangle(m_Brush, ClientRectangle);
}
else
base.OnPaintBackground(pevent);
}
private void SetTabBrush()
{
IntPtr hdc;
IntPtr hTheme;
Size sz = new Size(0, 0);
Bitmap bmp;
int h = Height;
hTheme = OpenThemeData(Handle, "TAB");
Rectangle displayrect = DisplayRectangle;
GetThemePartSize(hTheme, IntPtr.Zero, TABP_BODY, 0, ref displayrect, THEMESIZE.TS_TRUE, ref sz);
if (h > sz.Height) sz.Height = h;
bmp = new Bitmap(sz.Width, sz.Height);
Graphics g = Graphics.FromImage(bmp);
hdc = g.GetHdc();
Rectangle bmpRect = new Rectangle(0, 0, sz.Width, sz.Height);
DrawThemeBackground(hTheme, hdc, TABP_BODY, 0, ref bmpRect, IntPtr.Zero);
g.ReleaseHdc(hdc);
CloseThemeData(hTheme);
m_Brush = new TextureBrush(bmp);
bmp.Dispose();
g.Dispose();
}
private void TabpageEx_Disposed(Object sender, System.EventArgs e)
{
if (m_Brush != null) m_Brush.Dispose();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (AppIsXPThemed) SetTabBrush();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_THEMECHANGED)
SetTabBrush();
}
}
#endregion
}
namespace Designers
{
internal class TabControlExDesigner : System.Windows.Forms.Design.ParentControlDesigner
{
#region Private Instance Variables
private DesignerVerbCollection m_verbs = new DesignerVerbCollection();
private IDesignerHost m_DesignerHost;
private ISelectionService m_SelectionService;
#endregion
public TabControlExDesigner() : base()
{
DesignerVerb verb1 = new DesignerVerb("Add Tab", new EventHandler(OnAddPage));
DesignerVerb verb2 = new DesignerVerb("Insert Tab", new EventHandler(OnInsertPage));
DesignerVerb verb3 = new DesignerVerb("Remove Tab", new EventHandler(OnRemovePage));
m_verbs.AddRange(new DesignerVerb[] { verb1, verb2, verb3 });
}
#region Properties
public override DesignerVerbCollection Verbs
{
get
{
if (m_verbs.Count == 3)
{
Dotnetrix_Samples.TabControlEx MyControl = (Dotnetrix_Samples.TabControlEx)Control;
if (MyControl.TabCount > 0)
{
m_verbs[1].Enabled = true;
m_verbs[2].Enabled = true;
}
else
{
m_verbs[1].Enabled = false;
m_verbs[2].Enabled = false;
}
}
return m_verbs;
}
}
public IDesignerHost DesignerHost
{
get
{
if (m_DesignerHost == null)
m_DesignerHost = (IDesignerHost)(GetService(typeof(IDesignerHost)));
return m_DesignerHost;
}
}
public ISelectionService SelectionService
{
get
{
if (m_SelectionService == null)
m_SelectionService = (ISelectionService)(this.GetService(typeof(ISelectionService)));
return m_SelectionService;
}
}
#endregion
void OnAddPage(Object sender, EventArgs e)
{
Dotnetrix_Samples.TabControlEx ParentControl = (Dotnetrix_Samples.TabControlEx)Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
RaiseComponentChanging(TypeDescriptor.GetProperties(ParentControl)["TabPages"]);
Dotnetrix_Samples.TabPageEx P = (Dotnetrix_Samples.TabPageEx)(DesignerHost.CreateComponent(typeof(Dotnetrix_Samples.TabPageEx)));
P.Text = P.Name;
ParentControl.TabPages.Add(P);
RaiseComponentChanged(TypeDescriptor.GetProperties(ParentControl)["TabPages"], oldTabs, ParentControl.TabPages);
ParentControl.SelectedTab = P;
SetVerbs();
}
void OnInsertPage(Object sender, EventArgs e)
{
Dotnetrix_Samples.TabControlEx ParentControl = (Dotnetrix_Samples.TabControlEx)Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
int Index = ParentControl.SelectedIndex;
RaiseComponentChanging(TypeDescriptor.GetProperties(ParentControl)["TabPages"]);
Dotnetrix_Samples.TabPageEx P = (Dotnetrix_Samples.TabPageEx)(DesignerHost.CreateComponent(typeof(Dotnetrix_Samples.TabPageEx)));
P.Text = P.Name;
Dotnetrix_Samples.TabPageEx[] tpc = new Dotnetrix_Samples.TabPageEx[ParentControl.TabCount];
for (int i = Index; i <= tpc.Length - 1; i++)
{
tpc[i] = (Dotnetrix_Samples.TabPageEx)ParentControl.TabPages[Index];
ParentControl.TabPages.Remove(ParentControl.TabPages[Index]);
}
ParentControl.TabPages.Add(P);
for (int i = Index; i <= tpc.Length - 1; i++)
{
ParentControl.TabPages.Add(tpc[i]);
}
RaiseComponentChanged(TypeDescriptor.GetProperties(ParentControl)["TabPages"], oldTabs, ParentControl.TabPages);
ParentControl.SelectedTab = P;
SetVerbs();
}
void OnRemovePage(Object sender, EventArgs e)
{
Dotnetrix_Samples.TabControlEx ParentControl = (Dotnetrix_Samples.TabControlEx)Control;
Control.ControlCollection oldTabs = ParentControl.Controls;
if (ParentControl.SelectedIndex < 0) return;
RaiseComponentChanging(TypeDescriptor.GetProperties(ParentControl)["TabPages"]);
DesignerHost.DestroyComponent(ParentControl.TabPages[ParentControl.SelectedIndex]);
RaiseComponentChanged(TypeDescriptor.GetProperties(ParentControl)["TabPages"], oldTabs, ParentControl.TabPages);
SelectionService.SetSelectedComponents(new IComponent[] { ParentControl }, SelectionTypes.Auto);
SetVerbs();
}
private void SetVerbs()
{
Dotnetrix_Samples.TabControlEx ParentControl = (Dotnetrix_Samples.TabControlEx)Control;
switch (ParentControl.TabPages.Count)
{
case 0:
Verbs[1].Enabled = false;
Verbs[2].Enabled = false;
break;
case 1:
Verbs[1].Enabled = false;
Verbs[2].Enabled = true;
break;
default:
Verbs[1].Enabled = true;
Verbs[2].Enabled = true;
break;
}
}
private const int WM_NCHITTEST = 0x84;
private const int HTTRANSPARENT = -1;
private const int HTCLIENT = 1;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_NCHITTEST)
{
if (m.Result.ToInt32() == HTTRANSPARENT)
m.Result = (IntPtr)HTCLIENT;
}
}
private enum TabControlHitTest
{
TCHT_NOWHERE = 1,
TCHT_ONITEMICON = 2,
TCHT_ONITEMLABEL = 4,
TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL
}
private const int TCM_HITTEST = 0x130D;
private struct TCHITTESTINFO
{
public Point pt;
public TabControlHitTest flags;
}
protected override bool GetHitTest(Point point)
{
if (this.SelectionService.PrimarySelection == this.Control)
{
TCHITTESTINFO hti = new TCHITTESTINFO();
hti.pt = this.Control.PointToClient(point);
hti.flags = 0;
Message m = new Message();
m.HWnd = this.Control.Handle;
m.Msg = TCM_HITTEST;
IntPtr lparam = Marshal.AllocHGlobal(Marshal.SizeOf(hti));
Marshal.StructureToPtr(hti, lparam, false);
m.LParam = lparam;
base.WndProc(ref m);
Marshal.FreeHGlobal(lparam);
if (m.Result.ToInt32() != -1)
return hti.flags != TabControlHitTest.TCHT_NOWHERE;
}
return false;
}
protected override void OnPaintAdornments(PaintEventArgs pe)
{
}
public override System.Windows.Forms.Design.SelectionRules SelectionRules
{
get
{
if (Control.Dock == DockStyle.Fill)
return System.Windows.Forms.Design.SelectionRules.Visible;
return base.SelectionRules;
}
}
}
}
And this is how XML look like with two Tabs and one checkBox in each Tab:
<Form AllowDrop="True" BackColor="Control" ClientSize="392, 293" Location="15, 15" Name="Form_0" Text="Form_0 (design mode)" xmlns="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" xmlns:d="clr-namespace:Dotnetrix_Samples;assembly=DemoConsoleForpDesigner" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Form.Controls>
<d:TabControlEx AllowDrop="True" Location="63, 46" Name="TabControlEx1" SelectedIndex="1" Size="272, 189" TabIndex="0">
<d:TabControlEx.Controls>
<x:Reference>__ReferenceID0</x:Reference>
<x:Reference>__ReferenceID1</x:Reference>
</d:TabControlEx.Controls>
<d:TabControlEx.TabPages>
<d:TabPageEx x:Name="__ReferenceID0" AllowDrop="True" Enabled="True" Location="4, 22" Name="TabPageEx1" Size="264, 163" TabIndex="0" TabStop="False" Text="TabPageEx1" Visible="False">
<d:TabPageEx.Controls>
<CheckBox Location="61, 58" Name="CheckBox1" TabIndex="0" Text="CheckBox1" UseCompatibleTextRendering="True" UseVisualStyleBackColor="True" />
</d:TabPageEx.Controls>
</d:TabPageEx>
<d:TabPageEx x:Name="__ReferenceID1" AllowDrop="True" Enabled="True" Location="4, 22" Name="TabPageEx2" Size="264, 163" TabIndex="1" TabStop="False" Text="TabPageEx2" Visible="True">
<d:TabPageEx.Controls>
<CheckBox Location="96, 64" Name="CheckBox2" TabIndex="0" Text="CheckBox2" UseCompatibleTextRendering="True" UseVisualStyleBackColor="True" />
</d:TabPageEx.Controls>
</d:TabPageEx>
</d:TabControlEx.TabPages>
</d:TabControlEx>
</Form.Controls>
</Form>
I have added necessary properties in allowedProperties in DeployControlsOnDesignSurface method, but still no luck .
Thank you in advance,
Moma.
modified 27-Mar-17 11:39am.
|
|
|
|
|
|