Tuesday, June 26, 2007

WPF and CSLA (and subclassing Panel)

From Rob's blog, I found a discussion about WPF support in CSLA.NET, specifically what to subclass for the containers.

I haven't played w/ the DataPanelBase, as it's discussed, but I'd be curious what Rockford would say about my discussion on subclassing panel.

If he's building a container to host arbitrary UI, but the user may want an arbitrary layout, I would push for Decorator. This would allow the user to specify any panel she wants.

A bit more Xaml is an okay trade-off to ensure a clean object model and straight-forward usage semantics.

As Rob mentions, Window and Page are great examples of generic containers that only support a single child.

StackPanel is particularly bad since it will give it's child an infinite measure/arrange in the vertical direction. This is really bad if you put in an ItemsControl because it will take up as much space as needed to render all of its items. This could both mess up the UI and slow things way down--virtualization doesn't help much if all of the items are visible.

Monday, June 25, 2007

Rob and I hang out too much (RE: a good URL for the bag-o-tricks)

All of an hour ago, I made a post trying to bring sanity to the problem of finding the latest bag-o-tricks.

Looks like Rob thought of this 4 days ago.

Great minds, right?

Or maybe I should just keep up on my blog reading.

RE: CompositionTarget.Rendering

I use CompositionTarget.Rendering a lot in the bag-o-tricks. (Last count: AnimatingTilePanel, Graph, ZapDecorator, and FlipTile3D.)

Rob posted to an internal discussion about how to use (and how not to use) the event. (I saw a link on WiredPrairie as well.)

Specifically, it's important to remember this is a timer that ticks all of the time--every frame--potentially 60 times a second or more.

Since it's a static event, it will keep firing even if control you use it in isn't on the screen any more.

Hence, it's critically important that you manage the use of this event appropriately.

  • Un-register the event when your control is unloaded. I actually add a handler for the Unloaded event in my controls to do this. If you don't do this, events will still be fired to your delegate, even if the control is removed from the visual tree! This is not only a perf hit, but could introduce crazy behavior, crashes, data loss, mass hysteria, etc.
  • Potentially un-register the event when the UI isn't visible. Like when the window is minimized. Be careful, though. Finding window state from a control could be ugly. You also may want to let your animation run its course even if it's not visible, which leads me to the final bullet...
  • Un-register when your animation "settles". Since I used the Rendering event for my physics models, I do this when I reach equilibrium. Once all of the forces have balanced out and stuff stops moving, I un-register the event (and make sure to re-register once something changes that will start things moving again).

Users don't like their CPU pegged (unless something really cool is happening). A user with a dead laptop battery is not a happy user.

Happy hacking!

Keeping track of the Bag-o-Tricks

As I update the Bag-o-Tricks, I wanted to keep one URL for people to check to make sure they have the latest hotness.

(This is a nod to Rob's Url Manifesto. While I pick on him a lot about this, I think it's a great way to think about the user model for the web.)

Anyway, I'll keep this page updated with the latest info on the bag-o-tricks. (And, hopefully, a description of each demo/sample when I get some free time).

I'll have some updates soon, I promise.

Tuesday, June 19, 2007

A great way to learn a programming language

I'm sure many have heard discussions about the DLR and IronRuby coming out of MIX07.

As a guy who cares about great developer experiences and great user experiences, I've also paid some attention to Ruby on Rails.

So last night, I thought I'd dig in.

Instant Rails seems to be a popular place to start. A one-stop-shop for Ruby + Rails + Apache + MySQL. All runs on Windows by just extracting a zip file. (The only hiccup I encountered was making sure that port 80 was available.)

After I got things running and clicked around a bit, I realized I was trying to learn Japanese by picking up a book and trying to cross reference concepts to a Japanese-English dictionary.

Not very effective.

