Fooling around with the Office Assistant

I seem to have a special bond with ancient stuff. I were doing Pascal when C++ and WinForm was at their prime. Now I’m toying with a discontinued Microsoft Office feature. I just can’t help it, I have to work with Office 2003 around the clock since the upper IT management takes about a decade to certify a new software as “compatible”. Well, working for a big corporation has its pros and cons and we all have to cope with them.

I don’t feel the Office assistant intrusive though, it certainly doesn’t get in your way if you turn it off, but oh well, only when you know how. (Which, sadly, is not the case for many computer users). And I don’t think we can just let the woman take it from us. Gdzie jest czcionka? 😉

When I was in 7th grade or something. I read an article on PCWorld Vietnam about how to manipulate the Office Assistant. I diligently typed the code (in VBA) character-by-character without even understanding them :P. Nevertheless, the result was really satisfying, I were able to make the cat  (Links, an office assistant character) jump through hoops (literally).

Okay, enough trivia! I don’t remember any function from back then so I’ll have to start over. I have done various applications inter-operating with Microsoft Office’s VBA before, so I guess it would be an easy task. I added the Microsoft Excel’s object library to my  C# project and search for “assistant”. Lucky me, something popped up.

Assistant class in object browser

As you may see, it’s a child of the application class so you’ll have to start an Office application first. I chose Excel because it’s the application I have been working with the most. For aesthetics reasons, I tried to hide the Application’s window but that will hide the assistant too :(.  Apart from that, thanks to the well-built COM interface, initialize and display the assistant is easy.

using Microsoft.Office.Interop.Excel;
...
ApplicationClass Test = new ApplicationClass();
Test.Assistant.On = true;
Test.Assistant.Visible = true;

In case you are wondering, ApplicationClass is used to control Excel. All the possible action of “Assistant” is neatly listed within an enum so I just have to convert those to text and list them for the user to choose. No button-to-button editing for each action required!

 foreach (string TypeName in Enum.GetNames(typeof(Microsoft.Office.Core.MsoAnimationType)))
 {
     listBox1.Items.Add(TypeName);
 }

Because you can only see the Office Assistant when the host Office application has focus, I made a timer so the assistant will do the same action over and over, so the user can switch to the relevant application.

        private void timer1_Tick(object sender, EventArgs e)
        {
            Microsoft.Office.Core.MsoAnimationType[] Temp = (Microsoft.Office.Core.MsoAnimationType[])Enum.GetValues(typeof(Microsoft.Office.Core.MsoAnimationType));
            if (listBox1.SelectedIndex >= 0)
            {
                Test.Assistant.Animation = Temp[listBox1.SelectedIndex];
                Test.Visible = true;
            }
        }

"Get techy" with Rocky :p

You can also make the assistant say stuff you want:

 private void button1_Click(object sender, EventArgs e)
 // I created a button and a TextBox
 // When you enter some text into the TextBox and click the button, the
 // assistant will make a speech balloon with the text in it
 {
   timer1.Enabled = false;
   MessageBox.Show("Switch to Excel after clicking OK");
   System.Threading.Thread.Sleep(2000);
   Microsoft.Office.Core.Balloon Speech = Test.Assistant.NewBalloon;
   Speech.Heading = "Test balloon";
   Speech.Text = textBox1.Text;

   Speech.Mode = Microsoft.Office.Core.MsoModeType.msoModeModal;
   Speech.Show();
   timer1.Enabled = true;
 }

Bark!

So if you are still stuck with Office 2003 and wanted to play around with it a bit, here’s the compiled application (requires .NET 2.0 and Microsoft Office 2003 installed) and for those who want the code, here it is.

KTouch lession maker

Some of you might have landed on this article searching for the book instead, here it is 🙂

I have been trying to improve my typing by learning Dvorak and so far I’ve been to be able to type more accurately (I’m not talking about speed here :p). I have been using the Grass Soft touch typing program to train on Windows, it is pretty useful except for the limited word list. They claimed that they have included 500 most used word in English. That just doesn’t seem enough to me!

Besides, I have also started to use Linux and I needed a program that will keep me busy on the new platform. Of course in the open source world there’s always a lot of option too choose from, most of them are free too. I have chosen KTouch as it seems to be under active development and also endorsed by KDE.

KTouch with my customized lesson

So far, it’s a great program! It automatically detects your keyboard layout and select the appropriate starting lesson. It have two lesson for Dvorak: ABCD and computer generated. Despite the name, the computer generated lesson does not change as you take it because it’s just a file pre-rendered by the developer. I hit the same obstacle I did with the previous touch typing program: no variety.

Fortunately KTouch allows you to load external lesson files (they are just XML files) and text file; but due to poor application design, the text file won’t let you type the whole file but only the first few sentences. Which means if you want to practice with a long text, you’ll have to split them manually into multiple files.

<?xml version="1.0" encoding="utf-8"?><KTouchLecture>
<Title>Puzo, Mario - The Godfather.txt</Title>
<Comment>This is a lession created from Puzo, Mario - The Godfather.txt</Comment>
<FontSuggestions>Courier 10 Pitch</FontSuggestions>
<Levels>
<Level>
<LevelComment>level 1</LevelComment>
<NewCharacters>a lot :)</NewCharacters>
<Line>Chapter 1</Line>
<Line>Amerigo Bonasera sat in New York Criminal Court Number 3 and waited for</Line>
<Line>justice; vengeance on the men who had so cruelly hurt his daughter, who had</Line>
<Line>tried to dishonor her.</Line>
<Line>The judge, a formidably heavy-featured man, rolled up the sleeves of his black</Line>
<Line>robe as if to physically chastise the two young men standing before the bench.</Line>
</Level>
</Levels></KTouchLecture>

