Microsoft Agent; Providing a Custom Popup Menu

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

A second honeymoon with Microsoft Agent. Do you remember our article Programming Microsoft Agent in WinForms and our sample application PartIt? After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place (welcome :).)

Enough talking, let’s get to work! First, prepare your code that loads the Microsoft Agent and brings it to the screen.

After that, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including ‘Hide’ maybe) and finish the item event handlers.

Now, let’s complete it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

    AxAgentObjects.AxAgent agentCtl;
    AgentObjects.IAgentCtlCharacterEx agentChar;

    // initializing 'agentCtl'
    // . . .

    agentCtl.Characters.Load("Merlin", "merlin.acs");
    agentChar = agentCtl.Characters.Character("Merlin");
    agentChar.AutoPopupMenu = false;

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// agentCtl.ClickEvent += agent_ClickEvent;

public void agentCtl_ClickEvent(object sender, AxAgentObjects._AgentEvents_ClickEvent e)
{
    if (e.characterID == "Merlin")  // check for this if you have many characters
    {
        if (e.button == 2) // 1 = left, 2 = right
        {
            myContextMenu.Show(e.x, e.y);
        }
    }
}

Well done!

Have a nice Sunday!

Creating a Simple Twitter Client Application

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.twitoo.msi

Contents

Contents of this writing:

  • Contents
  • Overview
  • Introduction
  • Twitter API
    • What Twitter API is
    • API Documentation
    • Things to be Kept into mind
  • API Methods
    • Method Address
    • Methods with Arguments
    • Methods Require Authentication
  • Twitter and .NET
    • Accessing the API
    • Authentication
    • Encoding URIs
    • Business Objects
    • Retrieving Data
  • twittoo; Sample Application
    • Snapshots
    • Description
    • Interface
    • URL Shortening
    • Download

Overview

This writing discusses the Twitter API and how you can utilize it in your managed application. It begins by a brief discussion of the API and the methods used. After that, it digs into the discussion of how you can utilize the API into your application with help of code samples and examples. At the end of this writing, there’s a nice open-source Twitter client application available for download. Worth mentioning that this article focuses on the REST API set of Twitter, specifically XML endpoints.)

Introduction

Today, we are talking about Twitter as it is one of the most famous social networking services. This article discusses how you can create Twitter clients using .NET Framework. Oh, before we start, I’d like to introduce you my twitter account first; it’s @elsheimy.

Twitter API

What Twitter API is

Before we dig into the discussion of Twitter API we need to know first what the Twitter API is.

Twitter API is a set of endpoints (or methods) that allow applications to work with Twitter data.

Twitter API consists of three parts:

  • Two REST APIs:
    • REST API:
      This is the core API set; it allows developers to access core Twitter data, it contains most (if not all) of the methods and functions you would use to utilize Twitter data in your application, and it supports (mostly) three formats (or endpoints) for each method: XML, Atom, and JSON formats.
    • Search API:
      Fully integrating search methods allows you to search for tweets based on specific criteria and to search for daily/weekly hot trends. It supports only the JSON format. This set of API was originally developed by Summize, Inc. and integrated into the Twitter API. (Twitter is looking forward to unify the REST API.)
  • Streaming API:
    A set of API methods that allow near-realtime access to various subsets of Twitter public statuses. All methods are in JSON format, no XML or ATOM endpoints available.

Actually, the REST API would be very sufficient for you unless you find it easier to handle the JSON format.

It is worth mentioning that the REST API provides very handful and sufficient search methods that your application can use. If you need more searching capabilities (like searching for tweets that confirm to a set of criteria,) you can dig into the REST Search API.

You are not required to stick to a specific set of API methods. You can mix the methods you like from the three APIs.

Wikipedia: REST (Representational State Transfer) is a style of software architecture for distributed hypermedia systems such as the World Wide Web.

Wikipedia: JSON (JavaScript Object Notation) is a lightweight text-based open standard designed for human-readable data interchange based on the JavaScript programming language.

