Tuesday, May 27, 2008

A Major Silverlight PITA and Two Annoying 3.0 Limitations

Pardon my rant, but the thing I currently hate most about Silverlight (besides copious XML) is the Visibility property. Any sane framework would implement Visibility as a Boolean. Not Silverlight though. It’s creators in undoubted infinite wisdom implemented it as an enumeration. The values of the enumeration? There are two: Visible and Collapsed. Hmmm.

Of course this causes superfluous verbosity in common everyday code:

button1.Visibility = makeVisible ? Visibility.Visible : Visibility.Collapsed;

Or worse when things get a little more complex:

// don't display the panel if its button's aren't visible
panel1.Visibility = !(button1.Visibility == Visibility.Visible && button2.Visibility == Visibility.Visible) ? Visibility.Visible : Visibility.Collapsed;

Clearly this was done to keep Silverlight compatible with the Windows Presentation Foundation (WPF) which has three values in its enumeration property (Visible, Hidden, and Collapsed). But that’s just as ridiculous. Why WPF couldn’t use two properties, Visible (Boolean) and NotVisibleBehavior (enumeration) is beyond me.

It’s ok though, because .Net 3.0 gave me a cure to any Framework shortcomings: Extension Methods. A syntactic sugar cure for all my bitterness:

public static void SetVisible(this FrameworkElement element, bool visible) {
    element.Visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}

public static bool IsVisible(this Visibility visibility) {
    return visibility == Visibility.Visible;
}

Fantastic, now my "complex" example becomes:

// don't display the panel if its button's aren't visible
panel1.SetVisible(!(button1.IsVisible() && button2.IsVisible()));

Still not quite as nice as a Boolean visible property, but certainly doable.

3.0 Limitation #1, By Ref Extension Methods

But wait. Isn’t it best practice in Silverlight to use binding for these types of things? Separation of logic from presentation and all. So I should do:

<StackPanel Visibility="{Binding IsPanelVisible}">

And then:

public class DisplayStuff : INotifyPropertyChanged {
    public
Visibility IsPanelVisible { get; private set; }

    public void UpdateStatus(bool makeVisible) {
        IsPanelVisible = makeVisible ? Visibility.Visible : Visibility.Collapsed;
        // make sure to notify the control that the property has changed
        PropertyChanged(this, new PropertyChangedEventArgs("IsPanelVisible"));
    }
}

And we can set the DataContext of some parent element to an instance of DisplayStuff and all the children including our panel magically databind. That’s cool, but the ugliness is back (well, not as bad since I removed the buttons to simply the example, but you can pretend). This is because we extended FrameworkElement not Visibility. No problem, just extend Visibility right?

public static void SetVisible(this Visibility visibility, bool visible) {
    visibility = visible ? Visibility.Visible : Visibility.Collapsed;
}

Except this doesn’t work. Can you spot the problem?

It compiles. It runs. But the value of IsPanelVisible never changes. Oh yea, C# is pass by value by default. And now the .Net Framework 3.0 limitation. This isn’t possible:

public static void SetVisible(this ref Visibility visibility, bool visible) {

You get "The parameter modifier 'ref' cannot be used with 'this'." Grr.

Limitation #2, By Ref Automatic Properties

Ok, so remove “this”, and go back to C# 2.0 helper functions which extension methods are syntactic sugar for anyway:

public static void SetVisible(ref Visibility visibility, bool visible) {

And now our class can do:

ExtensionMethods.SetVisible(ref IsPanelVisible, makePanelVisible);

Right? Not so fast I’m afraid. Compile error. “A property or indexer may not be passed as an out or ref parameter”. And I guess this is reasonable. You can’t pass the address of a function, which is what a property is in the background. So you should pass the private variable that backs the property.

Except that I don’t have one! I used an automatic property. And .Net doesn’t let me access the private variable backing the automatic property. So I’m stuck!

And this is .Net 3.0 limitation #2. Automatic properties are wonderful until you try to do much with them. Why couldn’t the framework notice that I’m using an automatic property and pass the variable that I can’t access by ref to my function?

And now I find myself back in a .Net 2.0 world because all the features I like so much in 3.0 are more sugar than substance.

Conclusion

Allowing automatic properties to pass by reference or allowing access to the private member behind them would be nice. Allowing extension methods to change the instance they extend would be nice. But ultimately none of this would be a problem if Visible had been implemented as a Boolean. The way every other framework in the world does. </Complaining>