Structure of XML lesson files

So I made a program with C# to make KTouch lesson out of text files. Thanks to C#’s ability to process strings and XML, this has been a fairly easy task, here’s the code:

            const int LinePerLevel = 6;
            const int CharactersPerLine = 80;
            FileInfo Info = new FileInfo(Filename);
            string[] RawData = File.ReadAllLines(Filename, Encoding.Default);
            XmlTextWriter Writer = new XmlTextWriter(Filename + ".xml", Encoding.UTF8);
            Writer.WriteStartDocument();
            Writer.WriteStartElement("KTouchLecture");
            Writer.WriteRaw("rn");
            {
                Writer.WriteElementString("Title", Info.Name);
                Writer.WriteRaw("rn");
                Writer.WriteElementString("Comment", "This is a lession created from " + Info.Name);
                Writer.WriteRaw("rn");
                Writer.WriteElementString("FontSuggestions", "Courier 10 Pitch");
                Writer.WriteRaw("rn");

                Writer.WriteStartElement("Levels");
                Writer.WriteRaw("rn");
                {
                    int LevelCount = 1;
                    int LineCount = 0;
                    string[] LevelLines = new string[LinePerLevel];
                    string Buffer = "";
                    for (int i = 0; i < RawData.Length; i++)
                    {
                        string TrimmedLine = RawData[i].Trim();
                        if (string.IsNullOrEmpty(TrimmedLine))
                            continue;

                        TrimmedLine = TrimmedLine.Replace('“', '"');
                        TrimmedLine = TrimmedLine.Replace('”', '"');
                        TrimmedLine = TrimmedLine.Replace('’', ''');
                        TrimmedLine = TrimmedLine.Replace('‘', ''');
                        TrimmedLine = TrimmedLine.Replace("t", "");

                        if (Buffer.Length > 0 && (Buffer[Buffer.Length - 1] == '.' || Buffer[Buffer.Length - 1] == '?' || Buffer[Buffer.Length - 1] == '!'))
                            Buffer = Buffer + " " + TrimmedLine;
                        else
                            Buffer = Buffer + TrimmedLine;

                        while (LineCount < LinePerLevel && Buffer.Length > CharactersPerLine)
                        {
                            int CutOffSpacePos = Buffer.LastIndexOf(" ", CharactersPerLine - 1);
                            LevelLines[LineCount++] = Buffer.Substring(0, CutOffSpacePos);
                            Buffer = Buffer.Substring(CutOffSpacePos + 1);
                        }

                        if (Buffer.Length < = CharactersPerLine && LineCount < LinePerLevel)
                        {
                            LevelLines[LineCount++] = Buffer;
                            Buffer = "";
                        }

                        if (LineCount >= LinePerLevel)
                        {
                            Writer.WriteStartElement("Level");
                            Writer.WriteRaw("rn");
                            {
                                Writer.WriteElementString("LevelComment", "level " + LevelCount.ToString());
                                Writer.WriteRaw("rn");
                                Writer.WriteElementString("NewCharacters", "a lot :)");
                                Writer.WriteRaw("rn");
                                for (int j = 0; j < LevelLines.Length; j++)
                                {
                                    Writer.WriteElementString("Line", LevelLines[j]);
                                    Writer.WriteRaw("rn");
                                }
                            }
                            Writer.WriteEndElement();
                            LevelCount++;
                            LineCount = 0;
                        }
                    }

                    while (!string.IsNullOrEmpty(Buffer))
                    {
                        while (LineCount < LinePerLevel && Buffer.Length > CharactersPerLine)
                        {
                            int CutOffSpacePos = Buffer.LastIndexOf(" ", CharactersPerLine - 1);
                            LevelLines[LineCount++] = Buffer.Substring(0, CutOffSpacePos);
                            Buffer = Buffer.Substring(CutOffSpacePos + 1);
                        }

                        if (Buffer.Length < = CharactersPerLine && LineCount < LinePerLevel)
                        {
                            LevelLines[LineCount++] = Buffer;
                            Buffer = "";
                        }

                        if (LineCount >= LinePerLevel)
                        {
                            Writer.WriteStartElement("Level");
                            Writer.WriteRaw("rn");
                            {
                                Writer.WriteElementString("LevelComment", "level " + LevelCount.ToString());
                                Writer.WriteRaw("rn");
                                Writer.WriteElementString("NewCharacters", "a lot :)");
                                Writer.WriteRaw("rn");
                                for (int j = 0; j < LevelLines.Length; j++)
                                {
                                    Writer.WriteElementString("Line", LevelLines[j]);
                                    Writer.WriteRaw("rn");
                                }
                            }
                            Writer.WriteEndElement();
                            LevelCount++;
                            LineCount = 0;
                        }
                    }
                }
                Writer.WriteEndElement();
            }
            Writer.WriteEndElement();
            Writer.WriteEndDocument();
            Writer.Close();

