RandomNumber MarkupExtension

This is a really simple example of a custom MarkupExtension.  It generates a random number and then returns it as either an integer, string or double.  It turns out that its only semi-useful, though, because it doesn’t work well inside of a template.  This really illustrates how MarkupExtensions get used…it does generate a random number, but it only does so once.  So every time you use the template it gets the same value (not especially random).  This is because MarkupExtensions get evaluated by the parser at parse time.  Since the XAML in the template is only parsed once, you only get one random number.

This worked for my scenario because it was outside of a template.  I was just generating content and wanted to test some random data.  Inside of a template, you could write a ValueConverter (to use in conjunction with a binding) to accomplish the same thing.  If someone writes that, let me know…

One other thing that’s worth noting in the code below is that the Random is a static.  That’s good practice because for plenty of reasons, but one interesting thing side effect of letting it be an instance variable was that things got way less random.  I think that the Random class must seed itself with clock data when it gets instantiated.  Apparently these get instantiated faster than the granularity of the clock data so the random numbers end up coming in batches that all have the same value.

Here’s a usage example of the MarkupExtension version:

<Button>
  <Button.RenderTransform>
    <RotateTransform
       Angle="{l:RandomNumber Min=0, Max=360}" />
  </Button.RenderTransform>
</Button>

And here’s the class itself:

public class RandomNumber : MarkupExtension
{
    private static Random R = new Random();

    public override object ProvideValue(IServiceProvider sp)
    {
        double r = R.NextDouble();
        double value = Max - (r * (Max-Min));

        if (AsString)
        {
            if (IsInteger) value = (int)value;
            return value.ToString();
        }
        else
        {
            if (IsInteger)
            {
                return (int)value;
            }

            return value;
        }
    }

    private bool _AsString = false;
    public bool AsString
    {
        get { return _AsString; }
        set { _AsString = value; }
    }

    private bool _IsInteger = false;
    public bool IsInteger
    {
        get { return _IsInteger; }
        set { _IsInteger = value; }
    }

    private double _Max = 1;
    public double Max
    {
        get { return _Max; }
        set { _Max = value; }
    }

    private double _Min = 1;
    public double Min
    {
        get { return _Min; }
        set { _Min = value; }
    }
}

Leave a Reply