Using the Silverlight 2.0 ProgressBar Control


I’ve had the chance to play around a bit with SL 2.0 recently and thought I’d share some discoveries using the ProgressBar control. It’s not quite so easy to use as you might initially think.

To illustrate this I’ve created a simple Page.xaml user control,

<UserControl x:Class="ProgressBarTest.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <StackPanel Background="White">
        <ProgressBar x:Name="progressBar" Minimum="0" Maximum="100" IsIndeterminate="False" Height="20" Margin="5"></ProgressBar>
        <TextBlock x:Name="progressText" Height="20" Text="" Margin="5" ></TextBlock>
        <Button Name="startButton" Content="Start" Width="100" Height="30" Click="Button_Click"></Button>
    </StackPanel>
</UserControl>

Which produces something that looks like,

image

There are a few articles (and in fact Microsoft’s help documentation itself) that simple state that all you need to do is to set the ProgressBar’s Value property and away you go. I’ve seen code snippets like this,

public partial class Page : UserControl
{
    public Page()
    {
        InitializeComponent();
    }
 
    private int _progressValue = 0;
 
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        progressBar.Value = _progressValue;
        progressText.Text = _progressValue + "% complete";
 
        _progressValue += 10;
        if (_progressValue > 100) _progressValue = 0;
    }
}

And, sure enough, each time you click the Start button the ProgressBar increments nicely. But try something a bit more realistic like,

public partial class Page : UserControl
{
    public Page()
    {
        InitializeComponent();
    }
 
    private void DoSomethingHard()
    {
        for (int i = 0; i <= 100; i = i + 10)
        {
            progressBar.Value = i;
            progressText.Text = i + "% complete";
 
            Thread.Sleep(500);
        }
    }
 
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        DoSomethingHard();
    }
 
}

and you find that nothing happens to the ProgressBar until the end of the DoSomethingHard loop when the ProgressBar suddenly shows 100% complete. Not so good.

It turns out that any (long running or otherwise) user code on the main UI thread will essentially suspend any updates to any user interface elements on the page (not only ProgressBar’s). Only when control has been returned to Silverlight will it process the batched UI updates. I’m sure there’s a great explanation for this but it’s a real pain. I remember this problem when I used to code in VB (old school, that is) and the way round it then was to call the DoEvents function that would given the application a chance to process things like UI updates during intensive operations. Unfortunately, there is no such equivalent in Silverlight. In fact,

The only way to update the user interface during an operation is to have that operation performed in a different thread to the user interface.

Updating ProgressBar with BackgroundWorker

The simplest way to achieve this is to use the BackgroundWorker class. It handles spawning a new thread for you and allowing you to respond to changes to progress.

public partial class Page : UserControl
{
    BackgroundWorker _worker;
 
    public Page()
    {
        InitializeComponent();
 
        // Set up the BackgroundWorker class to do the Hard work in another thread
        _worker = new BackgroundWorker();
 
        // For some bizarre reason we need to tell the BackgoundWorker that we will be
        // reporting progress!
        _worker.WorkerReportsProgress = true;
        
        // Wire up an event handler to respond to progress changes during the operation.
        _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
 
        // Let the BackgoundWorker know what operation to call when it's kicked off.
        _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
    }
 
    void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // This is the opportunity to update the controls on the main thread
        progressBar.Value = e.ProgressPercentage;
        progressText.Text = e.UserState.ToString();
    }
 
    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        DoSomethingHard();
    }
 
    private void DoSomethingHard()
    {
        for (int i = 0; i <= 100; i = i + 10)
        {
            // Report some progress - this will result in the ProgressChanged event being
            // raised
            _worker.ReportProgress(i, i + "% complete");
 
            Thread.Sleep(500);
        }
    }
 
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Start the BackgoundWorker thread to do the Hard Work
        _worker.RunWorkerAsync();
    }
 
}

Of course, in a real world scenario you probably aren’t running the long running process in the code behind of your control. So you won’t necessarily have access to the BackgroundWorker instance within your loop. Consider an example where you have implemented an MVP pattern.

 

Updating ProgressBar within an MVP Implementation

Consider you have a View interface defined as,

public interface IPageView
{
    event EventHandler DoSomethingHard;
}

This will be used subscribed to by the Presenter to know when to DoSomethingHard.