It does:

  • Convert Unicode punctuations to their ordinary counterpart. For example “ and ” will be converted to “
  • Trim and combine different lines from different paragraphs to they’ll fit into the line format of KTouch. KTouch does not work well with long lines (you can’t see much toward the end of the line) so by default the line is trimmed at 80 characters

For a text source to practice, I decided to drop by project Gutenberg (FYI they digitize out-of-copyright books). They offer a range of about 3000 books in a dozen of languages to choose from. The books are mostly in plain text format, which is just perfect for this purpose.

The first novel I have chosen to type is Godfather by Mario Puzo 🙂 If you want to have a ride too then here’s the lesson maker’s source and here’s the lesson file for Godfather (note that due to bugs in KTouch, some characters toward the end of each lesson will disappear). The lesson file has over 2000 levels available for practicing.

Advanced C# splash screen

I once posted about how to make a splash screen with C#; yet it’s still a rectangle. While it’s true that you can replace my picture with something that have curvy edges, you’ll have aliased edges i.e. crooked and crude pixels around the edges.

Example of an aliased picture, the edges are not smooth and you can't see through the semitransparent parts

There’s a solution: alpha-blend the picture and you can see through the form; pixels with alpha value of neither 0 or 255 will be mixed appropriately with the background.

Alpha-blended picture. The edges are smoother and you can see through the form

To achieve this, you have to write your custom painting function with windows API, specifically this one