What I needed was a "vocabulary" and "grammar" introduction. (In quotes, because I'm referring to my Japanese analogy and not programming language constructs.) 

After the obvious search term--"ruby tutorial"--I stumbled upon an amazing tool: Try Ruby.

Holy cow! A ruby interpreter in-browser. A great way to understand the language. I had a bit of trouble dealing with mistyped lines, but otherwise, it's an amazing tool.

More programming languages should be presented this way.

This is certainly worth a few hours on a Tuesday night.

Kudos to the developers. 

Monday, June 18, 2007

Ya'll reading the WPF SDK blog, right?

People are always looking for deep content on WPF. This is a great source: http://blogs.msdn.com/wpfsdk/

Carole is an SDK writer that frequently digs in with those of us on the engineering side to make sure the SDK blog is speaking the truth.

Check it out!

Sunday, June 10, 2007

Don't subclass a Panel, unless you're making a Panel

I have a tongue-in-cheek thing I say a lot about WPF compared to Windows Forms:

In Windows Forms, there are two ways to do everything: a good way and a bad way.

In WPF, there are ten ways to do everything: two that are amazing, 3 that are good, 1 that is bad, and 4 that suck.

My goal in this post is to help move people upstream towards amazing, specifically how and when to use Panel (and its subclasses).

Markup subclassing

Everytime you go into VS and create a new WPF project, you see markup subclassing. This is the Window x:Class="WPFApplication1.Window1" stuff. It allows you to associate a piece of XAML to a subclass you define in Code. There are four approved places you'll see this in WPF: Application, Window, Page, and UserControl.

More than once, though, I've seen clever people (both outside and inside Microsoft) take this approach one step further. They want to reuse a piece of UI several times. Maybe they have a Grid defined with a set of columns, rows, and controls defined. It's pretty easy to take that Xaml-defined Grid (with all of its XML child elements), put it in it's own Xaml file, slap on an x:Class statement and start rolling. Things work great.

Rock-n-roll, right?

Well, not really.

Inheritance for polymorphism

I read a great internal paper at MS once--written by one of those brilliant old-guard devs with decades of perspective. The point the author pushed: use inheritance only for polymorphism. Translated: when making MyGrid, only subclass Grid if you mean for people to treat your new MyGrid just like another Grid. Code (and a user, for the most part) that deals with Grid should be able to "deal" with MyGrid without any issues.

Where things break down

So let's say you define your BadGrid Xaml like this (with an associated BadGrid.xaml.cs file with properties, events, etc):

<Grid x:Class="PanelFun.BadGrid"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<
Grid.RowDefinitions>
<
RowDefinition/>
<
RowDefinition/>
<
RowDefinition/>
</
Grid.RowDefinitions>
<
Grid.ColumnDefinitions>
<
ColumnDefinition/>
<
ColumnDefinition Width="100"/>
</
Grid.ColumnDefinitions>

<
Label Grid.Row="0" Grid.Column="0" Content="_First Name" Target="{Binding ElementName=FirstName}" />
<
TextBox Grid.Row="0" Grid.Column="1" Name="FirstName" />

<
Label Grid.Row="1" Grid.Column="0" Content="_Middle Name" Target="{Binding ElementName=MiddleName}" />
<
TextBox Grid.Row="1" Grid.Column="1" Name="MiddleName" />

<
Label Grid.Row="2" Grid.Column="0" Content="_Last Name" Target="{Binding ElementName=LastName}" />
<
TextBox Grid.Row="2" Grid.Column="1" Name="LastName" />

</
Grid>

Looks okay, right?


Let's use it.

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PanelFun">
    <local:BadGrid/>        
</Window>

Run the app. No problem!


Now let's say a naive user (maybe someone in your company or a customer) takes your control and sees that it's a Grid. They write this Xaml:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PanelFun">

<local:BadGrid>
<
Grid.RowDefinitions>
<
RowDefinition/>
</
Grid.RowDefinitions>

<
Label Grid.Row="3" Grid.Column="0" Content="_Favorite Color"/>
<
TextBox Grid.Row="3" Grid.Column="1" />
</
local:BadGrid>
</Window>

Things compile fine. They are using BadGrid just like a Grid. No problem, right?


The problem, of course, is that you didn't mean people to use BadGrid like a Grid. You wanted them to use it like a simple Control. Weird things happen, dogs and cats living together, mass hysteria.


In this case, you get the user-created row inserted into your grid. If the user tries to put a name on the TextBlock, the compiler blows-up because of naming conflicts between the definition scope (in BadGrid.Xaml) and the usage scope (in the Window). The user has no clue why things blew up. (I'm not all that sure myself, actually.)


If you want to use markup subclassing, stay inside the fences


Application, Window, Panel, UserControl. It's pretty simple to paste your desired Xaml into a UserControl, with no unplanned weirdness.


If you want to extend markup subclassing further, we should talk to Rob. He's the Xaml guy. :-)


If you want to do any subclassing, pick the right base class


In general (even if you're not using Markup subclassing), make sure you "mean it" when you use inheritance in WPF (or any object orientated framework, for that matter). If you subclass a ContentControl, do you mean people to treat it like a ContentControl all the time? Same thing for ItemsControl, Decorator, and Panel.


When would I subclass a Panel?


Well, if you want to make your own panel, start with Panel. AniTilePanel from my bag-o-tricks is a good example.


You might want to subclass a bit deeper, but only in special cases.You could make OrderedStackPanel, where you introduce your own AttachedProperty: OrderedStackPanel.Order. This can be applied to items and your panel will order the items in the stack based on the property. The cool thing: without the property set, your panel should act just like StackPanel. This holds to the theory about inheritance and polymorphism.


Make Sense? Sound good? Hack on!

What are you doing to protect the Internet?

For more information: http://www.savetheinternet.com/

To promote the cause yourself: http://www.savetheinternet.com/=promote

Sunday, June 3, 2007

Jobs & Gates

We've heard a lot about the meeting of Steve Jobs and Bill Gates.

9 Parts, I'm through part 3...cool stuff, via YouTube.