The Presenter is defined as,

public class PagePresenter
{
    // Define an event for the View to subscribe to to receive progress notifications
    public event EventHandler<ProgressChangedEventArgs> ReportProgressFromPresenter;
 
    public PagePresenter(IPageView view)
    {
        View = view;
        View.DoSomethingHard += new EventHandler(View_DoSomethingHard);
    }
 
    void View_DoSomethingHard(object sender, EventArgs e)
    {
        for (int i = 0; i <= 100; i = i + 10)
        {
            // If the View is listening...
            if (ReportProgressFromPresenter != null)
            {
                ReportProgressFromPresenter(this, new ProgressChangedEventArgs(i, i+"% complete"));
            }
 
            Thread.Sleep(500);
        }
    }
 
    private IPageView View {get; set;}
 
}

Note the Presenter has also defined an event that will be raised to notify the View of changes to it’s progress.

Our Page now provides a concrete implementation of the IPageView interface

public partial class Page : UserControl, IPageView
{
    BackgroundWorker _worker;
 
    PagePresenter Presenter { get; set; }
 
    public Page()
    {
        InitializeComponent();
 
        // Set up the presenter and subscribe to it's ReportProgressFromPresenter event
        Presenter = new PagePresenter(this);
        Presenter.ReportProgressFromPresenter += new EventHandler<ProgressChangedEventArgs>(Presenter_ReportProgressFromPresenter);
 
        _worker = new BackgroundWorker();
        _worker.WorkerReportsProgress = true;
        _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
        _worker.ProgressChanged += new ProgressChangedEventHandler(_worker_ProgressChanged);
    }
 
    /// <summary>
    /// When the Presenter tells us there's a change in progress we pass this on to the
    /// BackgroundWorker.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void Presenter_ReportProgressFromPresenter(object sender, ProgressChangedEventArgs e)
    {
        _worker.ReportProgress(e.ProgressPercentage, e.UserState.ToString());
    }
 
    void _worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        // This is the opportunity to update the main UI's controls
        progressBar.Value = e.ProgressPercentage;
        progressText.Text = e.UserState.ToString();
    }
 
    /// <summary>
    /// Raise the DoSomethingHard event for the Presenter to respond to.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // If our presenter is listening then let it know to start doing something Hard.
        if (DoSomethingHard != null)
        {
            DoSomethingHard(this, new EventArgs());
        }
 
    }
 
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // Start the backgroundworker thread to do the Hard Work
        _worker.RunWorkerAsync();
    }
 
    #region IPageView Members
 
    public event EventHandler DoSomethingHard;
    
    #endregion
}

So we’ve now separated the DoSomethingHard operation away from the user interface and it’s BackgroundWorker class. This makes the Presenter code easier to test against a mock View that may or may not even have a BackgroundWorker.

Remember that the consequence of using a separate thread to perform lengthy operations is that other user interface elements (like buttons) on the main UI thread will still be accessible while the operation is being performed. Unless you disable other controls appropriately you could have unexpected results.

kick it on DotNetKicks.com

author: Tokes | posted @ Tuesday, January 06, 2009 11:55 PM | Feedback (0)

Why Developers Don't Get Paid Like Soccer Players


I am in the middle of reviewing salaries for my team of 30 or so developers and couldn't resist a chuckle upon reading some recent posts on the subject. Soon Hui's post questions why great developers aren't paid 10 times more then their lesser colleagues. While Carlos Oliveira ponders a further question: if soccer players can earn so much why can't the mighty developer?

Neither of these posts, nor the comments within them, came anywhere near explaining this phenomenon. 

Let me start by clearing up why it is that soccer players can earn so much - soccer players don't get paid the amounts they do purely because of the skills they possess. image Let's face it, at the level they play they're all pretty damn slick. They do so because of the money they can earn for their clubs - this is driven by soccer fans who pay to see their teams play. If you can increase your team's fan base by recruiting a superstar player then this is worth paying for (just like rock stars making all their money from touring).

Kragen Javier Sitaker spectacularly missed the point by commenting that,

The second richest man in the world, much richer than any soccer player, is a programmer.

I'm assuming he means Mr Gates who is no more a programmer now than my mother (OK he's way more of a programmer than my mum but you'll see my point). He made his money by having a vision, great ideas, taking risks, good timing, great perseverance... and ultimately by being a great businessman; not by being a coding ninja.

