Click here to Skip to main content
15,887,027 members
Articles / Programming Languages / C#

A .NET Wizard control

Rate me:
Please Sign up or sign in to vote.
4.86/5 (89 votes)
24 Apr 2003CPOL7 min read 661.3K   8.7K   216  
A .NET Wizard control for the VS.IDE and client apps
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ComponentModel;

using UtilityLibrary.Collections;
using UtilityLibrary.Win32;
using UtilityLibrary.General;

namespace UtilityLibrary.CommandBars
{
  // Just to make this class work without the actual
  // designer which is not being added to this verson of the UtilityLibrary
  public class ReBarDesigner
  {
    public void PassMsg(ref Message message)
    {
    }

  }
  
  /// <summary>
  /// Summary description for Rebar.
  /// </summary>
  [ToolboxBitmap(typeof(UtilityLibrary.CommandBars.ReBar), 
     "UtilityLibrary.CommandBars.ReBar.bmp")]
  public class ReBar : Control, IMessageFilter
  {
    #region Class Variables
    RebarBandCollection bands = null;
    static bool needsColorUpdate = false;
    bool bGotIsCommonCtrl6 = false;
    bool isCommonCtrl6 = false;
    const int MINIMUM_HEIGHT = 30;

    // Designer Support
    ToolBarEx placeHolderToolBar = null;
    bool placeHolderAdded = false;
    ReBarDesigner rebarDesigner = null;
    bool addPlaceHolderToolBar = false;
    bool designerInTransaction = false;

    #endregion

    #region Constructors
    public ReBar()
    {
      SetStyle(ControlStyles.UserPaint, false);
      TabStop = false;
      Dock = DockStyle.Top;
      bands = new RebarBandCollection(this);
      bands.Changed += new EventHandler(Bands_Changed);
      addPlaceHolderToolBar = true;
    }
    #endregion

    #region Overrides
    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);
      if ( Capture )
        return;