Wikipedia: Atom Publishing Protocol (AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

This writing focuses on the XML endpoints of the REST API.

API Documentation

Twitter has with a very nice API documentation that documents each and all methods and endpoints of the three parts of the API. This documentation can be found here: http://apiwiki.twitter.com.

To avoid duplication, we are not going to discuss the Twitter API again in this writing; it is already documented and available for all users. Instead, we will take a brief look at the API and discuss how we can utilize it in our .NET application. For simplicity, we will focus on XML endpoints. Therefore, all of our code would rely on the XML features of .NET Framework 2.0 (i.e. System.Xml.dll library.) Oh, you are free to write the code the way you like (e.g. integrating LINQ for XML into the code.)

Now, we are going to look inside the Twitter API.

Things to be Kept into Mind

There are some basics of the Twitter API that a developer should keep into his mind.

  • Calls to the Twitter API are limited:
    Don’t expect to post unlimited updates or to follow thousands of users in just an hour! Many methods have a call limit (hourly/daily.) For example, at the time of this writing, you have only 150 API requests per hour, further requests would return an exception. In addition, you can send only 1,000 updates and 250 direct messages per day (can we remove this word ‘only€?) You can check Twitter limits here.
  • Every endpoint has its HTTP methods that need to be set (e.g. GET, POST, and DELETE) in the request. The documentation of each endpoint lists the HTTP methods required for that endpoint. For more information about HTTP and HTTP methods check the RFC 2616 Section 9 document.
  • Some methods (like methods send updates) require user authentication and others not. Authentication is discussed soon.

API Methods

The following is a discussion about Twitter API methods and how you can call them.

Method Address

Every method has an address (i.e. URI). The address of a method is something like that: http://api.twitter.com/1/statuses/public_timeline.format (for the statuses/public timeline method.) We replace the format field with the format we like to work with (e.g. xml, atom, json.)

Now, try to copy the method address http://api.twitter.com/1/statuses/public_timeline.xml and paste it into your browser. You should get something like that:


    
        Wed Apr 14 19:32:07 +0000 2010
        12179999634
        35 Ingenious Examples of Footwear - http://su.pr/1JxMAr
        <a href="http://apiwiki.twitter.com/" rel="nofollow">API</a>
        false
        
        
        false
        
        
            15736190
            Smashing Magazine
            smashingmag
            Freiburg, Germany
            Vitaly Friedman, editor-in-chief of SmashingMagazine.com ...
            http://a3.twimg.com/profile_images/572829723/...
            http://www.smashingmagazine.com
            false
            166019
            B2DFDA
            333333
            f51616
            ffffff
            eeeeee
            394
            Tue Aug 05 14:00:40 +0000 2008
            4
            -10800
            <time>Greenland</time>
            http://s.twimg.com/a/...
            true
            false
            true
            false
            false
            8703
            en
            false
        
        
        
        
        
    
    . . .

This method returns the 20 most recent statuses from arbitrarily-selected non-protected Twitter users; this list is called the Public Timeline. Because we have selected the XML format (or endpoint) we end up with XML data.

From the data returned we can extract the structure of statuses and users. Twitter API is good enough; it uses the same structure for all data returned. In other words, all methods (in the REST API, remember?) use the same structure (i.e. schema) to represent status and user objects. Those structures are covered soon.

Methods with Arguments

Most of methods accept arguments, some are optional and others are required. An example of a function requires a single argument is the users/show method that returns profile information about a specific user.

This function accepts one of three arguments:

  • id:
    The ID or screen name of the user.
  • user_id:
    The ID of the user.
  • screen_name:
    The screen name of the user.

We set arguments the same way we set web pages query strings. Considering my twitter account @elsheimy as an example, we could query user profile using one of four ways:

All of the four calls return the same results that should be like this:


    19645411
    Mohammad Elsheimy
    Elsheimy
    KB, Egypt
    Technology evangelist from Egypt born in 1991
    http://a1.twimg.com/profile_images/833061500/...
    http://justlikeamagic.com
    false
    278
    DBE9ED
    333333
    CC3366
    E6F6F9
    DBE9ED
    179
    Wed Jan 28 10:47:36 +0000 2009
    7
    7200
    <time>Cairo</time>
    http://s.twimg.com/a/1271811071/...if
    false
    
    true
    false
    
    1877
    en
    false
    
        Wed Apr 21 16:21:46 +0000 2010
        12585240751
        I'm reading this, it's really hot: #RFC 2616 - Hypertext Transfer Protocol ...
        <a href="http://bit.ly" rel="nofollow">bit.ly</a>
        false
        
        
        false
        
        
        
        
        
    

As you can see, it returns user information and the last update of that user. Notice that the structure of the data is the same in all methods.

Methods Require Authentication

Some methods require user authentication. Examples are functions update status, send direct messages, retrieve friends’ timeline, etc.

Let’s take an example. Bring up your browser and browse to the address of statuses/friends timeline method that returns user’s friends’ timeline, http://api.twitter.com/1/statuses/friends_timeline.xml. A small window like this should appear that asks you your Twitter username and password.

Figure 1. IE Authentication

Provide your Twitter username and password to pass to the results. If authentication was OK, you should receive the most recent 20 statuses of your friend timeline in the same structure (schema) as the Public Timeline method. On the other hand, if the authentication process failed, you should receive an error:


    /1/statuses/friends_timeline.xml
    Could not authenticate you.

Let’s consider another example, the direct_messages/new method. This function sends a new direct message to a specific user from the current user authenticated.

This function accepts two required arguments:

  • user:
    The ID or screen name of the recipient user. You can use one of two arguments in place of this argument:

    • screen_name:
      Screen name of the user.
    • user_id:
      The ID of the user.
  • text:
    Message body, not longer than 140 UTF-8-encoded characters.

The following two calls send the same message (‘hi, how is it going€) to the same user:

Notice that we need to encode arguments before we pass them to the server.

Because this function updates data, it requires HTTP POST method. Therefore, you won’t be able to try it from your browser unless your browser allows you to set HTTP methods in the request.

If the function succeeded, it should return the message object that has been sent, and it should be like this:


    88619848
    1401881
    all your bases are belong to us.
    7004322
    Wed Apr 08 20:30:04 +0000 2009
    dougw
    igudo
    
        1401881
        Doug Williams
        dougw
        San Francisco, CA
        Twitter API Support. Internet, greed, users, dougw ...
        http://s3.amazonaws.com/twitter_production/...
        http://www.igudo.com
        false
        1036
        9ae4e8
        000000
        0000ff
        e0ff92
        87bc44
        290
        Sun Mar 18 06:42:26 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://s3.amazonaws.com/...
        false
        3394
        false
        false
        true
    
    
        7004322
        Doug Williams
        igudo
        North Carolina
        A character.
        http://s3.amazonaws.com/...
        http://www.igudo.com
        false
        19
        69A1AA
        000000
        F00
        ACBEC1
        8A8F85
        3
        Thu Jun 21 21:16:21 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://static.twitter.com/...
        false
        382
        false
        true
        true
    

Notice that the sender and recipient are just user objects. It is worth mentioning that the direct_messages function return a list of direct_message objects (like the timeline functions that return list of status objects.)

Twitter and .NET

Now, you have a solid understanding of the Twitter API and how you can access it. Now, let’s utilize this API into our .NET applications.

Accessing the API

To access the API from your .NET application you need to create a HTTP request and send it to the server and wait for server response. Let’s consider an example. The following code snippet connects to the server and retrieves a System.Xml.XmlDocument that contains the returned data.

// C# Code

public static Main()
{
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml");
}

public static XmlDocument GetResponse(string uri)
{
    WebRequest req = WebRequest.Create(new Uri(uri));

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml")
End Sub

Public Function GetResponse(ByVal uri As String) As XmlDocument

    Dim req As WebRequest = WebRequest.Create(New Uri(uri))

    Dim doc As New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

We have used the System.Xml.WebRequest class to create the request and to get the response (as an instance of System.Xml.WebResponse class) from the server. Once we get the XmlDocument, we can walk through the data and retrieve it.

Authentication

You can take one of two approaches to authenticate Twitter users:

  • OAuth Authentication:
    An authentication protocol that allows users to approve application to act on their behalf without sharing their password. As this function requires more overhead and requires your application to be registered in the Twitter clients’ directory, we would better use the second approach in our examples.
  • Basic Authentication:
    To provide authentication information in each request you make to the server. Unfortunately, Twitter announced that this method will not be available later.

Considering a method like statuses/update method that updates the status information of the user (i.e. sends a tweet) we would develop our previous code to be like this:

// C# Code

public static void Main()
{
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API",
        "elsheimy", "b@zzword", true);
}

public static XmlDocument GetResponse(string uri, string username, string password, bool post)
{
    WebRequest req = WebRequest.Create(new Uri(uri));
    if (post)
        req.Method = "POST";
    if ((username != null) && (username.Trim() != String.Empty) && (!String.IsNullOrEmpty(password)))
        req.Credentials = new NetworkCredential(username.Trim(), password);

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API", "elsheimy", "b@zzword", True)
End Sub

Public Function GetResponse(ByVal uri As String, ByVal username As String, ByVal password As String, ByVal post As Boolean) As XmlDocument
    Dim req As WebRequest = WebRequest.Create(New Uri(uri))
    If (post) Then
        req.Method = "POST"
    End If
    If ((username  Nothing) And (username.Trim()  String.Empty) And (Not String.IsNullOrEmpty(password))) Then
        req.Credentials = New NetworkCredential(username.Trim(), password)
    End If

    Dim doc As XmlDocument = New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

Notice how we set the HTTP method based on the function requirements. It is worth mentioning that a status should not exceed 140 UTF-8-encoded characters.

Encoding URIs

Have you noticed the previous code? It tries to post the update ‘hello from the API€. Because the text is included with the URI, special handling to the text should be carried. This special handling for text included in URIs is usually called Percent-encoding or URL Encoding. This encoding replaces unsafe characters with their hexadecimal values preceded by percentage (%) signs. Unsafe characters are those somewhat conflicted with URI special characters. For example, if we would encode the text ‘hello from the API€ we would end up with ‘hello%20from%20the%20API€.

There are many unsafe characters that should be percent-encoded including $, +, &, :, and =. A nice discussion of URL Encoding can be found in the article URL Encoding by Brian Wilson.

Once we get the idea, we can create our percent-encoding class that encodes/decodes strings:

// C# Code

private static string[,] _chars = new string[,]
        {
        { "%", "%25" },     // this is the first one
        { "$" , "%24" },
        { "&", "%26" },
        { "+", "%2B" },
        { ",", "%2C" },
        { "/", "%2F" },
        { ":", "%3A" },
        { ";", "%3B" },
        { "=", "%3D" },
        { "?", "%3F" },
        { "@", "%40" },
        { " ", "%20" },
        { """ , "%22" },
        { "", "%3E" },
        { "#", "%23" },
        { "{", "%7B" },
        { "}", "%7D" },
        { "|", "%7C" },
        { "\", "%5C" },
        { "^", "%5E" },
        { "~", "%7E" },
        { "[", "%5B" },
        { "]", "%5D" },
        { "`", "%60" } };

public static string EncodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 0], _chars[i, 1]);

    return url;
}

public static string DecodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 1], _chars[i, 0]);

    return url;
}

' VB.NET Code
Public Module UrlEncoder

Private _chars(,) As String = _
{ _
{"%", "%25"}, _
{"$", "%24"}, _
{"&", "%26"}, _
{"+", "%2B"}, _
{",", "%2C"}, _
{"/", "%2F"}, _
{":", "%3A"}, _
{";", "%3B"}, _
{"=", "%3D"}, _
{"?", "%3F"}, _
{"@", "%40"}, _
{" ", "%20"}, _
{"", "%22"}, _
{"”, “%3E”}, _
{“#”, “%23”}, _
{“{“, “%7B”}, _
{“}”, “%7D”}, _
{“|”, “%7C”}, _
{“”, “%5C”}, _
{“^”, “%5E”}, _
{“~”, “%7E”}, _
{“[“, “%5B”}, _
{“]”, “%5D”}, _
{“‘”, “%60”}}

Public Function EncodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 0), _chars(i, 1))
Next

Return url
End Function

Public Function DecodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 1), _chars(i, 0))
Next

Return url
End Function

End Module

For clearness, we have included the encoded string of each character along with the character itself. You don’t have to do this. You can convert the character to a number and just output the number in hex format.

Now we could change the code that updates the status to the following:

// C# Code

public static void Main()
{
    string uri;
    string text = UrlEncoder.EncodeUrl("hello from the API");
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" + text;
    GetResponse(uri, "elsheimy", "b@zzwrd", true);
}
' VB.NET Code

Public Sub Main()

    Dim uri As String
    Dim text As String = UrlEncoder.EncodeUrl("hello from the API")
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" & text
    GetResponse(uri, "elsheimy", "b@zzwrd", True)
End Sub

.NET includes a nice function that escapes (encodes) a URI, System.Uri.EscapeUriString() function. However, this function does not encode all unsafe characters.

Business Objects

Once you know the structure of the XML data returned, you can create your business objects that would encapsulate this data. The following are the three classes that would represent our three core objects, the user, the status, and the message.

// C# Code

public structure TwitterUser
{
    public long ID;
    public string Name;
    public string ScreenName;
    public string Location;
    public string Description;
    public string ProfileImage;
    public string Url;
    public bool IsProtected;
    public long FollowersCount;
    public long FriendsCount;
    public string CreatedAt;
    public long FavoritesCount;
    public bool Verified;
    public bool Following;
    public long StatusCount;
}

public structure TwitterStatus
{
    public string CreatedAt;
    public long ID;
    public string Text;
    public string Source;
    public bool Truncated;
    public long InReplyToStatusID;
    public long InReplyToUserID;
    public bool Favorited;
    public string InReplyToScreenName;
    public TwitterUser User;
}

public structure TwitterMessage
{
    public long ID;
    public long SenderID;
    public long SenderScreenName;
    public long RecipientID;
    public long RecipientScreenName;
    public string Text;
    public string CreatedAt;
    public TwitterUser Sender;
    public TwitterUser Recipient;
}
' VB.NET Code

Public Structure TwitterUser
    Public ID As Long
    Public Name As String
    Public ScreenName As String
    Public Location As String
    Public Description As String
    Public ProfileImage As String
    Public Url As String
    Public IsProtected As Boolean
    Public FollowersCount As Long
    Public FriendsCount As Long
    Public CreatedAt As String
    Public FavoritesCount As Long
    Public Verified As Boolean
    Public Following As Boolean
    Public StatusCount As Long
End Structure

Public Structure TwitterStatus
    Public CreatedAt As String
    Public ID As Long
    Public Text As String
    Public Source As String
    Public Truncated As Boolean
    Public InReplyToStatusID As Long
    Public InReplyToUserID As Long
    Public Favorited As Boolean
    Public InReplyToScreenName As String
    Public User As TwitterUser
End Structure

Public Structure TwitterMessage

    Public ID As Long
    Public SenderID As Long
    Public SenderScreenName As Long
    Public RecipientID As Long
    Public RecipientScreenName As Long
    Public Text As String
    Public CreatedAt As String
    Public Sender As TwitterUser
    Public Recipient As TwitterUser
End Structure

Retrieving Data

Now you can walk through the XML data and get that data inside your objects. The following code returns a list of statuses from your friends’ timeline.

// C# Code

public static void Main()
{
    GetStatuses("elsheimy", "b@zzword");
}
public static List GetStatuses(string username, string password)
{
    XmlNode node = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml",
        username, password, true);

    List lst = new List(node.ChildNodes.Count);

    foreach (XmlNode nd in node.ChildNodes)   // for each status
        lst.Add(HandleStatus(nd));

    return lst;
}

public static TwitterStatus HandleStatus(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    TwitterStatus status = new TwitterStatus(
        nd["created_at"].InnerText,
        HandleNumber(nd["id"]),
        FormatText(nd["text"]),
        FormatText(nd["source"]),
        HandleBool(nd["truncated"]),
        HandleNumber(nd["in_reply_to_status_id"]),
        HandleNumber(nd["in_reply_to_user_id"]),
        HandleBool(nd["favorited"]),
        FormatText(nd["in_reply_to_screen_name"]),
        HandleUser(nd["user"]));
    return status;
}

public static TwitterUser HandleUser(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    long id = HandleNumber(nd["id"]);
    TwitterUser user;

    user = new TwitterUser(
        id,
        FormatText(nd["name"]),
        FormatText(nd["screen_name"]),
        FormatText(nd["location"]),
        FormatText(nd["description"]),
        nd["profile_image_url"].InnerText,
        nd["url"].InnerText,
        HandleBool(nd["protected"]),
        HandleNumber(nd["followers_count"]),
        HandleNumber(nd["friends_count"]),
        nd["created_at"].InnerText,
        HandleNumber(nd["favourites_count"]),
        HandleBool(nd["verified"]),
        HandleBool(nd["following"]),
        HandleNumber(nd["statuses_count"]));

    return user;
}
' VB.NET Code

Sub Main()
    GetStatuses("elsheimy", "b@zzword")
End Sub

Public Function GetStatuses(ByVal username As String, ByVal password As String) As List(Of TwitterStatus)
    Dim node As XmlNode = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml", username, password, True)

    Dim lst As New List(Of TwitterStatus)(node.ChildNodes.Count)

    For Each nd As XmlNode In node.ChildNodes
        lst.Add(HandleStatus(nd))
    Next

    Return lst
End Function

Public Function HandleStatus(ByVal nd As XmlNode) As TwitterStatus
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool

    Dim status As New TwitterStatus( _
            nd("created_at").InnerText, _
            HandleNumber(nd("id")), _
            FormatText(nd("text")), _
            FormatText(nd("source")), _
            HandleBool(nd("truncated")), _
            HandleNumber(nd("in_reply_to_status_id")), _
            HandleNumber(nd("in_reply_to_user_id")), _
            HandleBool(nd("favorited")), _
            FormatText(nd("in_reply_to_screen_name")), _
            HandleUser(nd("user")))

    Return status
End Function

Public Function HandleUser(ByVal nd As XmlNode) As TwitterUser
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool
    Dim id As Long = HandleNumber(nd("id"))

    Dim user As New TwitterUser( _
        id, _
        FormatText(nd("name")), _
        FormatText(nd("screen_name")), _
        FormatText(nd("location")), _
        FormatText(nd("description")), _
        nd("profile_image_url").InnerText, _
        nd("url").InnerText, _
        HandleBool(nd("protected")), _
        HandleNumber(nd("followers_count")), _
        HandleNumber(nd("friends_count")), _
        nd("created_at").InnerText, _
        HandleNumber(nd("favourites_count")), _
        HandleBool(nd("verified")), _
        HandleBool(nd("following")), _
        HandleNumber(nd("statuses_count")))
    Return user
End Function

twittoo; Sample Application

twitto, is our WinForms sample application that utilizes the Twitter API. This is just a very simple application with basic functionalities.

Snapshots

The following are snapshots of the application:

Figure 2 - twitto 0

Figure 3 - twitto 1

Figure 4 - twitto 2

Figure 5 - twitto 3

Figure 6 - twitto 4

Figure 7 - twitto 5

Description

This application was created using C# and WinForms 2.0 technology; it allows the user to navigate through his friends’ timeline, mentions, direct messages, retweets, and friends, and to update his status, reply to tweets, retweets, and to direct messages. Data is not refreshed automatically, the user have to click the ‘refresh€ button. (You can create your own routine that creates a timer that updates the data automatically.)

Interface

As you see, the application uses just the Windows Common Controls all around the application; no 3rd party controls were used.

To represent a status, message, or a user, the application overuses the System.Windows.Forms.TableLayoutPanel control to represent each status, message, or user. It consists of four columns and two rows. The following figure shows how the control is laid-out.

Figure 8 - TableLayoutPanel Status Template

URL Shortening

twittoo, has a very nice feature, it allows the user to insert a shortened URL into his tweets. For this to work, the application makes use of http://is.gd URL shortening service. The following is the function that utilizes the http://is.gd API:

// C# Code

public static string Shorten(string url)
{
    if (!System.Text.RegularExpressions.Regex.IsMatch
        (url, @"(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&:/~+#]*[w-@?^=%&/~+#])?"))
        throw new FormatException("The URL you specificed is not in the current format.");

    url = Uri.EscapeUriString(url);
    string reqUri = String.Format(@"http://is.gd/api.php?longurl={0}", url);

    WebRequest req = WebRequest.Create(reqUri);
    req.Timeout = 5000;
    using (System.IO.StreamReader reader =
        new System.IO.StreamReader(req.GetResponse().GetResponseStream()))
    {
        return reader.ReadLine();
    }
}
' VB.NET

Public Function Shorten(ByVal url As String) As String
    If (Not System.Text.RegularExpressions.Regex.IsMatch _
        (url, "(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&;:/~+#]*[w-@?^=%&;/~+#])?")) Then
        Throw New FormatException("The URL you specificed is not in the current format.")
    End If

    url = Uri.EscapeUriString(url)
    Dim reqUri As String = String.Format("http://is.gd/api.php?longurl={0}", url)

    Dim req As WebRequest = WebRequest.Create(reqUri)
    req.Timeout = 5000

    Dim reader As New System.IO.StreamReader(req.GetResponse().GetResponseStream())
    url = reader.ReadLine()
    reader.Close()

    Return url
End Function

Download

Download twittoo; our sample application

Creating a Simple Sheet Designer in C#

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.Sisc.msi

Contents

Contents of this article:

  • Contents
  • Introduction
  • Problem
  • Requirements
  • Solution
  • Snapshots
  • Component Design
  • Class Diagrams
  • Database Diagram
  • Characteristics
  • Background
  • Code Highlights
  • Download

Introduction

Today, we are going to create a simple application, actually a simple sheet designing tool. This tool gives the user the flexibility to design his sheets, reports, bills, invoices, and receipts (whatever.)

In this writing, we will refer to reports, bills, invoices, receipts, etc. with just the name sheets. For this, we will need to give a sheet a definition.

A sheet is just like an empty page (i.e. template) that contains sheet items. For example, a resume template is a sheet that has many items (name, birth date, address, etc.) A cash receipt is a sheet that has a few items (date, amount, charged to, received by, etc.)

We will go through this tool in a nice way. We will begin by a very simple system analysis and application design. After that, we will get into coding.

Actually, I€™m not an architect, and I think I will never be. So please, DO NOT blame me for this bad analysis. It is just an illustration to get you have a solid understanding of what our application is designed for.

Problem

The user is tied into the sheet designs that the developer has created for him. The user should have the ability to design his own sheets and/or to edit application defined sheets.

Requirements

User Requirements

The following is list of common user requirements:

  • The application should allow the user to create, edit, and delete sheets, and to group those sheets into categories he creates.
  • For the sake of simplicity, those categories would be only one level, no nested categories are allowed.
  • The application should be persistent. The data should be stored in a database and loaded when the user needs it.
  • The sheet should be loaded with right data and printed whenever the user asks.
  • Like Visual Studio, the user should have a Toolbox that has all types of sheet items. The user could insert a sheet item into his sheet by drawing it into the screen, dragging it from the toolbox, or just double-clicking its icon in the toolbox.
  • The user should have a ruler to allow him measuring sheet item dimensions. In addition, he should have a design-time-only grid.
  • The user should be allowed to set attributes of the sheet like the page setup, grid settings, background color, etc.
  • Not all sheet attributes are printed out; some are for design-mode only (like background color, and grid.)
  • Each type of sheet items should have its own attributes and the user should be allowed to change them too.
  • The user should be allowed to drag sheet items around the screen and position them.
  • The user should be allowed to cut, copy, paste, and delete sheet items.
  • Of course, the user should be allowed to print out the sheet, and to preview it too.
  • The application should be generic to be used in any system and in any application.
  • Other pieces of the system should have the possibility to interact with the sheet.

Functional Requirements

Common functional (developer) requirements are:

  • Abstraction. Component/Class abstraction should be considered into the design. The system should consist of several components each of which groups a related features together (e.g. interface objects, business objects, and data management.)
  • Extensibility. The system should be extensible. A good class hierarchy should be created, and derivation (inheritance) should be taken into consideration. In addition, every sheet item should be represented by a class in this hierarchy.
  • Technically, sheet items would be called shapes. Because they are just drawings into the sketch (or sheet.)
  • To allow the user to work with sheet items (shapes) and the application to populate the sheet with data, sheet items should not be drawn directly into the sheet page. Instead every item should represented by a class that has its own drawing routine.
  • The sheet should be a container for the shapes. All shapes are children of the sheet.
  • The sheet and rulers should be Windows controls to allow them to be hosted by a Windows Form.
  • Sheet items should be Windows controls too to allow them to be hosted by the sheet.
  • The sheet items should be owner-drawn controls and they should not be derived from existing Windows controls to help integrating them easily into the sheet.
  • For the sake of simplicity, the sheet and shapes should be serialized into XML and stored in the database.
  • The database should be a SQL Server database to allow faster communication and easier manipulation of XML data.
  • The database should have three tables for the three core system components, the category, the sheet, and the shape.
  • Every sheet item in a given sheet should have a unique name (or a tag) to allow other system components to interact with it (e.g. populate it with the right data.)
  • The developer should have the ability to control the quality of the painting process for each control or item independently.

Solution

After going through project requirements and considering a sophisticated system design, we got a nice plan for our project that would be called Geming SISC (Sheet Infrastructure Components.)

This project would be created using C# and .NET 2.0 (or future versions of course.)

Snapshots

The following are final snapshots of the application. Figure 1 shows a cash receipt sheet designed by labels, boxes, and lines. Figure 2 shows a simple resume template designed by only a few labels and a picture. The left pane of the application lists current sheets grouped by their categories.

Figure 01 - Geming SISC (Cash Receipt)

Figure 01 - Geming SISC (Cash Receipt)

Figure 2 - Geming SISC (Resume Template)

Figure 2 - Geming SISC (Resume Template)

As you know other system components could fill those fields up with correct information.

Component Design

Figure 3 shows the three components of our system.

Figure 3 - Geming SISC Component Design

Figure 3 - Geming SISC Component Design

The three core components of our system, Geming SISC are:

  • Geming.Sisc.Infrastructure:
    Contains the sheet control and shapes controls (label, text box, etc.) The sheet, rulers, and sheet items controls are all derived from System.Windows.Forms.Control.
  • Geming.Sisc.Data:
    Business objects that would be sent to the data among database manager objects. This component references the Geming.Sisc.Infrastructure component.
  • Geming.Sisc.Driver:
    The application interface that would be used to design sheets. It references the other two components.

Class Diagrams

Extensibility is one of the main goals of the system. A flexible class hierarchy should be considered as well as derivation (i.e. inheritance) of controls for other external objects.

The following is the class hierarchy for the sheet and the ruler controls.

Figure 4 - Sheet and Rulers Class Hierarchy

Figure 4 - Sheet and Rulers Class Hierarchy

As we can see, the base class is the System.Windows.Forms.Control class. This allows the sheet and rulers controls to be inserted into a Windows form or a Windows control.

The following figure, figure 5, shows the class hierarchy of sheet items (technically called shapes.)

Figure 5 - Sheet Items (Shapes) Class Hierarchy

Figure 5 - Sheet Items (Shapes) Class Hierarchy

As we can see, the base class for all shapes is the abstract Geming.Sisc.Infrastructure.ShapeBase class which inherits from System.Windows.Forms.Control class. All other shapes are derived from ShapeBase.

Two edit shapes were created, TextBoxShape that has a look like a Windows Text Box, and LabelShape that has a look like a normal Windows Label. Both are derived from the abstract EditableShapeBase.

Other shapes are BoxShape, LineShape, ImageShape, and CheckBoxShape.

All classes are serializable (implement System.Runtime.Serialization.ISerializable interface,) so they can easily converted into XML and pushed to the database.

For simplicity, we have developed just the box, line, image, check box, and two edit shapes (or sheet items.) You can go further and create any other shapes you like. In addition, you can create more specific items like CurrencyField, DateField, etc.

For more detailed class diagrams like those shows class members, check the application code.

Database Diagram

In its simplicity, database is defined as the following diagram illustrates:

Figure 6 - Geming SISC Database Diagram

Figure 6 - Geming SISC Database Diagram

Notice that, all data operations are carried out through stored procedures in the database.

The Shape.Value column is of the type xml to allow easier manipulation of XML data in the future.

Characteristics

Here are some of the characteristics (i.e. attributes) of the sheet (some are represented by properties):

  • Background color:
    The user should be able change the background color. Notice that the background color is not printed.
  • Non-printable grid:
    To help the user position sheet items. The user should be able to display them or not. In addition, the user can change the grid color.
  • Margin:
    The application should set the page margin based on print settings.
  • Title:
    Every sheet has a title, a description, and a category.

In addition, next is a list of some the characteristics of a shape (sheet item):

  • Selected:
    Is the shape currently selected or not. The user could select a shape by the mouse. A selection frame is drawn around the shape when selected.
  • Non-printable size grip:
    Each shape should have a size grip to allow the user to resize the shape.
  • Cloning:
    The shape should be able to be cloned; that is copied, to allow the copy and paste feature.

Background

Here are some refreshers of techniques we use in this system:

  • Painting

We will rely on custom painting in most situations. Therefore, well request help from System.Drawing classes specially the System.Drawing.Graphics class.

In some shapes like those mimic existing Windows controls (like the text box and the check box,) we will get help from classes in System.Windows.Forms.VisualStyles namespace to reflect the visual styles of Windows in our controls. In addition, System.Windows.Forms.ControlPaint class is used for drawing borders and selection rectangles.

  • Painting Quality

Every control in our project (sheet, rulers, and shapes) has a paining quality property that determines the quality of the drawing (low, medium, and high.) For this to work we will make use of some properties of the System.Drawing.Graphics object like those related to smoothing and anti-aliasing feature.

  • Serialization

All serialized objects should be marked with System.SerializableAttribute attribute. With help from System.Runtime.Serialization namespace we could customize the serialization process.

The core interface that would allow us to customize the serialization process is the System.Runtime.Serialization.ISerializable interface (implemented in all serializable classes.) Notice that, we should add the deserialization constructor to get correct deserialization.

  • Database

The database is a SQL Server client database with all operations included as stored procedures.

The system uses two-tier architecture; means that it accesses the database directly using just three objects, a connection, a command, and a data reader.

  • Design-Mode Support

For extending design-mode support we have created custom designer service for the Sheet class that inherits the System.Windows.Forms.Design.ParentControlDesigner class to allow nested controls in the Sheet object in the design-mode.

Code Highlights

In this section we will talk about significant blocks of code that would be of interest.

  • Painting Routines

The following is the code for OnPaint() routine overridden by only the ShapeBase class.

Listing 1 – ShapeBase.OnPaint() Method Code Listing
protected override void OnPaint(PaintEventArgs e)
{
    GraphicsManager.SetQuality(e.Graphics, PaintingQuality);

    ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, ButtonBorderStyle.Dotted);

    PaintShape(e.Graphics);

    if (Selected)
        DrawSelectionFrame(e.Graphics);

    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, GetResizeGripRect());
}

This function first calls the GraphicsManager.SetQuality() function that sets the quality attributes of the Graphics object. We will get back to this function in about a moment.

After that the function paints the control border using the System.Windows.Forms.ControlPaint class.

Next comes the interesting point. The function calls the virtual function PaintShape() that a derived class overrides to provide its own drawing routines.

For example, the TextBoxShape class has this PaintShape() override:

Listing 2 – TextBoxShape.PaintShape() Method Code Listing
public override void PaintShape(Graphics dc)
{
    base.PaintShape(dc);

    using (Brush b = new SolidBrush(this.BackColor))
    dc.FillRectangle(b, this.ClientRectangle);

    using (Pen p = new Pen(SystemColors.ActiveCaption, 1))
    {
        p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
        Rectangle r = this.ClientRectangle;
        dc.DrawRectangle(p, r);
    }

    Rectangle rect = this.ClientRectangle;
    rect.Offset(2, 2);
    rect.Width -= 4; rect.Height -= 4;

    StringFormat format = new StringFormat();
    format.LineAlignment = this.Alignment;
    if (this.RightToLeft == RightToLeft.Yes)
        format.FormatFlags = StringFormatFlags.DirectionRightToLeft;

    using (Brush b = new SolidBrush(this.ForeColor))
        dc.DrawString(this.Text, this.Font, b, rect, format);
}

After that the OnPaint() function draws selection frame if the shape is currently selected.

Just before the function closes, it calls the ControlPaint.DrawSizeGrip() function to draw the sizing grip that the user would resize the shape from.

  • Painting Quality

In our library Geming.Sisc.Infrastructure, we have created a helper class, GraphicsManager, which contains only one function, SetQuality().

This function accepts two input parameters, the graphics object and the quality you wish to set to that object. The following is the code for the SetQuality() function.

Listing 3 – GraphicsManager.SetQuality() Method Code Listing
public static void SetQuality(System.Drawing.Graphics g, PaintingQuality quality)
{
    if (g == null)
        throw new ArgumentNullException("g");

    if (quality == PaintingQuality.High)
    {
        g.CompositingQuality = CompositingQuality.AssumeLinear;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
    }
    else if (quality == PaintingQuality.Medium)
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.Bilinear;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
    }
    else
    {
        g.CompositingQuality = CompositingQuality.Default;
        g.InterpolationMode = InterpolationMode.Default;
        g.PixelOffsetMode = PixelOffsetMode.Default;
        g.SmoothingMode = SmoothingMode.Default;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.SystemDefault;
    }
}

Download

The application Geming.Sisc along with its code is available for download here.

Download Geming.Sisc

Moving a Form without the Title Bar

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Sample: Geming.Samples.FormDragging.zip

What is that?

Today, we are talking about how to move a form without its title bar.

You might have noticed that some applications with fancy UIs do not allow the user to move the window from its title bar. Honestly, some hide the overall title bar from the user. An example of these applications is Microsoft Windows Media Player -when in skin mode,- and Microsoft Windows Live Messenger. Both applications allow you to drag their windows using the client area not the title bar.

In this lesson, you will learn how to do this trick to move the form without its title bar.

At the end of this lesson, a sample application is available for download. It is a simple application with an outstanding UI illustrates how to move the form using its client area.

How?

Theoretically, you cannot drag any form without its title bar. However, we can simulate the dragging process which implies clicking with the left mouse button on the title bar and moving the cursor without releasing the left mouse button.

You can simulate this process using the SendMessage() function. This function is used to send a specific message (you can say command/order) to a specific object -specified by its handle.- This specific message can be attached with another messages (commands) to produce a new command. For example, in our lesson we will attach the message WM_NCLBUTTONDOWN with the message HTCAPTION to simulate the left mouse button clicking on the caption (title bar) of a window (form.)

Honestly, every object is a window. Every button -even the Close button- is a window, the menu item is a window, the status bar is a window, and the tooltip is another window!

The definition of SendMessage() is as following:

LRESULT SendMessage(
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam
    );

This function takes four arguments:

  • hWnd:
    The handle of the object (window.) to send the message to. Can be set to a window handle, HWND_DESKTOP (to send the message to the desktop,) HWND_BROADCAST (to send the message to all windows.)
  • Msg:
    The primary message that will be sent.
  • wParam:
    A secondary message to be attached to the primary message. Set to 0 to indicate NULL.
  • lParam:
    Another message that can be attached to the primary message. Set to 0 to indicate NULL. If wParam was not set, you cannot set lParam.

The return value of this function depends on the message sent. In our example, the function may return 0 if succeeded, or non-zero if failed.

It is worth mentioning that, in our example, another function must be called before SendMessage(); it is the ReleaseCapture() function. This function releases the mouse capture from a window (object) and restores the normal mouse processing. A window (object) that has captured the mouse receives all mouse input. Therefore, calling the ReleaseCapture() allows our form to release this mouse input (left-clicking) simulated.

The ReleaseCapture() function is very simple; its definition is as following:

BOOL ReleaseCapture(VOID);

This function returns TRUE if succeeded, or FALSE otherwise.

Let’s Code!

Now, we are going to put things together and mix them up.

The first step is to hide the title bar of the form. This can be done through the FormBorderStyle property of the form. You can set this property to FormBorderStyle.None to hide the toolbar and borders of the form.

The next step to take is to create the PInvoke methods for the functions. Here is the full code:

    // C# Code

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.I4)]
static extern int SendMessage(
    IntPtr hWnd,
    [param: MarshalAs(UnmanagedType.U4)]
    uint Msg,
    [param: MarshalAs(UnmanagedType.U4)]
    uint wParam,
    [param: MarshalAs(UnmanagedType.I4)]
    int lParam);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ReleaseCapture();

const uint WM_NCLBUTTONDOWN = 0xA1; // 161
const uint HTCAPTION = 2;
' VB.NET
Declare Function SendMessage Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, _
    ByVal Msg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Integer

Declare Auto Function ReleaseCapture _
    Lib "user32.dll" () As Boolean

Const WM_NCLBUTTONDOWN As Integer = 161
Const HTCAPTION As Integer = 2

Again and again, PInvoke stands for Platform Invocation; it is the CLR service allows .NET to call unmanaged code. This service requires special changes to the PInvoke method. Also, specific rules applied when PInvoking an unmanaged function.

The last code demonstrates the PInvoke methods for the functions, and the constants required.

The last step is to add the implementation code required to the MouseDown event of any control that you wish to allow the user to drag the form from. For instance, you can add the code to the MouseDown event of a PictureBox control that will act as the new and fancy title bar. In addition, you can add the code to the form’s MouseDown event to allow the user to drag the form from any point of its client area.

The following code demonstrates how to allow the user to drag the form from its client area:

    // Code

private void MainForm_MouseDown
    (object sender, MouseEventArgs e)
{
    // Releasing the mouse capture
    // to allow the form to receive
    // the order
    ReleaseCapture();
    // Ordering the form
    // Simulating left mouse button clicking
    // on the title bar
    SendMessage(this.Handle, // Form handle
        WM_NCLBUTTONDOWN, // Simulating the click
        HTCAPTION, // Attaching it to the title bar
        0); // No further options required
}
' VB.NET
Private Sub Form1_Load(ByVal sender As Object,ByVal e As EventArgs) _
    Handles MyBase.Load
    ReleaseCapture()
    SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
End Sub

It is better to check the return value of every call to determine whether the operation succeeded or failed.

A problem occurred!

It is wonderful to allow the user to drag the form from its client area. But, what if the user tried to drag the form from a label on the client area? It will not be dragged.

To overcome this situation, you can add the same code to the MouseDown event of the label. In this case, for better code maintenance and to avoid code repetition, you can define a method that contains the code, and call it from the MouseDown event of every control that you need to allow the user to drag the form from such as a label or a PictureBox. Another solution is to create a single MouseDown event handler for all controls that you are interested in.

Well! What’s next?

You can use this technique when creating applications with hot skins to allow the user to drag the form from its client area or from a specific control like a PictureBox that acts as the new hot title bar.

It is worth mentioning that you would better follow the guidelines when interoperating with unmanaged code. For example, PInvoke methods and constants should be declared inside an internal class called -in our example- SafeNativeMethods. In addition, it is better creating a wrapper method for the PInvoke methods. For example, instead of calling many functions to drag the form, you can create a single function named -for instance- MoveForm() that acts as a wrapper for the PInvoke methods. And that results in code that is more readable and easier to maintain. Plus, it prevents code repetition.

For more information about the functions, consult the MSDN documentation:

WOW! A code sample!

This is a very simple application that illustrates moving a form using its client area.

This sample created using Microsoft Visual Studio 2008 and .NET Framework 2.0.

The following is a snapshot from the application:

Moving Form without Title Bar Sample Snapshot

Download Here

Programmatically Taking a Screen Snapshot

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Honestly, this lesson is not primarily focusing on how to take a screen snapshot! Instead, it is focusing on how to simulate keyboard strokes and send them to the active application.

In .NET, this is done using the System.Windows.Forms.Forms.SendKeys class. As you might guess it is located in System.Windows.Forms.dll.

Using the SendKeys class you can call static methods like SendWait() to send the keystrokes and wait for them to be processed, or you can send them via the Send() method if you do not care about whether they processed or not.

Both Send() and SendKeys() requires a single argument keys. This argument represents the keys to send it.  Each key is represented by one or more characters. To get list of all values supported for this argument visit the documentation for the SendKeys class, visit this page.

For our example we will try to combine two keys Alt + Print Screen. Alt represented by a percent (%) sign. Print Screen key is represented by the value PrtSc enclosed in curly brackets.

// C#

public static Image TakeScreenSnapshot(bool activeWindowOnly)
{
    // PrtSc = Print Screen Key
    string keys = "{PrtSc}";

    if (activeWindowOnly)
        keys = "%" + keys; // % = Alt

    SendKeys.SendWait(keys);

    return Clipboard.GetImage();
}
' VB.NET

Public Shared Function TakeScreenSnapshot(activeWindowOnly As Boolean) As Image
    ' PrtSc = Print Screen key
    Dim keys As String = "{PrtSc}"

    If (activeWindowOnly) Then
        keys = "%" &amp; keys
    End If

    SendKeys.SendWait(keys)

    Return Clipboard.GetImage()
End Function

Programming Microsoft Agent in Windows Forms

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

App: Geming.PartIt.msi
Code: Geming.PartIt (Source).msi

Contents

Contents of this article:

  • Contents
  • Overview
  • Microsoft Agent Characters
  • Microsoft Agent SDK
  • Microsoft Agent Windows Forms Support
  • Loading Characters
  • Ordering the Character to Speak
  • Moving the Character
  • Character Animation
  • IAgentCtlCharacterEx Properties
  • AxAgent Control Events
  • Creating a Custom Popup Menu
  • Creating a Microsoft Agent Controller
  • Freeing up Resources
  • The Last Word
  • Microsoft Agent Compatibility
  • Where to Go Next?
  • A Real-World Example

Overview

Microsoft Agent is an unprecedented technology to create innovative, new conversational interfaces for applications and Web pages. It provides powerful animation capability, interactivity, and versatility, with incredible ease of development.

Microsoft Agent is a technology that provides a foundation for more natural ways for people to communicate with their computers. It is a set of software services that enable developers to incorporate interactive animated characters into their applications and Web pages. These characters can speak, via a Text-to-Speech (TTS) engine or recorded audio, and even accept spoken voice commands. Microsoft Agent empowers developers to extend the user interface beyond the conventional mouse and keyboard interactions prevalent today.

Enhancing applications and Web pages with a visible interactive personality will both broaden and humanize the interaction between users and their computers.

There are a limitless number of roles and functions that developers can create for these personalities to perform.

  • A welcome host could greet new users and provide a guided tour the first time a computer is turned on, an application is run, or a Web site is browsed.
  • A friendly tutor could lead someone through a task or a decision tree with instructions step-by-step along the way.
  • A messenger could deliver a notification or alert that a new e-mail has arrived and then offer to read it to you.
  • An assistant could perform tasks for you like looking up information on the Internet and then reading it out loud.

Although, this lesson focuses on Windows Forms, you can take advantage of Microsoft Agent in Web applications, and also XAML applications. For Web application, you can refer to the Microsoft Agent Platform SDK documentation. For XAML applications, it is the same as Windows Forms, except that you will need to add Microsoft Agent Control inside a Sysetm.Windows.Forms.Integration.WindowsFormsHost WPF control.

Microsoft Agent characters

Microsoft Agent characters can be found in various applications and Websites, including Office 2003 and its ascendants, it comes as the Office Assistant. Figure 1 shows the Clippit office assistant. And figure 2 shows Merlin the most widely-used Microsoft Agent character in Websites and applications.

Figure 1 - Clippit

Figure 1 - Clippit

Figure 2 - Merlin

Figure 2 - Merlin

There’re plenty of characters available for download. Though, Merlin is included by default in Windows. In addition, Office 2003 comes with many characters including Clippit.

Visit Microsoft Agent Ring for downloading plenty of characters.

Microsoft Agent SDK

Programming Microsoft Agent is very easy. But, in order to start programming you need to download the Microsoft Agent SDK. It’s available for downloadalong  with its documentation in the Microsoft Agent SDK download page on Microsoft. In addition, you need to download the Speech API and Text-to-Speech (TTS) Engine in order to enable the speech feature of Microsoft Agent. These packages are available in the Speech SDK in this page. The most recent version is 5.1.

The Text-to-Speech (TTS) Engine is used to translate text into voice.

It is worth mentioning that, if you need to extend speech feature of Microsoft Agent to support other languages other than English, you need to download your preferred language package from the Speech SDK page.

In addition, you are not end up using characters created for you. If you need to create your own characters you can download the Agent Character Editor and start using character creation.

Take into consideration that, for the application to run correctly on user’s machine, the user need to install two packages, Microsoft Agent and Microsoft TTS. Remember to inculde them as prerequisites in your application installer package.

After downloading and installing the Microsoft Agent SDK, you may notice that Microsoft Agent SDK offers you many components for many purposes. The most component that we are interested in is the ActiveX COM library AgentCtl.dll that contains the Microsoft Agent support features for Windows Forms. It contains the Microsoft Agent Control that’s the door for Microsoft Agent Windows Forms programming. This component -and many others- resides on %windir%MSAgent.

Microsoft Agent Windows Forms Support

Because AgentCtl.dll is an ActiveX component you cannot use it directly in your code. You need to create a RCW (Runtime-Callable Wrapper) component (assembly) that will act as a bridge between .NET and the COM component. This is done automatically while adding the ActiveX component control to your toolbox in Visual Studio. Another way is to use the AxImp.exe tool that comes with the .NET Framework SDK.

You might be wandering why we haven’t used the TlbImp.exe tool? Because this is an ActiveX COM component contains ActiveX controls. Therefore, we used the AxImp.exe.

Because .NET and COM cannot call each other directly, two types of wrapper components exist in the space, RCWs (Runtime-Callable Wrappers) and CCWs (COM-Callable Wrappers). RCWs are .NET assemblies act as the bridge between .NET and COM. Conversely, CCWs are COM components act as the bridge between COM and .NET.

To use Microsoft Agent in our Windows Forms application, simply, we’ll start by adding the Microsoft Agent Control to our toolbox. Figure 3 shows the Choose Toolbox Items dialog in the COM Components tab.

Figure 3 - Adding Microsoft Agent Control into Toolbox
Figure 3 – Adding Microsoft Agent Control into Toolbox

This component contains a single control Microsoft Agent Control that will be added automatically to the toolbox. After dragging it to the form, two components will be added automatically to the project references, AgentObjects and AxAgentObjects. These are the interoperability RCW wrapper components for the COM component. AxAgentObjects is the wrapper for the control and its related types. On the other hand, AgentObjects component is the wrapper for other types in the COM component. You might have noticed that a square-shaped control of type AxAgentObjects.AxAgent added to the form. This control will not be visible at runtime. Figure 4 shows the form.

Figure 4 - Microsoft Agent Control
Figure 4 – Microsoft Agent Control

AgentCtl uses STA (Single-Threaded Apartment) threading model. Means that the COM component supports -and bounds- only to a single thread.

To enable STA for your application you need to adorn the Main() function with the STAThreadAttribute attribute. Conversely, MTA (Multi-Threaded Apartment) threading model means that the COM component supports and can be accessed by multiple threads.

If neither STAThreadAttribute nor MTAThreadAttribute attributes has not been set, the threading model by default is MTA. Another way to set the threading model is the System.Threading.Thread.SetApartmentState instance (opposite of static) method.

To know whether a COM component supports STA, MTA, or both threading models, you can check the registry value ThreadingModel on HKCRCLSIDInprocServer32.

Loading Characters

After adding the AxAgent control to the form, you can now order the control to load the character(s) and show it.
AxAgent control contains a property Characters that’s of type AgentObjects.IAgentCtlCharacters that acts as a dictionary collection contains the loaded characters and its names.

For loading a character we use the Load() method of the Characters collection. For getting the loaded character we use the Character() method that returns an object of type AgentObjects.IAgentCtlCharacterEx that represents the loaded character.

Here’s the code for loading the character Merlin and showing it:

// Here I'll specify the character name
// and the character location.
this.axAgent1.Characters.Load(&quot;Merlin&quot;,
    @&quot;C:WindowsMSAgentcharsmerlin.acs&quot;);

AgentObjects.IAgentCtlCharacterEx character = this.axAgent1.Characters.Character(&quot;Merlin&quot;);

// A single argument contains a value to whether
// to animate the character while showing it
// or skipping the animation.
// You should set this to null or false.
// Set it to True to skip the animation.
character.Show(null);

In addition to Load() and Character() methods of the IAgentCtlCharacters object, you can use the Unload() method to unload a given loaded character.

Also IAgentCtlCharacterEx supports the Hide() method for hiding the character, that’s opposite to the Show() method.

Ordering the Character to Speark

After creating the character and showing it you can order it to speak whether a specific text using the Text-to-Speech (TTS) Engine or a recorded audio file. The following code segment makes the character introduces itself using coded string first and a recorded audio file next:

character.Speak(
    &quot;Hello everybody, I'm Merlin. I'll guide you throuh the application windows.&quot;,
    null);

character.Speak(null, @&quot;D:Recorded Introduction.wav&quot;);

As you have noticed, the Speak() method can take either a text for the first argument or a file path for the second argument. Figure 5 shows Merlin while speaking the text.

Figure 5 - Merlin Speaks
Figure 5 – Merlin Speaks

Take a look at the Think() method, it is very similar to Speak().

Moving the character

You can move the character around the screen by using the MoveTo() method.

character.MoveTo(100, 100, null);

This method takes three arguments, the first and second argument is the location on the screen that you wish to move the character to. The third argument takes a value indicating the moving speed. If this argument is null, then the default value 1000 is used. If you specified the value 0 the character will move without playing an animation. Obviously, a value under 1000 will slow down the character move, and a value greater than 1000 will speed up the move.

I had reasons to merge this section with the animation section. But, I had reasons too  to put it in a single section, I think it’s a special type of animations!

Character Animation

Every character has its own animations. Although, many characters share many animation names but that’s not required.

Every animation has a name. And to get the animation names that a character supports, you need to dig into the AnimationNames property that’s of type AgentObjects.IAgentCtlAnimationNames which implements the System.Collections.IEnumerable interface.

The following code enumerates the animation names of the character:

IEnumerator enumerator = character.AnimationNames.GetEnumerator();
while (enumerator.MoveNext())
    this.listBox1.Items.Add(enumerator.Current.ToString());

Now, it’s time to have some fun with the character. Try the following code:

character.Play(&quot;DoMagic1&quot;);
character.Speak(&quot;I'm better than Harry Potter. Am not I?&quot;, null);

Figure 6 shows Merlin speaking while doing some magic. (Proud Merlin)

Fogure 6 - Merlin Does Magic
Fogure 6 – Merlin Does Magic

Be aware that animation names that contain directions like left and right, it’s the left and right of the character not you.

In addition to the Play() method, the character object supports two methods for stopping playing animations, Stop() and StopAll(). Stop() stops the animation that’s specified by its single argument. StopAll() stops all animation of a specific type. This method has a single argument that can takes a string value of three “Move”, “Play”, and “Speak” to stop moving, animation or speaking commands. In addition to the three values, StopAll() can take a null value, and that means stopping all types of animations.

You might have noticed that many methods of the character object returns an object of type AgentObjects.IAgentCtlRequest. This type encapsulates a request to the character. If IAgentCtlRequest.Status is non-zero then the operation failed, otherwise succeeded.

Pay attention to the methods that return IAgentCtlRequest object, and the methods that requires it as an argument like Stop() method.

Another type of animation is gesturing to a specific point. Try this animation using the GestureAt() method.

IAgentCtlCharacterEx Properties

This list contains the most common properties of the character object (IAgentCtlCharacterEx):

  • AutoPopupMenu:
    A Boolean property indicates whether to allow the character pop-up menu or not. Pop-up menu is the menu that is shown when the user right-clicks on the character or its icon -if exists- on the taskbar. Default value is False.
  • Balloon:
    This is a read-only property that’s in turn contains read-only properties that indicate the characteristics like the back color of the balloon displayed while the character talking. This property contains many properties that most are self-explained from its names.
  • Left and Top:
    Controls the character location on the screen. (It is better using the MoveTo() method instead of these two properties if you want to play an animation while moving the character).
  • MoveCause:
    A read-only property returns a value indicates what caused the character’s last move. This property can return one of 5 values:

    • 0:
      The character has not been moved.
    • 1:
      The user moved the character.
    • 2:
      Your application moved the character.
    • 3:
      Another client application moved the character.
    • 4:
      The Agent server moved the character to keep it onscreen after a screen resolution change.
  • Pitch:
    A read-only property returns a value indicates the Pitch setting of the TTS.
  • SoundEffectsOn:
    Set this property to True to enable the sound effects, of False to disable it. Default is True.
  • Speed:
    A read-only property indicates the speed of the character. (Also there’re some operations that supports specifying the speed like the MoveTo() method).
  • VisibilityCause:
    A read-only property returns a value indicates the character’s visibility state. This property can return one of 8 values:

    • 0:
      The character has not been shown.
    • 1:
      User hid the character using the command on the character’s taskbar icon pop-up menu or using speech input..
    • 2:
      The user showed the character.
    • 3:
      Your application hid the character.
    • 4:
      Your application showed the character.
    • 5:
      Another client application hid the character.
    • 6:
      Another client application showed the character.
    • 7:
      The user hid the character using the command on the character’s pop-up menu.

Agent Server is the hard-core component that controls the Microsoft Agent behind the scenes. It controls all the characters loaded by your application and other applications and Websites. It’s the one which servers all orders for every Microsoft Agent character. Worth mentioning, it’s contained in the executable file AgentSvr.exe located on %windir%MSAgent. This file will be launched automatically when the first character loaded, and will shuts down automatically when the last character unloaded. After all, you can think about the Agent Server as the hard-core internal component of the Microsoft Agent.

AxAgent Control Events

An odd behavior of that control is that it defines events that related to the character and most likely to be located in the character object itself. Maybe it’s a feature to be located here because AxAgent controls the birth and death of the characters, but most of the time you will find that annoying.

The most widely-used events are:

  • BalloonShow and BalloonHide:
    Occur when a balloon of a loaded character shown or hid.
  • ClickEvent and DblClick:
    Occur when a loaded character clicked or double-clicked.
  • DragStart and DragComplete:
    Occur when a user tries to drag a loaded character.
  • ShowEvent and HideEvent:
    Occur when a loaded character shown or hide, whether the user who made this or your code. If you want to check what caused the character to be shown check the IAgentCtlCharacterEx.VisibilityCause. As a refresher, read the last section to know about this property.
  • MoveEvent:
    Occurs when a loaded character moved whether the user moved it or it’s moved by your code. See the IAgentCtlCharacterEx.MoveCause in the previous section if you want to know what caused the character to be moved.
  • RequestStart and RequestComplete:
    Occur when a request being restarted or after it completed. Note that many methods accept and many others return IAgentCtlRequest objects that defines requests sent to the server.

Creating a Custom Popup Menu

After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place.

First, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including “Hide” maybe) and complete the item event handlers.

Now, let’s get it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

AxAgentObjects.AxAgent agentCtl;
AgentObjects.IAgentCtlCharacterEx agentChar;

// initializing 'agentCtl'
// . . .

agentCtl.Characters.Load(&quot;Merlin&quot;, &quot;merlin.acs&quot;);
agentChar = agentCtl.Characters.Character(&quot;Merlin&quot;);
agentChar.AutoPopupMenu = false;

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// agentCtl.ClickEvent += agent_ClickEvent;

public void agentCtl_ClickEvent(object sender, AxAgentObjects._AgentEvents_ClickEvent e)
{
    if (e.characterID == &quot;Merlin&quot;)  // check for this if you have many characters
    {
        if (e.button == 2) // 1 = left, 2 = right
        {
            myContextMenu.Show(e.x, e.y);
        }
    }
}

Creating a Microsoft Agent controller

When dealing with Microsoft Agent, there’s recommended practices. One of is abstracting the Microsoft Agent implementation code from the UI code for achieving the maintainability and usability goals, and these goals can be achieved through a controller class that encapsulates the functionality that you need and provides easy access to lots of operations using a single call. A good controller class example is like this:

internal sealed class AgentController
{
    private AgentObjects.IAgentCtlCharacterEx _char;
    private AxAgentObjects.AxAgent _agent;

    public AgentController(AxAgentObjects.AxAgent agent,
        string characterLocation)
    {
        _agent = agent;
        _agent.Characters.Load(&quot;CHAR&quot;, characterLocation);
        _char = _agent.Characters.Character(&quot;CHAR&quot;);
        _char.Show(null);
    }

    public bool IsVisible() { return _char.Visible; }
    public void Animate(string animation, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.Play(animation);
    }
    public void Speak(string text, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.Speak(text, null);
    }
    public void MoveTo(int x, int y, bool stopAll)
    {
        if (stopAll)
            _char.StopAll(null);
        _char.MoveTo((short)x, (short)y, null);
    }
    // Encapsulating many operations into one
    public void Surprise()
    {
        _char.StopAll(null);
        _char.Play(&quot;Alert&quot;);
        _char.Play(&quot;Sad&quot;);
    }
}

Freeing up Resources

After finishing your work with Microsoft Agent you should first unload the character by calling AxAgent.Characters.Unload() method. Second, because all objects of the RCW (Runtime-Callable Wrapper) assemblies created earlier are just a wrapper around the unmanaged COM objects, they exhaust a great amount of unmanaged resources that can retain in memory for a long time, you strictly should always dispose these object when you have done with it.

Fortunately, AxAgent control inherits indirectly from the IDisposable interface, so you can easily dispose it by calling its Dispose() method. Conversely, most objects (like the character object IAgentCtlCharacterEx) in the RCWs do not implement this interface, so you need to do it the hard way and use the Marshal class to dispose it. Here’s the code that can do that:

while (System.Runtime.InteropServices.
Marshal.FinalReleaseComObject(_char) &gt; 0);

The FinalReleaseComObject() simple tries to decrement the references of the COM object to 0. You need to call FinalReleaseComObject() again and again until it returns 0, means that no references remain.

FinalReleaseComObject() tries to decrement the reference count to 0 in one time, ReleaseComObject() tries to decrement it gradually. So ReleaseComObject() needs more calls than FinalReleaseComObject(). and FinalReleaseComObject() is faster than ReleaseComObject().

Decrementing object’s references means disposing it.

You learned how to free-up the resources. But, there’s a question now. At which point should you dispose objects? It depends! The point at which you need to dispose objects depends on your use of objects. First, if you finished working with the AxAgent control and you do not want it any more till the user closes your application, it’s recommended that you dispose it as soon as possible. Otherwise, add the Dispose() call to the protected Dispose(bool) method of the form, so that it disposed when the form closes (honestly, disposes). The following code snippet demonstrates this:

protected override void Dispose(bool disposing)
{
    try
    {
        if (disposing)
        {
            this.axAgent1.Dispose();
            if (components != null)
                components.Dispose();
        }
    }
    finally { base.Dispose(disposing); }
}

This code strictly follows the disposing design guidelines.

You can step to the Dispose() method from the Members combo box in the top-right of the Code Editor window of the form.

A programming extremist like me (one of the Qaeda squad programming team :P) always disposes his objects even if the application is shutting down. To be honest, when the application shuts down all resources and memory managed by the application will be released immediately, so you actually do not need to release objects while the application shuts down.

On the other hand, for some objects like IAgentCtlCharacterEx you should take the Marshal class approach. But where to use it?

A great location for the FinalReleaseComObject() method (or ReleaseComObject() as well) is in the controller class. You can make your controller class implements the IDisposable interface and add your code inside the Dispose() method. Check the following example:

Code abbreviated for clarity.

internal sealed class AgentController : IDisposable
{

    ............

    public void Dispose()
    {
        _char.StopAll(null);
        _char.Hide(null);
        _agent.Characters.Unload(&quot;CHAR&quot;);
        while (System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_char) &gt; 0) ;
    }
}

You should dispose the controller class as soon as you finish using your character, or even with the form disposal along with the disposing of the AxAgent control.

The Last Word

Needless to say that you can take advantage of the Microsoft Agent and TTS technology to produce applications that provide friendly tutor for the user.

You are not end up using existing Microsoft Agent characters, you can create your own characters and animations using the Microsoft Agent Character Editor that’s available for download in the Microsoft Agent SDK download page.

Microsoft Agent Compatibility

Microsoft announced that Windows 7 would not be compatible with Microsoft Agent. However, you still can install the SDK and run your applications correctly.

Where to Go Next?

Good references for Microsoft Agent are:

  • Microsoft Agent SDK Documentation. Available for download with the SDK, see “Micrososft Agent SDK” section on the top.
  • Book: Microsoft Agent Software Development Kit.
    ISBN: 0-7356-0567-X
  • Book: Developing for Microsoft Agent.
    ISBN: 1-5723-1720-5

A Real-world Application

This lesson does not contain a sample project, it contains a real-world application, Geming PartIt! that’s used for parting large files into small pieces for merging them later, so you can send them via e-mail or storing them on CDs and Floppy Disks. This application uses Microsoft Agent character Merlin to provide a friendly guide for the user through the application.

Download the sample app!
Download the sample source!


It is my pleasure receiving comments and feedbacks about my application (and code of course.)