[DllImport("user32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, Int32 crKey, ref BLENDFUNCTION pblend, Int32 dwFlags);

Which can be a daunting task. Luckily we have someone from Visual C# kicks did the hard work and released the source for public use! But that’s not the end of my story just yet. I wanted the splash screen to fades in and out. The timing part was easy, just a timer and several more lines of code and it’s done.

        enum Phases {
            FadeIn,
            Hold,
            FadeOut
        };
        Phases CurrentPhase = Phases.FadeIn;
        int FadeInStep = 0;
        const int FadeInLast = 20;
        int HoldStep = 0;
        const int HoldLast = 80;
        int FadeOutStep = 20;
        const int FadeOutLast = 0;
        const int OpacityMultiplier = 5;

        private void timerFade_Tick(object sender, EventArgs e)
        {
            if (CurrentPhase == Phases.FadeIn)
            {
                if (FadeInStep < FadeInLast)
                {
                    FadeInStep++;
                    this.Opacity = (OpacityMultiplier * FadeInStep) / 100.0;
                }
                else
                    CurrentPhase = Phases.Hold;
            }
            else if (CurrentPhase == Phases.Hold)
            {
                if (HoldStep < HoldLast)
                {
                    HoldStep++;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    CurrentPhase = Phases.FadeOut;
            }
            else if (CurrentPhase == Phases.FadeOut)
            {
                {
                    FadeOutStep--;
                    this.Opacity = (OpacityMultiplier * FadeOutStep) / 100.0;
                }
                else
                    this.Close();
            }

I have even altered the custom paint code to utilize the form’s transparency factor

                //Set up blending options
                API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
                blend.BlendOp = API.AC_SRC_OVER;
                blend.BlendFlags = 0;
                blend.SourceConstantAlpha = (byte)(255 * this.Opacity);
                blend.AlphaFormat = API.AC_SRC_ALPHA;

                API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, this.BackColor.ToArgb(), ref blend, API.ULW_ALPHA);

But that didn’t work to my expectation! The form is fading in and out gradually but the alpha blending effect is lost.

Fugly!

After fiddling around, I figured out that the custom paint code somehow didn’t override all the necessary painting functions and the setter of this.Opacity call those functions every time it’s changed. Searching around the list of functions can be overridden I found nothing interesting

That function doesn't help...

So I created a new variable and leave the form’s default Opacity alone. After all, if I have adjusted transparency myself in the custom paint function, who needs .NET’s alpha-blend-incapable render? The code above becomes:

        double MyOpacity = 0;

            if (CurrentPhase == Phases.FadeIn)
            {
                if (FadeInStep < FadeInLast)
                {
                    FadeInStep++;
                    MyOpacity = (OpacityMultiplier * FadeInStep) / 100.0;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    CurrentPhase = Phases.Hold;
            }
            else if (CurrentPhase == Phases.Hold)
            {
                if (HoldStep < HoldLast)                 {                     HoldStep++;                     this.UpdateFormDisplay(this.BackgroundImage);                 }                 else                     CurrentPhase = Phases.FadeOut;             }             else if (CurrentPhase == Phases.FadeOut)             {                 if (FadeOutStep > FadeOutLast)
                {
                    FadeOutStep--;
                    MyOpacity = (OpacityMultiplier * FadeOutStep) / 100.0;
                    this.UpdateFormDisplay(this.BackgroundImage);
                }
                else
                    this.Close();
            }
        }
                //Set up blending options
                API.BLENDFUNCTION blend = new API.BLENDFUNCTION();
                blend.BlendOp = API.AC_SRC_OVER;
                blend.BlendFlags = 0;
                blend.SourceConstantAlpha = (byte)(255 * MyOpacity);
                blend.AlphaFormat = API.AC_SRC_ALPHA;

                API.UpdateLayeredWindow(this.Handle, screenDc, ref topPos, ref size, memDc, ref pointSource, this.BackColor.ToArgb(), ref blend, API.ULW_ALPHA);

And Voilà!

Alpha blended splash against my blog in the background, you can see through the semi-transparent form

You can get the project, source code and complied binary here.

Facebook block workaround (and no, it doesn’t involve DNS)

image1

The reason [Source]

It has been a bit hard to reach facebook lately. For some ISP like Viettel and FPT, a DNS change is all it takes to resolve facebook’s IP and restore access to the site. For some other, like EVN and VNPT, here’s what you got

image2

Changing DNS won’t work, which means the rats must have bitten something else. You are forced to use slower methods to overcome this, which could heavily impact your ability to play happy farm 🙁

That’s until I read that facebook still work with SSL (e.g when you replaces http in the address with https you are able to login), but it reverts to http the next time you click a link and you’ll have to do it again. This is especially annoying with games, which uses a lot of redirects to show their ads and stuff (damn, they are greedy).

And so I made something to do the hard work for me, so that I won’t miss the daily login bonuses :p. It’s utterly simple and a bit hard to use  but it does the job.

The code

Make a form that looks like this

image3

Write these to the form’s code

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Executes when a request is made, it will fix the protocol or anything in
        /// the address if the first check box is checked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void webBrowser1_Navigating(object sender, WebBrowserNavigatingEventArgs e)
        {
            string Address = e.Url.ToString();
            // Facebook uses some complicated redirection method so we'lll have to ignore this
            if (Address.Contains("redirectiframe.html"))
                return;
            // If it's not from facebook or facebook's cache, just ignore it
            if (!Address.Contains("facebook") && !Address.Contains("fbcdn"))
                e.Cancel = true;
            else
                if (checkBox1.Checked && Address.Contains(textBox1.Text))
                {
                    e.Cancel = true;
                    webBrowser1.Navigate(Address.Replace(textBox1.Text, textBox2.Text));
                }
        }

        /// <summary>
        /// Load facebook on start
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            webBrowser1.Navigate("http://www.facebook.com");
        }

        /// <summary>
        /// Fixes link after the document is loaded, may break some pages
        /// and creates an endless loop on login so it's not enabled by default
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
        {
            if (checkBox2.Checked)
                webBrowser1.DocumentText = webBrowser1.DocumentText.Replace(textBox3.Text, textBox4.Text);
            toolStripTextBox1.Text = webBrowser1.Url.ToString();
        }

        /// <summary>
        /// The go button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            webBrowser1.Navigate(toolStripTextBox1.Text);
        }

        /// <summary>
        /// Show/hide options
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripButton2_Click(object sender, EventArgs e)
        {
            groupBox1.Visible = !groupBox1.Visible;
        }

        /// <summary>
        /// Update load progress
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void webBrowser1_ProgressChanged(object sender, WebBrowserProgressChangedEventArgs e)
        {
            toolStripProgressBar1.Maximum = (int)e.MaximumProgress;
            toolStripProgressBar1.Value = (int)e.CurrentProgress;
        }
    }

