Adventures in WPF with MVVM
Recently at work I've been doing a bit of WPF development and been having fun with it. I was excited at the prospect of working with WPF since I've been getting into some C# development and I've been enjoying the language a lot. I figured it would be a nice addition to my repertoire since my last crack at GUI development was with the Qt framework in C++ a few years ago.
I was writing a new component to an already existing tool that has been implemented using the MVP (Model View Presenter) pattern. I was told by a senior developer to check out MVVM (Model View ViewModel) as in his opinion, that was a more correct pattern for WPF development. After a lot of reading and thinking, I definitely agree.
I read many articles on WPF, most notably Josh Smith's articles (specifically this one), found some great examples online, and got a pretty good understanding of the subject. I've really come to see the beauty and power of WPF, and I barely scratched the surface. Through databinding, we can create a truly thin view layer, that gives us the flexibility we need for a lookless ViewModel, much the same way we separate the "what" versus the "how" in html/css. Commands serve to enhance this decoupling, since in patterns like MVP or PM we see coupling by way of events. Commands free us almost completely from this coupling by use of the RelayCommand (also mentioned in Smith's article), so we have a much more generic way of dealing with the actions created by the GUI.
While my application utilized probably less than 5% of WPFs capabilities, I was still able to find a few things that really caused me problems, the main one being Scrollpanes.
Oh my god, what a mess. This was given to me as a suggestion near the end of my project because the user wanted to be able to shrink the window to have it always present on their screen as they were working. Stupidly, I agreed outright thinking it would be trivial to implement...what a mistake.
The use of Scrollpanes makes most WPF components go completely haywire. I had been using a GridLayout with min and max dimensions set, with a ListBox inside a panel inside the GridElement, but adding the scrollpanel to the main window causes the ListBox to stretch uncontrollably and prevent my GUI from looking anywhere near proper or useable. I played for a very long time to tame the controls but to no avail...alas I had to hack it.
I tried lots of different types of Panels instead of a StackPanel in my grid to limit the Listbox, but they all yielded the same result. I decided that since my ListBox sits inside of a Grid, I needed to bind the MaxHeight of that grid location to some other value in the control to keep the ListBox from growing or shrinking too much. The problem with this is that there is no element that you can bind straight to and get the exact height your looking for.
Enter the hack:
My height was just a tiny bit too big creating a weird always offscreen ListBox (in fact 33 pixels too large, which was the height of the label sitting above the ListBox). So I implemented an IValueConverter:
class HeightToAdjustedHeightConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter,
CultureInfo culture) { var height = (double) value - 33d; return height < 360d ? 360d : height; //360 being the minimum height allowed for the listbox } public object ConvertBack(object value, Type targetType,
object parameter,
CultureInfo culture) { return null; } }
All I did after that was include it as a converter for the binding on MaxHeight (Note you need to name your usercontrol and bind to its x:Name. NOT ideal):
<Grid Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" ClipToBounds="True" MaxHeight="{Binding ElementName=AdHocUserControl, Path=ActualHeight,
Converter={StaticResource HeightToAdjustedHeightConverter}}">
The only other alternative I could think of is to extend one of the panels and try to play with its growth behavior.
I spoke with a few other C#/WPF people and it seems like my hack isn't unheard of. Scrollbars are a pain everywhere apparently, you just have to make your peace with them. I'm sure there are lots of gotchas the further you move into the platform, its just a matter of uncovering them and documenting them for your fellow developers.











