Browsing Posts tagged WPF

    There have been updates in the client since I last blogged about the SO client. Since the first version received some attention I thought there might be people who are just interested in downloading the client instead of building one so I quickly added some features, cleaned it up and upload the binaries on http://stackoverflowclient.codeplex.com

    In order to turn that sample into a working client I had to add another class called StackOverflowNotifier.

        class StackOverflowNotifier
        {
            event EventHandler LoggedIn;
            int PollInterval { get; set; }
            event EventHandler QuestionsReceived;
            event EventHandler RequestLogin;
            void Start();
            void Stop();
        }
    

    As you can see the class polls the stackoverflow active questions page periodically and downloads all the questions. The Start method, starts the timer that raises its ‘Tick’ event after regular intervals.
    It was important to use the System.Windows.Forms.Timer timer because it raises the tick events on the UI thread and you can not interact with the WebBrowser control from a non UI thread.

    The default polling interval is 2 minute as of now and when the questions are downloaded you are notified in system tray via a popup only if there are any questions received with ‘Interesting’ attribute set to true.
    Though the latest list of questions is updated in the list view so you can click on the tray icon to view the current list of questions.

    I found a very nice Tray notification control for WPF at http://www.hardcodet.net/projects/wpf-notifyicon which I used in the project. I used the FancyBaloon user control shipped in its samples and modified it so that it can be bound to a collection view so I can put the next/previous buttons and allow user to navigate through questions from the same popup instead of showing 10 different popups.

    The above mentioned minor re-factoring helped clean up the code in the MainWindow. I also fixed a couple of bugs and added a trace listener that shows popup on the tray if the client barfs on exception while parsing the html.

    Thats pretty much it. Go get your copy of the latest version and start answering some questions on SO

    Introduction

    If you’ve discovered this blog post via search engine most likely you want to build a StackOverflow desktop client. Someone once said ‘talk is cheap, show me the code’ so you can go straight to http://stackoverflowclient.codeplex.com/ and download the code sample that logs you into StackOverflow site and shows you all the latest posts in a WPF application.

    StackOverflow client as we know it is a popular site for asking programming related questions. One of the great things about this site is that it has a reputation system in which you get upvoted for the right answers and downvoted for the wrong answers. What this means is that people with higher reputation are most likely very smart people like Jon Skeet and Marc Gravell. However if you have low reputation this could also mean that you do not have time to visit the site more often and answer more questions. This is exactly why you would need a desktop client for SO. Without boring you with further introductory information lets get straight to the code.

    Design

    There are many ways in which you can build a client for SO

    1. Monitor the RSS feeds and show desktop popups (Easiest but not very useful)
    2. Log into the site using WebClient class and maintain the sessions (Harder way and can be easily detected and blocked unless done right)
    3. Put a Web Browser control on your form and simulate navigation on it, read and parse the html to extract information (Medium difficulty and can easily break with css/markup changes but they are not very frequent.)

    I chose the 3rd way of building the client.
    So essential ingredients for writing a client in WPF/C# for SO using the 3rd methods are as follows:

    A question on StackOverflow can be represented by a class with following definition

    class Question
    {
    public Uri Url { get; set; }
    public string Title { get; set; }
    public bool Interesting { get; set; }
    public int Votes { get; set; }
    public int Answers { get; set; }
    public int Views { get; set; }
    }

    All the information in this class can be easily populated by parsing the home page of the SO site.
    The bool interesting property tells if you’ve marked the tag as interesting on your SO profile which highlights the questions for you so you can easily find questions that have the same tags in which you’re interested.

    So for parsing the html of the page you will need a component in your code called StackOverflowParser.
    I’ll cut the implementation details here and just tell you what services each component provides.
    So the Parser currently has two methods

    public IEnumerable GetQuestions(string html){}
    public bool IsLoggedIn(string html){}
    

    Both the methods take html of a page and return you some information based on the parsed html (using HtmlAgilityPack).

    To determine that you’re logged in the IsLoggedIn just checks for a link to logout page in the html “/users/logout”. This link would only appear on a page if you’re logged in.

    To get the questions from the home page, the GetQuestions method finds all the divs with id “question-summary”. These divs contain all the questions and information related to them that can be parsed into Question object.

    Now that the basic parsing component is ready we need a component that uses this parser and navigates on the site to particular pages that have the relevant html on them.
    For this you need a StackOverflowNavigator component. This component currently has following methods:

    public void BeginNavigateToHome(Action callback){}
    public void BeginGetActiveQuestions(Action> callback){}
    public void EnsureLoggedIn(Action action){}
    

    And following events

    public event EventHandler RequestLogin = delegate { };
    public event EventHandler LoggedIn = delegate { };
    

    BeginNavigateToHome asynchronously navigates the WebBrowser control (which is passed in the constructor) to the Home Page and then executes the action that you’ve passed as callback.

    BeginGetActiveQuestions similarly navigates to the home page, parses out the questions and raises the callback that you’ve passed. This method also ensures that you’re logged in using EnsureLoggedIn method because ‘bool interesting’ property of Questions can only be populated if you’re logged in hence it checks if you’re not logged in then it raises the ReqestLogin event. The form on receiving this event should open up a window with the same WebBrowser control on it and make it visible because the navigator will have navigated the page to the login page of SO site.

    When the user enters his login/password SO redirect the person to the home page. The navigator constantly monitors the Web Browser control and on each navigation it checks where the user is now logged in and as soon as the user is logged in it raises the LoggedIn event. This is when you can hide the window because user is now logged in and soon your call back of activequestions will be raised because thats one of the pending tasks the navigator had to do.

    As soon as you get the callback with questions you can then iterate over the questions and show popups on desktop to notify the user about new questions.

    You will obviously need to keep last set of questions to make sure that you don’t show the popups repeatedly.

    The code sample on codeplex simply shows the active questions on WPF window in a ListBox control. Since Overroot is going to pursue some other ideas as our upcoming products you are free to take this project from here and build a complete SO client on it.

    If you want to contribute to this project contact hasan#overroot$com (Replace # with @ and $ with .)
    If you have any trouble understanding the code just leave a comment on this post or send me an email.

    Happy coding!