Usage

Open options, check the first check box and you’ll be able to login, open applications and view photos. Should something breaks, try uncheck the check box and press Go to reload the page.

Choose no when IE asks you this, as images are loaded with http

image4

The second check box corrects the links and images on the page to https, only use this when images are not loading

Alternatively, you con use this to access facebook with its ip address. Try navigate to 69.63.184.143 and have it replace http://www.63.184.143 with https://69.63.184.143 (facebook removes the leftmost part before the first . and replaces it with www when you login)

Here’s the executable

Web page interoperability with .NET browser controls

Everyone knows that the best way to manipulate or extract data from a hypertext page is to use JavaScript. The langue is built entirely to serve that purpose so it’s natural to think about it every time someone tell you to get an element’s text or skip unnecessary screens. Firefox’s XUL even relies on the language to control the entire application’s behavior.

That method does well most of the time but it requires that you have access to the JavaScript source and can modify the page, which is not the case in some situation:

  1. Rapidshare makes you wait for two minutes or so when you want to get some file from them for free. It’s better than before when you have to go past some feline CAPTCHA but you still have to click on the download button after the timer expire, which means you can’t click on a Rapidshare link and leave it to download itself. Will Rapidshare allow you to edit the download page to insert the JavaScript that will click the button for you? No!
  2. You want to capture stock value from your finance information agency’s web site and save it into an Excel file for later reference, what should you do? Ask Yahoo Finance to modify its web page to send some fancy XMLRequest to your server every time it updates? And even if they do so you’ll have to make a listening server for those request on your side.

Of course there are other solution to those problem other than coding it on your own. There’s an extension for Firefox called Skipscreen that will automatically download the Rapidshare file for you or you con hire a better information provider that have built-in analysis functions on their site for you.

