Letz learn togather

  • Creating a re-usable media player using silverlight controls.

          So, let’s quickly dive in and get started. Our objective here is to build a media player control which can be used to download streaming content and play the media while preserving the aspect ratio to respond to poor quality videos. Hope it will be fun.

    Step 1: Getting Started

    The media player is a complex control. It will in-turn host several other controls which will handle the basic functionality for the media player. In the course of this document, we will explore how to work with the following aspects of the media player:

     

    1.       Play

    2.       Pause

    3.       Stop

    4.       Forward

    5.       Backward

     

     

    6.       Mute

    7.       Status display

    8.       Aspect ratio preservation

    9.       Full screen

    10.   Timeline /Streaming

     

     

    Step 1.0: creating the user interface.  

                            

     The diagram above depicts the individual sections of the media player control. The hosting UI element contains three main regions, namely the media element, which hosts the streaming media, the controls sections at the bottom of the player and the download progress/ timeline control. A similar UI may be created using the Expressions Blend editor.

    Step 1.1: Hooking up events

     If we move to the code-behind file for the user control, it will have very little content. However, unlike other versions of Silverlight, now we can directly access the child elements of the user control from the code-behind and hook up events. We may add event handlers in the code or in XAML. Once we have placed a media element on the host UI, we can call the appropriate events for the media element on hitting one of our control panel elements.

    We will start by hooking up the basic functionality after we have placed a media element control on the host UI.

    <MediaElement x:Name="thePlayer" AutoPlay="False" Visibility="Visible"/>

    Step 1.1.1: Handling Play

    The following XAML snippet creates a play button on the host page:

    <Image x:Name="BtnPlay” Source="Images/MyPlayImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnPlay.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnPlay_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to play the media:

    thePlayer.Play();

     

    Step 1.1.2: Handling Pause

    The following XAML snippet creates a pause button on the host page:

    <Image x:Name="BtnPause" Source="Images/MyPauseImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnPause.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnPause_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to pause the media:

    thePlayer.Pause();

     

    Step 1.1.3: Handling Stop

    The following XAML snippet creates a stop button on the host page:

    <Image x:Name="BtnStop" Source="Images/MyStopImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnPause.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnStop_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to stop the media:

    thePlayer.Stop();

     

    Step 1.1.4: Handling Forward

    The following XAML snippet creates a forward button on the host page:

    <Image x:Name="BtnFwd" Source="Images/MyFwdImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnFwd.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnFwd_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to forward the media:

    thePlayer.Position = // Desired position (forwards on the timeline) on forward click ;

     

    Step 1.1.5: Handling Backward

    The following XAML snippet creates a backward button on the host page:

    <Image x:Name="BtnBwd" Source="Images/MyBwdImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnBwd.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnBwd_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to reverse the media:

    thePlayer.Position = // Desired position (backwards on the timeline) on forward click ;

     

    Step 1.1.6: Handling Mute

    The following XAML snippet creates a mute button on the host page:

    <Image x:Name="BtnMute" Source="Images/MyMuteImage.png" Stretch="Fill" Cursor="Hand">

    The following code snippet adds a click event handler in the code behind:

    this.BtnMute.MouseLeftButtonDown += new MouseButtonEventHandler(this.BtnMute_MouseLeftButtonDown);

    The following code snippet triggers the media element’s event to mute the media:

    thePlayer.IsMuted = true; //(To toggle back, set the property to false)

     

    Step 1.1.7: Handling Status Display

    To handle status of the media, we could use several approaches. I will depict it using a very simple method. We will have a storyboard with a time-line of a second. This is an empty storyboard, which will virtually server as our timer. We will hook up to the events of the story board and display the current media position in a label.

    The following XAML snippet creates a storyboard on the host page:

    <Storyboard x:Name="Timer" Duration="00:00:01"/>

    The following code snippet adds a click event handler in the code behind:

    this.Timer.Completed += new EventHandler(this.Timer_Completed);

    The following code snippet triggers the storyboard’s event to display the media status:

    this.statusText.Text = // Display text;

    The following properties of the media element will be handy while seeking such display information:

    thePlayer.Position

    thePlayer.NaturalDuration

     

    Step 1.1.8: Handling Aspect Ratio

    To preserve the aspect ratio of a media, use the following properties of the media element:

    thePlayer.Stretch = Stretch.None; // Doesn’t strech media, ideal for poor quality videos

    thePlayer.Stretch = Stretch.Uniform; // Strtches the video preserving the aspect ratios

     

    Step 1.1.9: Handling Full Screen

    To take a media element into the full screen mode, we have to investigate the following properties of the host control element:

    Application.Current.Host.Content.IsFullScreen = true;

    Declare an event in the media player, raise the event on clicking the full-screen button and use the aforesaid code snippet in the event handler. Once in full screen mode, it will respond to the ‘Esc’ key to take you back to the normal mode.

     

    Step 1.1.10: Handling Stream download/ Timeline

    To handle a media element’s stream download, we have to investigate the following properties/ events:

    this.thePlayer.DownloadProgressChanged += new RoutedEventHandler(this.ThePlayer_DownloadProgressChanged);

    private void ThePlayer_DownloadProgressChanged(object sender, RoutedEventArgs e)

            {

                if (thePlayer.DownloadProgress <= 1) // Signifies incomplete download

                {

                     // Do you custom task here, probably changing the gradient of the timeline UI element or the value of a slider element. This property may be appropriately data bound.

                }

            }

     

    Step 2: Hosting the player

    The media player can be hosted as a Silverlight control on any other Silverlight host control. The host control can hook up to and trigger events of the media player appropriately (especially the full screen and similar events. To use any Silverlight control on another host control, we may use the following syntax:

    Include:

    xmlns:mediaplayer="clr-namespace:MyNamespace;assembly=MyAssembly"

    Create control using:

    <mediaplayer:myplayer></mediaplayer:myplayer>

     

    To conclude, its exciting that the rich media object model in Silverlight exposes so much for the developers to build on. The UI for the player might be customized completely using Expressions Blend. Hope this tutorial helped in explaing how easy it is to play with media controls in Silverlight.

  • Headstart - Silverlight 2.0 User Controls

     With the new bits of Silverlight 2.0 just released, it’s exciting times for web developers around the globe. However, it also time to get ramped-up on the new ways. So, let’s quickly dive in and get started. Our objective here is to build a very simple user control which has an image and some text, on clicking the image, there is a small animation that renders a media element and allows us to play a video. Hope it will be fun.

    1.     Creating a project structure

      The solution will comprise a Silverlight application and a web application. The Silverlight application has a Silverlight user control, while the web application renders the Silverlight components.

    Step 1: Create a Silverlight Project

     

     

    The Silverlight application will automatically prompt you to add a new web application with an attached Silverlight link. If you are not planning to use an already existing web application, select the defaults and click OK.  This will add a web application, with a .xap file in the ClientBin directory, which will contain Silverlight related information. The Silverlight application contains a Page.xaml user control by default. We may add other user controls later. The web application will host the Silverlight control inside a Silverlight plug-in. You may notice a web page and an html page were added to the application, by default, to serve as test pages.

     

    Step 2: Understanding Animations

     Now that our project structure is ready, let’s jump start into building our user control. The user control will have the aforesaid animation and be hosted on a web page. To start with, open Page.xaml in Blend.

     

    Visual Studio 2008 will work seamlessly with blend to open the whole solution. While we will later hook up events inside Visual Studio, Blend is the place to get our animations gelled in.  Though this article is not intended at exploring animations in details, yet, we will have a basic look into how we can make things work.

      Inside Blend, I first add up a few dummy images and a movie to use while building our user control. Once that is ready, we add a canvas to our user control and drag drop a Text Block to hold some text, an image for the start up thumbnail, an image which is a placeholder for the media buttons   and a media element, pointing  to our movie. The initial opacities of the media element and the play buttons are set to zero, while the thumbnail and textboxes are fully visible.  The idea is, on clicking the thumbnail, the opacity of the thumbnail will go from 100 to 0 and so will be for the Text Block, while the opacities of the media element and the play buttons will grow from 0 to 100. On closing the animation, the reverse happens.

     Keeping this idea in mind, we would need two storyboards for the animations. It could be achieved using one, but I would keep things very simple for this basic demo. On creating the forward animation, the initial and the final key-frames should look like the following:

     

     

      Thus, from an initial state, in the final key-frame, the user control expands, the thumbnail grows and fades out while the media player seamlessly fades in. A close button appears on the top to reverse the animation. 

      The reverse storyboard just reverses the animation sequence and restores the control to its original shape. Thus, while the first key-frame will be a replica of the last key-frame of the forward animation, the last key-frame of the reverse animation will appear as follows:

      Blend gives full playback opportunity, without having to deploy and test the control on a real site. Once the animations look satisfactory enough, it’s time for us to hook up the events. The forward storyboard is triggered by clicking the initial image thumbnail, while clicking the close button triggers the reverse animation.  So, let’s quickly hook up the events and test our control.

     

    Step 3: Hooking up events

     If we move to the code-behind file for the user control, it will have very little content. However, unlike other versions of Silverlight, now we can directly access the child elements of the user control from the code-behind and hook up events. Let’s add two event handlers n the code. The final code block should resemble the following:

    public Page()

            {

                InitializeComponent();

                image.MouseLeftButtonDown += new MouseButtonEventHandler(image_MouseLeftButtonDown);

                rectangle.MouseLeftButtonDown += new MouseButtonEventHandler(rectangle_MouseLeftButtonDown);

            }

            void rectangle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)

            {

                ReverseBackground.Begin();

            }

     

     Here, rectangle is the name for my close button while image is my initial thumbnail preview for the video. I get hold of the two storyboards (while must be added to the user control’s resources collection) and play them appropriately. Simple as a cakewalk, isn’t it.

     

    Step 4:  Let’s see it work

      Now that all our components are ready, let’s have a look at the end product. In the web application, there must be a test page to test your control (if you had selected the default options). On this page, you will notice a section like:

    <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MediaPlayer.xap" Version="2.0" Width="100%" Height="100%" />

     This new control allows you to host a plug-in on a page and configure its source xaml from the .xap reference created earlier. That’s  all. Build and run your web application, it should run seamlessly.  In the next article, I will look into actually hooking up some functionality into the media player we just designed to get the video play/ pause/ stop and other similar functionality working.

     

  • Data Binding, the WPF way

    Lets look at the following quickly:

    ·         Creating bindings in code

    ·         Creating bindings in XAML

    ·         Static Resources

    ·         Binding Direction

    ·         Update Frequency

    ·         Hierarchical data binding

    Creating bindings in code

     The System.Windows.Data.Binding class is what synchronizes two properties:

    ·         Source is the object where the data is coming from.

    ·         Path is the property to retrieve the value from.

    ·         Target identifies the instance and the property the data is going to.

     

    As an example, we create a simple class called Customers having a few properties:

     

    class Customer

    {

    string Name;

    string Address;

    string Phone;

    }

     

     

    Now, to get the properties bound to WPF controls using code, we need to create the binding object and associate it with the desired control.

    Customer customer=New Customer(“Foo”,….);

    Binding binding=new Binding();

    Binding.Source=customer;

    Binding. Path=new PropertyPath(“Name”);

    MyTextBox.SetBinding(TextBox.TextProperty,binding);

     

     

    In the context of the above example, source object and property can be any CLR object while the target property must be a DependencyProperty.

     

    Creating bindings in XAML

    Bindings can be seamlessly created in XAML, as in code. Once again, binding needs a target DependencyProperty, while source typically is a static resource or is set in code-behind.

    As an example, let us look at the following snippet, where we bind a customer’s name to the Text property of a TextBox control:

    <StackPanel>

    <StackPanel.Resources>

    <me.Customer x:Key=”customer” Name=”Foo”…../>

    </StackPanel.Resources>

    <Label>Customer Name: </Label>

    <TextBox>

    < TextBox .Text>

    <Binding Source=”{StaticResource customer}” Path=”Name”/>

    </TextBox .Text>

    </TextBox>

    <StackPanel>

     

    As a shorthand notation, the following would have also accomplished the same objective:

    <TextBox Name=”tb” Text=”{Binding Source={StaticResource customer} , Path=Name}”/>

     

    The real flexibility of XAML binding is realized when we tie the properties of different elements together.

    As an example, we try to tie up the opacity of an image to the value of a slider control:

     

    <StackPanel>

    <Slider Name=”sl” Minimum=”0” Maximum=”1” Width=”100” Value=”1”>

    </Slider>

    <Image Source=”i1.jpg”  Width=”100” Opacity=”{Binding ElementName=s1  , Path=Value}”>

    </Image>

    <StackPanel>

     

     

     

    The DataContext Property provides a binding source which is inherited across all children of a given control. It is typically set in code behind, but here we shall look into a XAML based example:

    <Grid>

    <Grid.DataContext>

    <me.Customer x:Key=”customer” Name=”Foo”…../>

    </Grid.DataContext>

    <Label Content=”Name:”>

    <Textbox Text=”{Binding Path=Name}”>

    </Grid>

     

     

    In the above situation, the TextBox directly uses the grid’s DataContext to locate its binding source.

     

     

    Binding Direction

    Binding can control how data is transferred using the Mode property.

    The possible values of this property are:

     

    ·         OneTime - copy value once

    ·         OneWay - copy value from source to target only

    ·         TwoWay - copy value from source to target and vice-versa

     

    The default mode depends on the target DependencyProperty and whether the source property is read-only. For example, the following snippet shows a one way binding:

    <TextBox Name=”tb” Text=”{Binding Source={StaticResource customer} , Path=Name, Mode=OneWay}/>

     

     

    Update Frequency 

    Updates can be controlled using the UpdateSourceTrigger property.

    The possible values of this property are:

     

    ·         LostFocus  - copies the values when the focus is lost on source

    ·         PropertyChanged  - copies the values when the source value changes

    ·         Explicit – copies the values only when Binding.UpdateSource is called

     

    <TextBox Name=”tb” Text=”{Binding Source={StaticResource customer} , Path=Name, UpdateSourceTrigger =PropertyChanged }” />

     

     

    Hierarchical data binding

    Look into this post by John, simple and self-explainatory:

    http://joshsmithonwpf.wordpress.com/2007/05/05/binding-a-treeview-to-a-dataset/

     

  • WPF and Silverlight

    It has been fun playing around with VSTO for a few months when Orcas came my way but Silverlight seems to be the new thing to play with. I will concentrate on these in my future posts. I will start with a old post on WPF data binding and move on to Silverlight in the future weeks. Hope you have some fun reading them.

     

  • VSTO Addins and Orcas...Part 2

    Changing the default form of a folder

    One you have a replacement form region, this is the next thing you would be opting to do.

    http://msdn2.microsoft.com/en-us/library/bb231505.aspx

    If you use the object model to dynamically create items, be sure to identify the proper message type while creating the object. You can always use the default and the custom form side by side depending on application needs as long as the message classes are different.

     

    Creating Ribbons

    I would point you to a very simple example rather than scripting it all over again.

    http://blogs.msdn.com/kathleen/archive/2007/05/09/vsto-my-favorite-feature-ribbon-designer.aspx

     

    Modifying the Menu Bar

          //Method to add controls to the task bar

           

          CommandBarPopup foundMenu = (CommandBarPopup)Application.ActiveExplorer().CommandBars.ActiveMenuBar.

                        FindControl(MsoControlType.msoControlPopup,

                        missing, menuTag, true, true);               

          CommandBar menuBar = DataStore.oApp.ActiveExplorer().CommandBars.ActiveMenuBar;

          CommandBarPopup newMenuBar = (CommandBarPopup)menuBar.Controls.Add(

                            MsoControlType.msoControlPopup, missing,

                            missing, missing, false);

          if (newMenuBar != null)

          {

                            newMenuBar.Caption = "Hello";

                            newMenuBar.Tag = menuTag;                       

                            buttonOne = (CommandBarButton)newMenuBar.Controls.

                            Add(MsoControlType.msoControlButton, missing,

                                missing, 1, true);

                            buttonOne.Style = MsoButtonStyle.

                                msoButtonIconAndCaption;

                            buttonOne.Caption = "My Button";

                            buttonOne.Tag = " My Button ";

                            buttonOne.Click += new _CommandBarButtonEvents_ClickEventHandler(buttonOne_Click);

                    }

     

    Adding an application level task pane

    Create a user control (PSTab) and then add it as follows in your ThisAddIn.cs:

    PSTab pst = new PSTab();

    pst.Width = 200;

    CustomTaskPane myCustomTaskPane = this.CustomTaskPanes.Add(pst, "MyTaskPane");

    myCustomTaskPane.Width = 200;

    DataStore.MyCustomTaskPane = (Object)myCustomTaskPane;

    myCustomTaskPane.DockPosition = Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionRight;

    myCustomTaskPane.Visible = true; 

     

     

     

    Adding an inspector level task pane

    Handle the Application.Inspectors.NewInspector event to get a hadle to the current inspector. Create a custom task pane and add it to the inspector. An elongated post can be found at:

    http://msdn2.microsoft.com/en-us/library/bb322451(VS.80).aspx

    Don’t get confused. Just use it as a guideline for the events and property accessors. The needed code is not even half as long.

  • VSTO Addins and Orcas...Part 1

    I will brush through some quick things you can easily do using the really cool new product from the VS team. Some were these were really a pain before. Thank you folks for letting me off the manifest and resource file menace. Great work.

    Creating Form Regions

    It is a real cakewalk. Just follow these pics to create a replacemrnt form region:

    Use a custom message class if it is a replacement form region. The next post will cover how to set such a replacement form as the default form for a folder.

     

  • Playing with Contacts...

    Creating contacts

    ContactItem  objContact = objOutlook.CreateItem(olContactItem);

    objContact.FullName = "Abhijit";

    objContact.Email1Address = "abhijit@contoso.com";

    objContact.CompanyName = "Contoso";

    objContact.JobTitle = "Consultant";

    objContact.HomeTelephoneNumber = "533-522-1111";

    objContact.HomeAddress = "India" ;

    objContact.Birthday = "9/15/1966";

    objContact.Save();

     

     

    Methods for looking up contacts

    This can be done using several methods. I will point you to this nice post on Wes’ blog rather than re-writing the same code. However, shoot me a mail if you bump into a blocker.

    http://weblogs.asp.net/whaggard/archive/2007/01/30/how-do-i-access-my-outlook-contacts-from-my-web-application.aspx

     

    Filtering contacts

    NameSpace oNS= oApp.GetNamespace("mapi");

    oNS.Logon(<profile>,<Password>,false,false) ;

    MAPIFolder cContacts = oNS.GetDefaultFolder(OlDefaultFolders.olFolderContacts);

    Items oItems = cContacts.Items;

    ContactItem  oCt;

    oCt = oItems.Find("Filter string") ;

    That's it, just use a filter string corresponding to your application requirements.

  • Working with the Page Ccontrol

    The addin development methodologies have changed sizeably in Orcas. However, to use this COM  based control, you still have to develop your form region within Outlook. You can then import it into Orcas and handle the top level event for the scheduler. If you have built a replacement form region, this scheduler will override the default scheduler and you can play around with the events.

     Create an appointment

     Hit the developer tab and create a new form region

     Hit the contol toolbox and browse to add the page control

    Now that you have the page control on the form region region, you may save it and import it from within Orcas. If Outlook has been configured to pick up .vfb files from a particular location, once you add a recipient to the page control, you should be able to automatically see the free busy status. So, do not script your own parser unless absolutely neccessary.

     

  • Free busy...keeping you busy?

    Well, though I beleive very few of us have to actually track the iCalendar specification and code against it, it becomes neccessary especially when your mail server is not Exchange and you do not find a free adapter. Besides, even to use the free busy information from the Exchange data store in a custom application, we need to know what is to be done. I will share the ways to do it, both in an Exchange and a non-Exchange environment:

    Accessing Free Busy Information – Exchange

    1.       Using Object Model to get the bits

          This is fairly easy:

    Recipient oRecip;

    AddressEntry oAddrEntry;

    oRecip = oApp.Session.CreateRecipient(<Exchange alias>);

    if (oRecip.Resolve()==true)

    {

         oAddrEntry = oRecip.AddressEntry;

         mylistBox.Items.Add(oAddrEntry.GetFreeBusy(DateTime.Now, 15, true));

    }

          

    2.       Using WebDAV

     

          Use the second function in specific cases:

     

          public void PrintInfoUsingExchangeWebDAV(string id,string  password,string domain,string serverid,string usersmtp)

          {

     

                string server = <Your Exchange Server>;

                NetworkCredential credentials = new NetworkCredential(id, password, domain);

                string uri = string.Format("https://{0}/public/?cmd=freebusy&start=2007-09-06T09:00:00+05:30&end=2007-09-06T14:00:00+05:30&interval=60&u=SMTP:{1}", serverid, usersmtp);

                HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;

                request.Credentials = credentials;

                request.Method = "GET";

                request.ContentType = "text/xml";

                request.CookieContainer = new CookieContainer();

                request.CookieContainer.Add(GetAuthCookies(server, credentials));

                request.KeepAlive = true;

                request.AllowAutoRedirect = false;

                request.UserAgent = "Mozilla/4.0(compatible;MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.1)";

                try

                {

                    System.Net.HttpWebResponse response =    (HttpWebResponse)request.GetResponse();

                    Stream dataStream = response.GetResponseStream();

                    StreamReader reader = new StreamReader(dataStream);

                    string responseFromServer = reader.ReadToEnd();

                    txt_Response.Text = responseFromServer;

                    response.Close();

                    reader.Close();

                    dataStream.Close();

                    response.Close();

                }

                catch (Exception ex)

                {

                    string msg = ex.Message;

                    throw (ex);

                }

           }

     

                  // When Exchange is setup for Forms Authentication you need to do the login separately

       public CookieCollection GetAuthCookies(string server, NetworkCredential credentials)

       {

                // URI to OWA authorization dll

                string authURI = string.Format("{0}/exchweb/bin/auth/owaauth.dll", server, credentials.UserName);

                byte[] bytes = Encoding.UTF8.GetBytes(string.Format("destination={0}/exchange/{1}&username={2}\\{1}&password={3}", server, credentials.UserName, credentials.Domain, credentials.Password));

                HttpWebRequest request = WebRequest.Create(authURI) as HttpWebRequest;

                request.Method = "POST";

                request.ContentType = "application/x-www-form-urlencode";

                request.CookieContainer = new CookieContainer();

                request.ContentLength = bytes.Length;

                request.AllowAutoRedirect = false;

                using (Stream requestStream = request.GetRequestStream())

                    requestStream.Write(bytes, 0, bytes.Length);

                using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)

                {

                    response.Cookies = request.CookieContainer.GetCookies(request.RequestUri);

                    return response.Cookies;

                }

     

            }

     

     

    3.       Using Exchange Web Services

          I am not documenting this section in details as it is specific to Exchange 2007 and beyond. But certainly is the recommended approach for the future. Peep into:

                     http://msdn2.microsoft.com/en-us/library/bb408417.aspx 

     

     Accessing Free Busy Information – Non Exchange

               This can be tricky. Create your own .vfb file parser to return you the status bits. Bind the appropriate values to the presentation UI.  I had once written such a parser to read from Oracle Collaboration suite and will try to make the assembly available for re-use. However, I certainly do not recommend this as an approach as the product team has spent a sizeable amount of time in devising a very sound parser. One should ideally trick around and use the Outlook’s page control to play in such a situation. I will post the tricks to do that. Then just add recipients to the page control and allow Outlook to look up the free busy information from the configured location.

     

     

  • Outlook Sync

    Creating and sending a mail is a very simple task. However, while writing custom applications with form region fetching data from web services or other business object layers, often it is a big performance overhead to fetch these info on form region load. Rather, it's much better to sync up with Outlook's Send/ Receive process and pre-fetch the data and cache it accordingly. Oops, now this is simple too but trust me, difficult to locate this island of information. Lets see how it is done:

    A simple routine to create a mail is as follows:

    Mail

    NameSpace oNameSpace;

    oNameSpace= oApp.GetNamespace("MAPI");

    oNameSpace.Logon(<Profile>,<Password>,false,false);

    MailItem newMail = Application.CreateItem(Outlook.OlItemType.olMailItem);

    newMail.To = toValue;

    newMail.Subject = subjectValue;

    newMail.Body = bodyValue;

    newMail.SaveSentMessageFolder = oOutboxFolder;

    newMail.Send();

    To sync with Outlook's event, try this out:

    Syncing up with Send Receive

    SyncObject mySyncObject = this.Application.Session.SyncObjects[1];

    mySyncObject.SyncStart += new Microsoft.Office.Interop.Outlook.SyncObjectEvents_SyncStartEventHandler(mySyncObject_SyncStart);

    mySyncObject.SyncEnd += new Microsoft.Office.Interop.Outlook.SyncObjectEvents_SyncEndEventHandler(mySyncObject_SyncEnd);

    mySyncObject.OnError += new Microsoft.Office.Interop.Outlook.SyncObjectEvents_OnErrorEventHandler(mySyncObject_OnError);

     

     

    void mySyncObject_SyncStart()

    {

      //TODO--MessageBox.Show("SyncStart");

    }

    void mySyncObject_SyncEnd()

    {

      //TODO--MessageBox.Show("SyncEnd");

    }

     

    void mySyncObject_OnError(int Code, string Description)

    {

      //TODO--MessageBox.Show("SyncError");

    }

    Simple and fun. Hope it helps you folks.

  • All the fun with Appointments

    Lets take them one by one:

    Creating Appointments:

    AppointmentItem newAppointment;

    newAppointment = Application.CreateItem(OlItemType.olAppointmentItem);

    newAppointment.MeetingStatus = OlMeetingStatus.olNonMeeting;

    newAppointment.Subject = "Dummy";

    newAppointment.Location = "Anywhere";

    newAppointment.Body = "Hello Appointment";

    newAppointment.Start = DateTime.Now;

    newAppointment.End = DateTime.Now;   

    Recipients sentTo = newAppointment.Recipients;

    Recipient sentInvite = null;  

    sentInvite = sentTo.Add(AttendeeAlias);

    sentInvite.Type =OlMeetingRecipientType.olRequired;           

    newAppointment.Display(true);

    sentTo.ResolveAll();

    newAppointment.Send();

    newAppointment.Save();

    newAppointment.Close(OlInspectorClose.olDiscard);

    If you are using a custom form region and have replaced the default appointment form, you need to add in the corresponding message class:

    newAppointment.MessageClass = "IPM.Appointment.Custom_appointment";

     

    Send Cancellation and Deleting

    If you are having a handle to the corresponding appointment, use this simple piece of code:

    newAppointment.MeetingStatus = OlMeetingStatus.olMeetingCanceled; newAppointment.Send();

    newAppointment.Delete();

     

    Searching Calendar for appointments/ Sorting appointments

    Get a handle to the folder and use a similar code block: 

    Folder oCalendar;

    Items oItems;

    Items oResItems;

    String strRestriction;

    //Date Formatmm/dd/yyyy hh:mm AMPM

    oCalendar=(Folder)Application.Session.GetDefaultFolder(OlDefaultFolders.olFolderCalendar);

    oItems = oCalendar.Items;   

    strRestriction = "[Start] <= '" + startDate.ToLongDateString() + "' AND [End] >= '" + endTime.ToLongDateString() + "'";   

    oResItems = oItems.Restrict(strRestriction);

    oResItems.Sort("[Start]",missing );

     

     

    Adding custom properties

    Use a similar code block. Ideally this should be passed through an encryption routine:

    UserProperty transactionID = newAppointment.UserProperties.Add("TransactionID", OlUserPropertyType.olText, true, OlUserPropertyType.olText);                   

    transactionID.Value = "1";

     

    Looking up for custom properties

    UserProperty transactionID =existingAppointment.UserProperties.Find("TransactionID", missing);

     

    Handling Appointment Response

    The response comes in as a meeting item. Get the associated appointment and loop through the recipient collect to fetch individual response:

     

    MeetingItem mi;

    AppointmentItem ai;

    ai = mi.GetAssociatedAppointment(false);

    ai.Write += new ItemEvents_10_WriteEventHandler(ai_Write);

     

    void ai_Write(ref bool Cancel)

    {

    Recipients rcps = mi.GetAssociatedAppointment(false).Recipients;

          Recipient rp = rcps[Application.Session.CurrentUser.Name]; //Loop across the collection or use a specific alias

          string response = rp.MeetingResponseStatus.ToString();     

    }

     

    Handling Scheduling Assistant Events

    The assistant (page control) is a COM control and throws up a single top level event that you can trap in from your addin. This becomes vey handy for custom validation rules.

    newAppointment.PropertyChange += new ItemEvents_10_PropertyChangeEventHandler(newAppointment_PropertyChange);

    void newAppointment_PropertyChange(string Name)

    {

      switch (Name)

                {

                    case "Start":

                        UpdateMeetingTimes();

                        break;

                    case "End":

                        UpdateMeetingTimes();

                        break;                

                    case "RequiredAttendees":

                        UpdateAttendees();

                        break;                

                    case "OptionalAttendees":

      UpdateAttendees();

                        break;                

                    case "ResponseStatus":

      UpdateStatus();

            break;

                    case "MeetingStatus":

      UpdateStatus();

                        break;

                    default:

                        break;

                }

    }

     

  • VSTO all the way...

     As a developer at Microsoft, I have been programming with VSTO for quite some time. With Orcas coming in and with the new releases of Exchange supporting web services, development has changed considerably. When I got stuck earlier, I had to hunt a lot for information. Well, it was all there, but honestly, in bits and parts everywhere. In a series of posts across several blogs, I had earlier tried to connect these islands. I will make an effort to sum up everything here with appropriate pointers to additional information. Hope this helps and do reach me if it doesnt. Happy working. Please leave your feedback on how I can improve this blog by letting me know the things you would like me to append in here.


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker