I gotta hand it to Loren Heiny of Jumping Minds software. This is just a very natural, no brainer. The ability to use commonly used proofreading gestures to edit Word documents and email messages makes perfect sense on the Tablet PC and he’s executed it very well. Like me with TEO, Loren discovered the hard way that writing Office add ins for Tablet PC isn’t a cakewalk and after what seemed like an eternity, the long awaited Ink Gestures 1.1 is available for the public. There are also several videos showing how to use it.

Congrats Loren on beating me to market! :) And on making Office one step closer to being the ideal Tablet PC software.

http://www.inkgestures.com

I’ve heard great things about this add in for quite some time but only now did I get a chance to actually install it. I think this is a must-have for any Tablet PC user but it’s also very useful to anyone that likes to keep an organized PIM.

Basically, what Anagram does is parses text from the clipboard and converts it into a Contact, Task, Appointment, etc. I mostly use it for contacts because many times people will email me with their contact info but it’s not in a VCF. It’s in an email signature or even worse, the body of their email. So you select the text (it doesn’t matter if you get stuff that isn’t a part of the contact) and Anagram will do its best to figure out where fax numbers, home numbers, email addresses, etc. all go.

The best part is, since TEO 2.x and above will override Outlook’s standard UI’s for things like this, it just works by default with TEO! I’m working on a screen capture video much like Rob Bushway did with TEO and ActiveWords to show off how Anagram works with TEO. I have one already but it has someone’s contact info that might not want to be shared.

Get this add in!

I’m not sure who reads my blog, but if anyone can give me the answer to this question I’d love to know. In .NET, how can I tell if my object is in COM heaven? Meaning it’s been separated from it’s RCW and can no longer be called. I know if you call Marshal.ReleaseComObject() it will return the reference count but what if I just want that count without decrementing it? I don’t like trapping InvalidComObjectException if I can avoid it.

Oh and I should mention that it doesn’t matter that I’m still holding a managed reference to it. The problem appears to be stemming from Windows Forms when it recursively disposes controls, it takes a different path for disposing ActiveX controls. So somewhere in there it’s doing a FinalReleaseComObject on the object even though I still have a reference to the RCW.

When you have complicated UI requirements, sometimes you might forget that all of your controls are initialized in your form’s constructor in Windows Forms. This can lead to dramatic performance problems, especially when COM interop is involved. Take for example the MapPoint integration in TEO 3.0.

In TEO 3.0, rather than just calling out to MapPoint and bringing back an image, there is a live MapPoint instance embedded in a tab that you can work with just as if you were using MapPoint. Drag/zoom/etc. However, MapPoint is an out-of-process COM server which means that when the control is created, MapPoint.exe is launched invisibly in the background to serve this functionality. While I’m proud of the plugin, it’s probably one of those things that you won’t use every time you open a contact window. Yet all that initialization is still done in the forms constructor, contributing several seconds to load times. Or at least it would have been.

If you have lots of complicated user interface components tucked away in a tab or invisible panel, consider using this control wrapper I made called DelayLoadedControl(Of T). To use it, you basically pass it a type of a control (must derive from System.Windows.Forms.Control) and add this in place of your control and none of the construction or initialization will be performed until the DelayLoadedControl becomes visible. What this means in TEO’s case is that MapPoint (and probably other tabs as well) won’t be invoked until that tab is selected, saving you valuable form launch time without complicating your design surface too much. Just stick your controls in a UserControl. The code for the control wrapper is below.

/// <summary>
/// A panel that allows for delay-loaded instantiation of controls by waiting for
/// the time that the panel is first shown.
/// </summary>
/// <remarks>
/// The type that is passed into this control instance must be a control with a 
/// default constructor. This constructor will be used to create the control when
/// it is added to the panel. This control is very useful for crowded UI's that
/// take up valuable seconds when a form is constructed, but might not be shown
/// right away such as in a tab page that might never be used. This allows the
/// time spent creating that control to be deferred until the user actually
/// requests the UI.
/// </remarks>
/// <typeparam name="T">Your control's type which must expose a default public
/// constructor.</typeparam>
public class DelayLoadedPanel<T> : Panel where T : Control, new( )
{

    /// <summary>
    /// Holds the control instance that is hosted inside this control 
    /// </summary>
    private T _innerControl;

    /// <summary>
    /// Event that is raised when the control is created on the fly inside the
    /// delay loaded panel.
    /// </summary>
    public event EventHandler<ControlCreatedEventArgs<T>> InnerControlCreated;

    /// <summary>
    /// Initializes a new instance of the <see cref="DelayLoadedPanel"/> class.
    /// </summary>
    public DelayLoadedPanel( )
    {
    }

    /// <summary>
    /// Releases the unmanaged resources used by the <see cref="T:Control"/> and
    /// its child controls and optionally releases the managed resources.
    /// </summary>
    /// <param name="disposing">true to release both managed and unmanaged
    /// resources; false to release only unmanaged resources.</param>
    protected override void Dispose( bool disposing )
    {
        if ( IsDisposed )
            return;
        if ( disposing )
        {
            if ( _innerControl != null )
            {
                _innerControl.Dispose( );
                _innerControl = null;
            }   // if
        }   // if
        // Calling base last. This is how I think it should be done anyway.
        base.Dispose( disposing );
    }

    /// <summary>
    /// Returns the control that is hosted inside this panel, or null if the
    /// control is not yet created.
    /// </summary>
    public T InnerControl
    {
        get
        {
            if ( IsDisposed )
                throw new ObjectDisposedException( );
            return _innerControl;
        }
    }

    /// <summary>
    /// Forces the creation of the inner control even though the panel may
    /// not have been displayed yet.
    /// </summary>
    public void ForceInnerControlCreation( )
    {
        if ( _innerControl == null )
        {
            _innerControl = new T( );
            _innerControl.Dock = DockStyle.Fill;
            Controls.Add( _innerControl );
            if ( InnerControlCreated != null )
            {
                InnerControlCreated( this, new ControlCreatedEventArgs<T>( _innerControl ) );
            }   // if
            _innerControl.Visible = true;
        }   // if
    }

    /// <summary>
    /// Raises the <see cref="E:Control.VisibleChanged"/> event.
    /// </summary>
    /// <remarks>
    /// Overridden to provide on-demand instantiation of the control type
    /// supplied in the 
    /// </remarks>
    /// <param name="e">An <see cref="T:EventArgs"></see> that contains the
    /// event data.</param>
    protected override void OnVisibleChanged( EventArgs e )
    {
        base.OnVisibleChanged( e );
        if ( Visible )
        {
            ForceInnerControlCreation( );
        }   // if
    }

}   // class

/// <summary>
/// Event arguments passed to the <see cref="E:DelayLoadedPanel.InnerControlCreated"/>
/// event of the <see cref="T:DelayLoadedPanel"/> class.
/// </summary>
public class ControlCreatedEventArgs<T> : EventArgs where T : Control, new( )
{

    /// <summary>
    /// The control instance that was created.
    /// </summary>
    private T _innerControl;

    /// <summary>
    /// Initializes a new instance of the <see cref="T:ControlCreatedEventArgs"/>
    /// class.
    /// </summary>
    /// <param name="innerControl">The inner control that was instantiated.</param>
    public ControlCreatedEventArgs( T innerControl )
    {
        if ( innerControl == null )
            throw new ArgumentNullException( "innerControl" );
        _innerControl = innerControl;
    }

    /// <summary>
    /// The control that was created inside the <see cref="T:DelayLoadedPanel"/>.
    /// </summary>
    public T InnerControl
    {
        get
        {
            return _innerControl;
        }
    }

}   // class