      bool hit = HitGripper(e);
      if ( hit )
      {
        if ( ShowMoveCursor(e) )
          Cursor.Current = Cursors.SizeAll;
        else
          Cursor.Current = Cursors.SizeWE;
      }
      else
        Cursor.Current = Cursors.Default;
    }
    protected override void OnMouseDown(MouseEventArgs e)
    {
      base.OnMouseDown(e);
      bool hit = HitGripper(e);
      if ( hit ) 
      {
        Capture = true;
        if ( ShowMoveCursor(e) )
          Cursor.Current = Cursors.SizeAll;
        else
          Cursor.Current = Cursors.VSplit;
      }
      else
        Cursor.Current = Cursors.Default;

    }

    protected override void OnMouseUp(MouseEventArgs e)
    {
      base.OnMouseUp(e);
      bool hit = HitGripper(e);
      Capture = false;
      if ( hit )
      {
        if ( ShowMoveCursor(e) )
          Cursor.Current = Cursors.SizeAll;
        else
          Cursor.Current = Cursors.SizeWE;
      }
      else
        Cursor.Current = Cursors.Default;
      
    }

    protected override void Dispose(bool disposing)
    {
      if ( disposing )
        bands.Changed -= new EventHandler(Bands_Changed);
    }
    protected override void OnHandleCreated(EventArgs e)
    {
      base.OnHandleCreated(e);
      RealizeBands();
    }
    
    protected override void WndProc(ref Message m) 
    {
      base.WndProc(ref m);

      switch( m.Msg )
      {
        case (int)Msg.WM_PAINT:
          PaintBackground();
          break;
        
        case (int)Msg.WM_NOTIFY:
        case (int)((int)Msg.WM_NOTIFY + (int)Msg.WM_REFLECT):
        {
          NMHDR note = (NMHDR)m.GetLParam(typeof(NMHDR));
          switch (note.code)
          {
            case (int)RebarNotifications.RBN_HEIGHTCHANGE:
              UpdateSize();
              break;

            case (int)RebarNotifications.RBN_CHEVRONPUSHED:
              NotifyChevronPushed(ref m);
              break;
            
            case (int)RebarNotifications.RBN_CHILDSIZE:
              NotifyChildSize( ref m );
              break;
            
            case (int)NotificationMessages.NM_NCHITTEST:
              break;
          }
        }
          break;
      }
    }
    protected override void OnParentChanged(EventArgs e)
    {
      if (Parent != null)
        Application.AddMessageFilter(this);
      else
        Application.RemoveMessageFilter(this);  
    }

    protected override Size DefaultSize
    {
      get 
      { 
        return new Size(100, 44); 
      }
    }

    protected override void CreateHandle() 
    {
      if (!RecreatingHandle)
      {
        INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX();
        icex.dwSize = Marshal.SizeOf(typeof(INITCOMMONCONTROLSEX));
        icex.dwICC = (int)(CommonControlInitFlags.ICC_BAR_CLASSES | CommonControlInitFlags.ICC_COOL_CLASSES);
        bool  fail = WindowsAPI.InitCommonControlsEx(icex);
      }
      base.CreateHandle();

    }
  
    protected override CreateParams CreateParams
    {
      get
      {
        CreateParams createParams = base.CreateParams;
        createParams.ClassName = WindowsAPI.REBARCLASSNAME;
        createParams.Style = (int)(WindowStyles.WS_CHILD | WindowStyles.WS_VISIBLE
          | WindowStyles.WS_CLIPCHILDREN | WindowStyles.WS_CLIPSIBLINGS);
        createParams.Style |= (int)(CommonControlStyles.CCS_NODIVIDER | CommonControlStyles.CCS_NOPARENTALIGN);
        
        createParams.Style |= (int)(RebarStyles.RBS_VARHEIGHT | RebarStyles.RBS_AUTOSIZE);
        return createParams;
      }
    }
    
    public override bool PreProcessMessage(ref Message msg)
    {
      foreach (Control band in bands)
      {
        if (band.PreProcessMessage(ref msg))
          return true;
      }
      return false;
    }
    #endregion

    #region Properties
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public RebarBandCollection Bands
    {
      get { return bands; }
    }

    internal bool PlaceHolderAdded
    {
      get { return placeHolderAdded; }
    }

    internal bool AddPlaceHolderToolBar
    {
      set { addPlaceHolderToolBar = value; }
      get { return addPlaceHolderToolBar; }
    }

    internal ToolBarEx PlaceHolderToolBar
    {
      get { return placeHolderToolBar; }
    }

    internal ReBarDesigner RebarDesigner
    {
      set { rebarDesigner = value; }
      get { return rebarDesigner; }
    }

    internal bool DesignerInTransaction
    {
      set { designerInTransaction = value; }
      get { return designerInTransaction; }
    }

    #endregion

    #region Methods
    public void UpdateBackgroundColor()
    {
      for ( int i = 0; i < bands.Count; i++ )
      {
        // Update Rebar band information
        // This make sure that the background color and foreground color
        // of the bands are set to the new system colors
        UpdateBand(i);
        ToolBarEx toolBar = (ToolBarEx)bands[i];
        toolBar.Invalidate();
      }
      Invalidate();
    }

    static public void UpdateBandsColors(object sender, EventArgs e)
    {
      needsColorUpdate = true;
    }
  
    public void BeginUpdate()
    {
      WindowsAPI.SendMessage(Handle, (int)Msg.WM_SETREDRAW, 0, 0);
    }

    public void EndUpdate()
    {
      WindowsAPI.SendMessage(Handle, (int)Msg.WM_SETREDRAW, 1, 0);
    }

    internal void UpdateBands()
    {
      if (IsHandleCreated) 
      {
        RecreateHandle();
      }
    }

    public override void Refresh()
    {
      base.Refresh();

      foreach( ToolBarEx tool in Bands )
      {
        tool.UpdateToolBarItems();
      }
    
    }
    #endregion

    #region Implementation
    private bool IsCommonCtrl6()
    {
      // Cache this value for efficenty
      if ( bGotIsCommonCtrl6 == false )
      {     
        DLLVERSIONINFO dllVersion = new DLLVERSIONINFO();
        // We are assummng here that anything greater or equal than 6
        // will have the new XP theme drawing enable
        dllVersion.cbSize = Marshal.SizeOf(typeof(DLLVERSIONINFO));
        WindowsAPI.GetCommonControlDLLVersion(ref dllVersion);
        bGotIsCommonCtrl6 = true;
        isCommonCtrl6 = (dllVersion.dwMajorVersion >= 6);
      }
      return isCommonCtrl6;
    }

    internal void PassMsg(ref Message m)
    {
      WndProc(ref m);
    }

    internal void AddPlaceHolder()
    {
      placeHolderAdded = true;

      // Add place holder toolBar for designer support
      if ( placeHolderToolBar == null )
      {
        placeHolderToolBar = new ToolBarEx();
        placeHolderToolBar.m_parent = this;
      }
            
      REBARBANDINFO bandInfo = GetBandInfo(0, placeHolderToolBar);
      int result = WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_INSERTBANDW, 0, ref bandInfo);
    }

    internal void RemovePlaceHolder()
    {
      placeHolderAdded = false;
      REBARBANDINFO bandInfo = GetBandInfo(0, placeHolderToolBar);
      int result = WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_DELETEBAND, 0, 0);
    }

    internal void RemoveBand(int index)
    {
      // index is base on the Band collection
      // but we need the actual index used in the native Rebar control
      int actualIndex = GetBandActualIndex(index);
      int result = WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_DELETEBAND, actualIndex, 0);
    }

    private void PaintBackground()
    {
      // This is for the very first time the ToolBarEx place holder is added
      if ( bands.Count == 0  &&  addPlaceHolderToolBar == true)
      {
        addPlaceHolderToolBar = false;
        AddPlaceHolder();
      }
            
      if ( needsColorUpdate)
      {
        needsColorUpdate = false;
        for ( int i = 0; i < bands.Count; i++ )
        {
          // Update toolbar specific information
          // This update is to guarantee that the toolbar can resize
          // the buttons appropiately in case the SystemMenuFont was
          // changed along with the system colors
          ToolBarEx toolBar = (ToolBarEx)bands[i];
          toolBar.UpdateToolBarItems();
        }
        for ( int i = 0; i < bands.Count; i++ )
        {
          // Update Rebar band information
          // This make sure that the background color and foreground color
          // of the bands are set to the new system colors
          UpdateBand(i);
        }
      }

      // We don't need to paint the gripper if we are going
      // to let the operating system do the painting
      if ( IsCommonCtrl6() )
        return;
            
      Control c = null;
      Rectangle rc;
      for ( int i = 0; i < bands.Count; i++ )
      {
        Graphics g = CreateGraphics();
        c = bands[i];
        RectangleF rf = g.ClipBounds;
        rc = c.Bounds;
        if ( rf.Contains(rc) )
        {
          ToolBarEx toolBar = ( ToolBarEx )bands[i];
          if ( toolBar.BarType == TBarType.MenuBar ) 
          {
            // The menu bar height is smaller that the other toolbars
            // and if the menubar is in the same row with a toolbar that is bigger in height
            // the toolbar gripper will not be painted correctly if we use the actual height
            // of the menubar. Instead ajust the rectangle to compensate for the actual height
            // of the band
            Rectangle menuRect = GetBandRect(i);
            int offset = (menuRect.Height - rc.Height)/2-1;
            rc = new Rectangle(rc.Left, rc.Top-offset, menuRect.Width, menuRect.Height-2);
          }
          DrawGripper(g, rc);
        }
        g.Dispose();
      }
    }

    private void DrawGripper(Graphics g, Rectangle bounds)
    {
      bounds.X = bounds.Left - 7;
      bounds.Width = 7;

      g.FillRectangle( ColorUtil.VSNetControlBrush, bounds );
      int nHeight = bounds.Height;
      for ( int i = 2; i < nHeight-1; i++) 
      {
        if ( ColorUtil.UsingCustomColor )
          g.DrawLine( ColorUtil.VSNetBorderPen, bounds.Left, bounds.Top+i, bounds.Left+3, bounds.Top+i );
        else 
          g.DrawLine( SystemPens.ControlDark, bounds.Left, bounds.Top+i, bounds.Left+3, bounds.Top+i );
        i++;
      }
    }

    protected bool HitGripper(MouseEventArgs e)
    {
      // Find out if we hit the gripper
      Point mousePoint = new Point(e.X, e.Y);
      Control c = null;
      Rectangle bounds;
      for ( int i = 0; i < bands.Count; i++ )
      {
        c = bands[i];
        bounds = c.Bounds;
        
        // adjust to gripper area
        bounds.X = bounds.Left - 7;
        bounds.Width = 7;
        
        if ( bounds.Contains(mousePoint) )
          return true;
      }
      return false;

    }

    private bool ShowMoveCursor(MouseEventArgs e)
    {
      // Even though we can actually move the toolbars around always
      // sometimes it is more intuive to show the "Move" cursor depending
      // how many bars are in the same row that always showing the resize cursor
      Point mousePoint = new Point(e.X, e.Y);
      Control c = null;
      Rectangle bounds;
      for ( int i = 0; i < bands.Count; i++ )
      {
        c = bands[i];
        bounds = c.Bounds;
        
        // adjust to gripper area
        bounds.X = bounds.Left - 7;
        bounds.Width = 7;
        
        if ( bounds.Contains(mousePoint) )
        {
          if ( bounds.Left <= 5 )
          {   // The left value would be actually at least 2 if the toolbar
            // is on the edge of the main window as opossed to be somewhere in the middle of the
            // strip. The assumption here is that the gripper starts approximately 2 pixel from the edge
            return true;
          }
        }
      }
      
      return false;
    }
        
    private void NotifyChevronPushed(ref Message m)
    {
      NMREBARCHEVRON nrch = (NMREBARCHEVRON) m.GetLParam(typeof(NMREBARCHEVRON)); 
      REBARBANDINFO rb;
      int bandIndex = nrch.uBand;
      rb = GetRebarInfo(bandIndex);
      int actualIndex = rb.wID;
      Control band = (Control)bands[actualIndex];
      Point point = new Point(nrch.rc.left, nrch.rc.bottom);
      (band as IChevron).Show(this, point);
      
    }

    private void NotifyChildSize( ref Message m )
    {
      NMREBARCHILDSIZE size = ( NMREBARCHILDSIZE )m.GetLParam( typeof( NMREBARCHILDSIZE ) );

      if( size.uBand >= 0 && size.uBand < bands.Count )
      {
        ToolBarEx toolBar = ( ToolBarEx )bands[ (int)size.uBand ];
        toolBar.ToolbarSizeChanged( size );
      }
      /*
      for( int i = 0; i < bands.Count; i++ )
      {
        // Update toolbar specific information
        // This update is to guarantee that the toolbar can resize
        // the buttons appropiately in case the SystemMenuFont was
        // changed along with the system colors
        ToolBarEx toolBar = ( ToolBarEx )bands[i];
        toolBar.ToolbarSizeChanged( size );
      }
      */
    }

    bool IMessageFilter.PreFilterMessage(ref Message message)
    {
      ArrayList handles = new ArrayList();
      IntPtr handle = Handle;
      while (handle != IntPtr.Zero)
      {
        handles.Add(handle);
        handle = WindowsAPI.GetParent(handle);  
      }

      handle = message.HWnd;
      while (handle != IntPtr.Zero)
      {
        Msg currentMessage = (Msg)message.Msg;
        if (handles.Contains(handle)) 
          return PreProcessMessage(ref message);
        handle = WindowsAPI.GetParent(handle);
      }
      
      return false;
    }

    private void RealizeBands()
    {
      ReleaseBands();
      BeginUpdate();
    
      for (int i = 0; i < bands.Count; i++)
      {
        REBARBANDINFO bandInfo = GetBandInfo(i, null);
        WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_INSERTBANDW, i, ref bandInfo);
      }

      UpdateSize();
      EndUpdate();
      CaptureBands();

      if ( bands.Count == 0 && addPlaceHolderToolBar )
      {
        if ( designerInTransaction == false )
        {
          // If the designer is engage in a transaction
          // don't add the place holder toolbar here, defer the
          // addition to the PaintBackground routine so that
          // we avoid some painting problems
          addPlaceHolderToolBar = false;
          AddPlaceHolder();
        }
      }

    }

    internal void UpdateBand(int index)
    {
      if (!IsHandleCreated) return;
        
      BeginUpdate();

      // Make sure we get the right index according to the band position in the rebar
      // and not to the index in the toolbar collections which can or cannot match the order
      // in the rebar control
      int actualIndex = GetBandActualIndex(index);

      REBARBANDINFO rbbi = GetBandInfo(actualIndex, null);
      ToolBarEx tb = (ToolBarEx)bands[actualIndex];
      int idealSize = tb.GetIdealSize();
      rbbi.cxIdeal = idealSize;
      WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_SETBANDINFOW, index, ref rbbi);

      UpdateSize();
      EndUpdate();
    }

    private int GetBandActualIndex(int bandIndex)
    {
      // This maps between the indexes in the band collection and the actual
      // indexes in the rebar that can actually change as the user moves
      // the bands around
      REBARBANDINFO rb;
      rb = GetRebarInfo(bandIndex);
      return rb.wID;
    }

    private void UpdateSize()
    {
      /*
      Height = WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_GETBARHEIGHT, 0, 0) + 1;
      if( Height != 1 )
      {
        // Give it some size
        RECT rc = new RECT();
        rc.bottom = Height;
        WindowsAPI.SendMessage( Handle, (int)RebarMessages.RB_SIZETORECT, 0, ref rc );
      }
      */
    }

    public int GetRebarHeight()
    {
      int height = WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_GETBARHEIGHT, 0, 0) + 1;
      return height;      
    }

    public Rectangle GetBandRect(int bandIndex)
    {
      RECT rect = new RECT();
      int index = GetBandActualIndex(bandIndex);
      WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_GETRECT, bandIndex, ref rect);
      return new Rectangle(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top);
    }

    private REBARBANDINFO GetRebarInfo(int index)
    {
      REBARBANDINFO rbbi = new REBARBANDINFO();
      rbbi.cbSize = Marshal.SizeOf(typeof(REBARBANDINFO));
      rbbi.fMask = (int)(RebarInfoMask.RBBIM_ID|RebarInfoMask.RBBIM_IDEALSIZE);
      WindowsAPI.SendMessage(Handle, (int)RebarMessages.RB_GETBANDINFOW, index, ref rbbi);
      return rbbi;
    }
    
    private REBARBANDINFO GetBandInfo(int index, Control currentBand)
    {
      bool placeHolder = false;
      Control band;
      if ( currentBand != null )
      {
        placeHolder = true;
        band = currentBand;
      }
      else
        band = bands[index];
      REBARBANDINFO rbbi = new REBARBANDINFO();
      rbbi.cbSize = Marshal.SizeOf(typeof(REBARBANDINFO));
          
      if ( !IsCommonCtrl6() )
      {
        rbbi.fMask = (int)RebarInfoMask.RBBIM_COLORS;
        rbbi.clrBack = (int)ColorUtil.RGB( ColorUtil.VSNetControlColor );
        rbbi.clrFore = (int)ColorUtil.RGB( 255, 0, 255 );
      }

      rbbi.iImage = 0;
      rbbi.hbmBack = IntPtr.Zero;
      rbbi.lParam = 0;
      rbbi.cxHeader = 0;

      rbbi.fMask |= (int)RebarInfoMask.RBBIM_ID;
      rbbi.wID = index;
  
      if ((band.Text != null) && (band.Text != string.Empty))
      {
        rbbi.fMask |= (int)RebarInfoMask.RBBIM_TEXT;
        rbbi.lpText = Marshal.StringToHGlobalAnsi(band.Text);
        rbbi.cch = (band.Text == null) ? 0 : band.Text.Length;
      }
  
      rbbi.fMask |= (int)RebarInfoMask.RBBIM_STYLE;
      rbbi.fStyle = (int)(RebarStylesEx.RBBS_CHILDEDGE | RebarStylesEx.RBBS_FIXEDBMP);
      if ( placeHolder == false )
        rbbi.fStyle |= (int)RebarStylesEx.RBBS_GRIPPERALWAYS;

      ToolBarEx tb = (ToolBarEx)band;
      if ( tb.UseNewRow == true)
        rbbi.fStyle |= (int)(RebarStylesEx.RBBS_BREAK);
      rbbi.fStyle |= (band is IChevron) ? (int)RebarStylesEx.RBBS_USECHEVRON : 0;

      rbbi.fMask |= (int)(RebarInfoMask.RBBIM_CHILD);
      rbbi.hwndChild = band.Handle;
      
      rbbi.fMask |= (int)(RebarInfoMask.RBBIM_CHILDSIZE);
      rbbi.cyMinChild = band.Height;
      rbbi.cxMinChild = 0;
      rbbi.cyChild = 0;
      rbbi.cyMaxChild = 0; 
      rbbi.cyIntegral = 0;
      
      rbbi.fMask |= (int)(RebarInfoMask.RBBIM_SIZE);
      rbbi.cx = band.Width;
      rbbi.fMask |= (int)(RebarInfoMask.RBBIM_IDEALSIZE);
      rbbi.cxIdeal = band.Width;
      
      return rbbi;        
    }

    private void Bands_Changed(Object s, EventArgs e)
    {
      UpdateBands();
    }

    private void Band_HandleCreated(Object s, EventArgs e)
    {
      ReleaseBands();
        
      ToolBarEx band = (ToolBarEx) s;
      UpdateBand(bands.IndexOf(band));
      
      CaptureBands();
    }
    
    private void Band_TextChanged(Object s, EventArgs e)
    {
      ToolBarEx band = (ToolBarEx) s;
      UpdateBand(bands.IndexOf(band));
    }

    private void CaptureBands()
    {
      foreach (Control band in bands)
      {
        band.HandleCreated += new EventHandler(Band_HandleCreated);
        band.TextChanged += new EventHandler(Band_TextChanged);
      }
    }
    
    private void ReleaseBands()
    {
      foreach (Control band in bands)
      {
        band.HandleCreated -= new EventHandler(Band_HandleCreated);
        band.TextChanged -= new EventHandler(Band_TextChanged);
      }
    }
    #endregion

  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO ArtfulBits Inc.
Ukraine Ukraine
Name:Kucherenko Oleksandr

Born:September 20, 1979

Platforms: Win32, Linux; - well known and MS-DOS; Win16; OS/2 - old time not touched;

Hardware: IBM PC

Programming Languages: Assembler (for Intel 80386); Borland C/C++; Borland Pascal; Object Pascal; Borland C++Builder; Delphi; Perl; Java; Visual C++; Visual J++; UML; XML/XSL; C#; VB.NET; T-SQL; PL/SQL; and etc.

Development Environments: MS Visual Studio 2001-2008; MS Visual C++; Borland Delphi; Borland C++Builder; C/C++ any; Rational Rose; GDPro; Together and etc.

Libraries: STL, ATL, WTL, MFC, NuMega Driver Works, VCL; .NET 1.0, 1.1, 2.0, 3.5; and etc.

Technologies: Client/Server; COM; DirectX; DirectX Media; BDE; HTML/DHTML; ActiveX; Java Servlets; DCOM; COM+; ADO; CORBA; .NET; Windows Forms; GDI/GDI+; and etc.

Application Skills: Databases - design and maintain, support, programming; GUI Design; System Programming, Security; Business Software Development. Win/Web Services development and etc.

Comments and Discussions