But if you are a control freak like me and want to make sure everything work exactly as what it is intended for then crawling through thousands of line of Skipscreen’s source code can be a bit of a pain (mind you that JavaScript is neither a strong typed or object oriented language so have fun visualizing variables and objects in your head). About the stock, can you be sure that your provider doesn’t tail gate your order? (e.g. You decide to buy some interesting stock and order them to do so, they find that stock interesting too and buy the same stock before actually place your order so they get the better price)

With Internet Explorer (e.g. the default browser control)

It’s simple actually, just create a browser control, use it’s Navigate(url) method for the page you want. For demonstration, I’ll try to get time from the text box on this page.

image

Here’s the code:

            HtmlElement IEElement1 = webBrowser1.Document.GetElementById("clockspot");
            if (IEElement1 != null)
            {
                textBox1.Text += "ie text 1: " + IEElement1.GetAttribute("value") + "rn";
            }
            HtmlElementCollection IEElements = webBrowser1.Document.GetElementsByTagName("input");
            if (IEElements.Count > 0)
            {
                foreach (HtmlElement IEElement in IEElements)
                {
                    if (IEElement.Name == "clockspot")
                        textBox1.Text += "ie text 2: " + IEElements[0].GetAttribute("value") + "rn";
                }
            }

This shows two ways you con search for a text box on the page. The first is by element ID. Actually, the textbox only have the name attribute but since this is IE and we have hideous rule all around, it will return the text box you want. The second way is to get all <input> elements on the Pago and check their name attribute.

Once you have found the textbox, get its “value” attribute. This value is updated in real time (the clock on the page is updated with JavaScript dynamically)

image

Side note: Running browser control in standard compliance mode

The first time you test the C# browser control  when you have IE8 installed may make you disappointed – it does not pass. This is because by default, the control run in quirks mode (this can break a lot of stuff). To force it to render the page in standard mode you’ll have to edit the registry, add this value:

[HKEY_CURRENT_USERSoftwareMicrosoftInternet ExplorerMainFeatureControlFEATURE_NATIVE_DOCUMENT_MODE]
"MyApplication.exe"=dword:13880

Replace MyApplication with your application’s executable name. Also if you are debugging in Visual Studio, add MyApplication.vshost.exe too.

With Firefox… or more exactly, XUL runner

XUL runner is the engine behind Firefox, it renders everything from web pages to Firefox itself. Being a cross platform platform (confusing isn’t it?), it will provide you with more cross-platform flexibility and may even save you from having to recompile the code. The is – if Mono is going somewhere.

XUL runner doesn’t work with C# out of the box, you’ll have to download Skybound’s  component – GeckoFX for that. This will give you a nice drag-and-drop component on the toolbox, but you are not done yet!

The basic component only provide basic functions like navigate, get/set for element text. To work with values contained in specific types of element you’ll need an experimental feature – DOM manipulation, which can be downloaded from here.

Add it to the original GeckoFX project, recompile it with the appropriate XUL version (1.8 or 1.9 or 1.9.1) and then you can use the code below:

            GeckoElementCollection FFElements = geckoWebBrowser1.Document.GetElementsByName("clockspot");
            if (FFElements.Count > 0)
            {
                GeckoElement Element = FFElements[0];
                GeckoInputElement Input = new GeckoInputElement(Element.DomObject);
                textBox1.Text += "ff text: " + Input.Value + "rn";
            }

The first step is similar to what you do with the IE control: search for the element. When it’s found, convert it to an input element (GeckoInputElement), then you can get its value. This is also updated in real time.

Conclusion

Due to time constraints, this post can only cover the very basic. You can explore other feature of the controls yourself – try replacing the “get” with “set” for example. Applications is endless. You can now make Rapidshare furious, dominate the stock market or use the internet itself for your data mining project!

Web application testing framework

This is an update section and is added on 10/07/2010

If you happened to use the .net Framework and wanted to test your web application’s behaviour in browsers, you have WaitN. Perhaps a bit of code from its first page will best illustrate what it does

[Test] 
public void SearchForWatiNOnGoogle()
{
 using (var browser = new IE("http://www.google.com"))
 {
  browser.TextField(Find.ByName("q")).TypeText("WatiN");
  browser.Button(Find.ByName("btnG")).Click();
  
  Assert.IsTrue(browser.ContainsText("WatiN"));
 }
}