So, let's get this straight. It's cold, heartless economics.

Developers get paid according to the VALUE their employer derives from their work

While a soccer player's value can scale wonderfully by their ability to attract more paying fans, a developers ability to increase their value is somewhat more limited. In fact, a developer (in a service company) who simply codes his heart out without sharing their knowledge and experience or interacting with clients, will not continue to earn more money regardless of the quality of their code. There is a (almost) deterministic ceiling based on the rate they are charged out, and their company's overheads (which for anything but the smallest company are way higher than you might think). There's a limit to what an employer can afford to pay a developer.

A developer in a product based company (like Microsoft or Google) may be able to fare better since the company's profits (earned by more than just the development team) may allow them to reward employees more generously.

So often determining someone's salary is a case of looking at the range of salaries from graduate to most senior and ranking people according to their individual value between these extremes.

It's also important to note that great developers know more than writing great code. In fact, their value is often increased more by the things they do outside of writing code (which as many people have commented on is notoriously difficult to measure). For example,

  • Calmness under pressure
  • Communication skills
  • Ability (and perseverance) to see a project through to the end without getting bored and asking to move to something more interesting
  • Ability to deliver what was asked for on time within budget
  • Ability to instil confidence in their company, team and clients
  • Ability to be able to make tough (sometimes technical) decisions decisively
  • Knowledge to apply the appropriate coding techniques/practices to a problem given the (sometimes frustrating) constraints of time, scope, requirements, budget...
  • Ability to share their knowledge and experience to other team members

Even if you were 10 times better at coding than someone else (which you'll never be able to prove) the amount you are remunerated is only partially influenced by this fact.

So be aware of how to increase your value (and hence salary) but don't expect to be paid like Beckham - you may be disappointed.

author: Tokes | posted @ Sunday, December 07, 2008 6:16 PM | Feedback (3)

Preventing Double Form Posts


Found this technique in a comment on this post and thought it warranted a repeat as it took a while for me to find it and I hadn’t seen this before (maybe I’m out of touch but humour me).

Most posts on this topic focus on disabling the submit button that causes the post. This works fine when you only have one submit control. But, if you have multiple buttons and/or link buttons that can cause a post and want to avoid potential conflicts if a user clicks a second submit control before the first one has been processed then this is really simply approach.

 

Assuming you can live with the fact that javascript must be enabled then all you need to do is to change your page from,

<form id="form1" runat="server" >  
    
    <!-- will cause a post, via a call to __doPostBack -->
    <asp:LinkButton ID="LinkButton1" runat="server" onclick="LinkButton1_Click">Do Something</asp:LinkButton> 
    
    <!-- will cause a post via standard HTML <input type="submit" ... -->
    <asp:Button ID="Button1" runat="server" Text="Do Something Else" onclick="Button1_Click" /> 
 
</form>

to this,

<form id="form1" runat="server" onsubmit="this.onsubmit=function(){return false;}; return true;" >  
    
    <!-- will cause a post, via a call to __doPostBack -->
    <asp:LinkButton ID="LinkButton1" runat="server" onclick="LinkButton1_Click">Do Something</asp:LinkButton> 
    
    <!-- will cause a post via standard HTML <input type="submit" ... -->
    <asp:Button ID="Button1" runat="server" Text="Do Something Else" onclick="Button1_Click" /> 
 
</form>

The onsubmit attribute is set to redefine itself once it has been executed on the first submit. Subsequent attempts to submit the form will be ignored as the redefined function simply returns false.

Once the form has been submitted for the first time and returns the original definition on the attribute is reinitialised and the form can be submitted again.

 

Nice and easy!

kick it on DotNetKicks.com

author: Tokes | posted @ Friday, November 14, 2008 8:16 AM | Feedback (6)

Adding WSDL Documentation to Your WCF Services


While WSDL is great to understand the syntax of a service – it’s operations, inputs and outputs - it’s not quite so good at gaining a semantic understanding. You can, however, provide textual context for a service and it’s operations through the <wsdl:documentation> element. The documentation element is allowed inside any WSDL 2.0 element and can contain any information you want.

It used to be so easy… using ASMX style services, you could partially control this element in code using the WebService and WebMethod attributes.