focus tab items in a CAB workspace

In one of the projects i worked on we where implementing Microsoft CAB (Composite UI Application Blocks) one of the features of the CAB is the tab view workspace.

Problem:
Tab items (which in our application inherited from System.Windows.Forms.Control as one of its base classes) where created and added to the Infragistics.Practices.CompositeUI.WinForms.UltraTabWorkspace workspace from the work item class.
When the tab got focus by calling the Show(ByVal smartPart As Object) method on the SmartParts.IWorkspace workspace class,
the last control to have the focus did not get it back infect the focus was on the tab itself and not on one of its child controls(as we required).

for example if a tab had a Textbox as one of it’s child controls, when the focus is on the Textbox then we change to another tab and change back the focus
did not return to the Textbox(but stayed on the tab header itself)

there was no way to pick an event by the view after it was showed by the workspace,
so i had to inherit from the UltraTabWorkspace and override the OnSmartPartActivated method.

Another problem was inner controls that had their child controls stored in a panel.
This was solved by having each of those controls implement an interface IPanelControl which returned the panel property of that control.

Solution:
the solution was to iterate thru all controls and child controls(and panel child controls) and listen to their GotFocus event,
when it is invoked save a reference to that control so we can refocus it later
(another problem was controls that where added dynamically i solved by overriding the OnControlAdded method)

refocusing was done by overriding the method OnSmartPartActivated on the UltraTabWorkspace

here is the code:

class TabView : System.Windows.Forms.Control
{
    /// <summary>
    /// 'monitor the views control add flow
    /// </summary>
    /// <param name="e"></param>
    /// <remarks></remarks>
    protected override void OnControlAdded(System.Windows.Forms.ControlEventArgs e)
    {
        base.OnControlAdded(e);
        zSetFocusHandlers(e.Control);
        e.Control.ControlAdded += ControlAddedEvent;
        if ((e.Control) is IPanelControl && ((IPanelControl)e.Control).PanelControl != null)
        {
            ((IPanelControl)e.Control).PanelControl.ControlAdded += ControlAddedEvent;
        }
    }

    /// <summary>
    /// 'monitor the views child controls flow
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    /// <remarks></remarks>
    protected void ControlAddedEvent(object sender, System.Windows.Forms.ControlEventArgs e)
    {
        zSetFocusHandlers(e.Control);
        e.Control.ControlAdded += ControlAddedEvent;
        if ((e.Control) is IPanelControl && ((IPanelControl)e.Control).PanelControl != null)
        {
            ((IPanelControl)e.Control).PanelControl.ControlAdded += ControlAddedEvent;
        }
    }

    /// <summary>
    /// this is a recursive method, that will
    /// loop throw all child controls and add a handler for the focus event
    /// </summary>
    /// <param name="cnt"></param>
    /// <remarks></remarks>
    private void zSetFocusHandlers(Control cnt)
    {
        AddControlFocusHandler(cnt, false);
        foreach (Control cntm in cnt.Controls)
        {
            zSetFocusHandlers(cntm);
        }
    }

    /// <summary>
    /// 'add the focus event, her we can filter the controls that dont need handeling
    /// </summary>
    /// <param name="cnt"></param>
    /// <remarks></remarks>
    protected virtual void AddControlFocusHandler(Control cnt, bool Force)
    {
        if (Force || (cnt) is IFocusHandeling
            || (cnt) is TextBox
            || (cnt) is ComboBox
            || (cnt) is Button
            || (cnt) is ListBox
            || (cnt) is CheckBox
            || (cnt) is RichTextBox
            || (cnt) is Infragistics.Win.UltraWinGrid.UltraGrid
            || (cnt) is Infragistics.Win.UltraWinEditors.UltraTextEditor)
        {
            cnt.GotFocus += Control_GotFocusEvent;
        }
    }

    /// <summary>
    /// 'when a control got a focus remember it for future use
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    /// <remarks></remarks>
    protected void Control_GotFocusEvent(object sender, System.EventArgs e)
    {
        ControlToFocus = (Control)sender;
    }

    protected Control ControlToFocus;

    /// <summary>
    /// this happens when the smart part is activated, it origens in the Tab control
    /// </summary>
    /// <remarks></remarks>
    public void SmartPartActivated()
    {
        if (ControlToFocus != null)
        {
            //calling a thread that will manage the focusing of the last focusd control
            FocusThread = new Threading.Thread(zFocusLastControl);
            FocusThread.Start();
        }
    }

    private Thread FocusThread;
    private bool FocusThreadTerminate;

    /// <summary>
    /// this method will loop and try to focus the last focusd control until the thread is aboarted or loop is finished
    /// this method is running in a nother thread
    /// </summary>
    /// <param name="obj"></param>
    /// <remarks></remarks>
    private void zFocusLastControl(object obj)
    {
        try
        {
            FocusThreadTerminate = false;
            for (int i = 0; i <= 10; i++)
            {
                zSetFocusFocus();
                if (FocusThreadTerminate) break;

                Threading.Thread.Sleep(200);
                //this code will normally never be reached
            }
        }
        catch (Threading.ThreadAbortException exa)
        {
        }
        //this is the thread exception that will be thrown when thread is aborted by the application
        catch (Exception ex)
        {
        }
        //any other error will apear here
    }

    /// <summary>
    /// try to set the focus to the control
    /// if succeeded then abort the calling thread else continue wating and try again in the next cicle
    /// </summary>
    /// <remarks></remarks>
    private void zSetFocusFocus()
    {
        if (ControlToFocus == null)
        {
            //FocusThread.Abort()
            FocusThreadTerminate = true;
            return;
        }
        if (ControlToFocus.InvokeRequired)
        {
            //ControlToFocus.BeginInvoke(New Threading.ThreadStart(AddressOf setFocusFocus))
            ControlToFocus.Invoke(new Threading.ThreadStart(zSetFocusFocus));
        }
        else if (ControlToFocus.CanFocus())
        {
            ControlToFocus.Focus();
            //FocusThread.Abort()
            FocusThreadTerminate = true;
        }
    }
}

public interface IPanelControl
{
    Control PanelControl
    {
        get;
    }
}

/// <summary>
/// inherited UltraTabWorkspace object in order to get access to the override methods
/// </summary>
/// <remarks>the reason is to set the TabKey property when the a workItem is created</remarks>
public class UltraTabWorkspace : Infragistics.Practices.CompositeUI.WinForms.UltraTabWorkspace
{

    protected override void OnSmartPartActivated(Microsoft.Practices.CompositeUI.SmartParts.WorkspaceEventArgs e)
    {
        base.OnSmartPartActivated(e);
        if (e.SmartPart != null)
        {
            ((TabView)e.SmartPart).SmartPartActivated();
        }
    }
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: