How to deal with stubborn Silverlight (a.k.a. stubborn me)

Alrighty then… So here we are in this new decade of ours so I should start with a warm welcome! With a new decade comes new technology and this is shaping up to be no exception. To start things off right, I decided to bite into Silverlight. Let’s face it, Microsoft has had the old Windows Forms model for quite some time and they’ve made it pretty clear that they think WPF is pretty spiffy, and I tend to agree. Now, since the last major paradigm shift that I experienced was with ASP.NET back in the very early part of the last decade, I’ll let Silverlight kick off this decade. So, what’s the scoop today?

I decided to start with some small composite applications in Silverlight. The first of these was supposed to be simple. This is going to be tucked into my company’s internal CRM 4.0 and will simply aggregate notes from different sources and display them in the Silverlight user control. Now, I should mention that I already had this thing working using good old HTML, but I had to start somewhere!

The plan here was just to have a list box that would show the notes. Nothing crazy, should be quick right? Yes, and no. I hit speed bumps and you may too so I hope this helps. I started with the default bit of XAML:

<UserControl x:Class="MyNamespace.MainPage" 
   xmlns="... mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">    
  <Grid x:Name="LayoutRoot">
  </Grid>   
</UserControl>

Great! Let’s get started. So first I’ll need a ListBox, that’s easy enough. I’ll save you all the extra stuff you probably don’t need help with like styling gradients and such. So that gets us to here:

<Grid x:Name="LayoutRoot">   
   <ListBox />
</Grid>

Crazy exciting so far. If I plan on showing anything in this ListBox I’m going to need to add items. Now I’m serving up data from data from a database and I want to customize how the ListBoxItems are going to look, so I’ll need to create a template for that. Again, I’ll spare you all the visual fluff:

<DataTemplate x:Key="NoteTemplate">    
    <Border CornerRadius="10">    
     <Grid>    
      <Grid.RowDefinitions>    
       <RowDefinition />    
       <RowDefinition />    
       <RowDefinition />    
      </Grid.RowDefinitions>    
      <TextBlock Text="{Binding Category}" Grid.Row="0" />    
      <StackPanel Orientation="Horizontal" Grid.Row="1">    
       <TextBlock Text="Created on " />    
       <TextBlock Text="{Binding CreatedOnDate}" />    
       <TextBlock Text=" at " />    
       <TextBlock Text="{Binding CreatedOnTime}" />    
       <TextBlock Text=" by " />    
       <TextBlock Text="{Binding CreatedBy}" />    
      </StackPanel>    
      <TextBlock Text="{Binding Description}" Grid.Row="2" />    
     </Grid>    
    </Border>    
  </DataTemplate>   

OK, so now we’ve got the DataTemplate defined, let’s reference it on our ListBox:

<ListBox ItemTemplate="{StaticResource NoteTemplate}" />   

OK, that should do it. Let’s take a look:

image

Hmmm… not quite what I was expecting. I didn’t specify a width, so I would expect it to stretch. Alas, no such luck. As it turns out, the default style for a ListBoxItem sets the HorizontalContentAlignment to Left but we want it to stretch. So, we’ll have to define a style on the ListBox’s ListBoxItem in order to set it to stretch. How do we accomplish that? First, let’s define the style:

<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">   
  <Setter Property="HorizontalContentAlignment" Value="Stretch" />   
</Style>   

Now that we have the style defined. Since we’re instantiating the ListBoxItems programmatically through Data Binding, we can’t set the style directly on the ListBoxItem’s XAML because it’s not there. However, we can get to it through the ListBox’s ItemContainerStyle property.

<ListBox ItemTemplate="{StaticResource NoteTemplate}" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" />   

OK, let’s take another look:

image

What the heck? Now it’s all stretched, but way too far! How can we solve that? My initial guess is that we just need some wrapping. It’s the description TextBlock that has too much text and is pushing us way out in right field. So within short order I add TextWrapping to the TextBlock like this:

<TextBlock Text="{Binding Description}" Grid.Row="2" TextWrapping="Wrap" />

Wow, for some reason that did absolutely nothing! Frustrating to say the least. After trying several other things I’m having trouble finding the solution, so I go and talk to my friend Bing. He points me to lots of message boards where people have the same issue, but they always seem to end 1 of 2 ways. Either they want you to specify the width or they never actually come to a solution. Now, if you are in a situation where you can specify the width, then you’re set. It will happily wrap the text for you. In fact, during my testing it appears that you can also set the MaxWidth and that will also work if you have that luxury. Unfortunately, I didn’t. I don’t know how big or how small this thing is going to be, and I don’t want to constrain the users.

So here’s the secret that worked for me. After reaching that point of too much frustration and not enough sleep, I changed 1 property on the ListBox and it did the trick. The ListBox has an attached property of a ScrollViewer. It puts horizontal and vertical scrollbars on the edges of your control for you. The ScrollViewer has a property called HorizontalScrollBarVisibility. In a fit of trial and error, I set that to Disabled and voila!

<ListBox ItemTemplate="{StaticResource NoteTemplate}" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />

Suddenly I’m right where I want to be:

image

It seems that setting the ScrollViewer’s HorizontalScrollBarVisibility to Disabled works its magic in the rendering enough to realize that without a horizontal scroll bar and with text wrapping that the actual rendered width is the same as the parent ListBoxItem’s width. Here’s the code in total that I used:

<UserControl
    xmlns="<a href="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >http://schemas.microsoft.com/winfx/2006/xaml/presentation</a>"
    xmlns:x="<a href="http://schemas.microsoft.com/winfx/2006/xaml" >http://schemas.microsoft.com/winfx/2006/xaml</a>"
    xmlns:d="<a href="http://schemas.microsoft.com/expression/blend/2008" >http://schemas.microsoft.com/expression/blend/2008</a>"
    xmlns:mc="<a href="http://schemas.openxmlformats.org/markup-compatibility/2006" >http://schemas.openxmlformats.org/markup-compatibility/2006</a>"
    x:Class="MyNamespace.CommentsRollup" mc:Ignorable="d" Loaded="UserControl_Loaded">
 <UserControl.Resources>
  <DataTemplate x:Key="NoteTemplate">
    <Border CornerRadius="10">
     <Grid>
      <Grid.RowDefinitions>
       <RowDefinition />
       <RowDefinition />
       <RowDefinition />
      </Grid.RowDefinitions>
      <TextBlock Text="{Binding Category}" />
      <StackPanel Orientation="Horizontal" Grid.Row="1">
       <TextBlock Text="Created on " />
       <TextBlock Text="{Binding CreatedOnDate}" />
       <TextBlock Text=" at " />
       <TextBlock Text="{Binding CreatedOnTime}" />
       <TextBlock Text=" by " />
       <TextBlock Text="{Binding CreatedBy}" />
      </StackPanel>
      <TextBlock Text="{Binding Description}" Grid.Row="2" TextWrapping="Wrap" />
     </Grid>
    </Border>
  </DataTemplate>
  <Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
   <Setter Property="HorizontalContentAlignment" Value="Stretch" />
  </Style>
 </UserControl.Resources>
    <Grid x:Name="LayoutRoot">
     <ListBox ItemTemplate="{StaticResource NoteTemplate}" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
    </Grid>
</UserControl>

I sure hope this saves you some frustration. If any of you Silverlight gurus out there know of a better way to accomplish this, please let me know the right way to do it. Thanks and enjoy!

Advertisements

10 thoughts on “How to deal with stubborn Silverlight (a.k.a. stubborn me)”

    1. That’s correct. As I understand it, the StackPanel is a different beast altogether and it only seeks to take up as much room as it needs, as it’s pretty hard to convince it to do otherwise.

  1. Awesome post. I read it with increasing excitement as all the steps I had taken were listed.
    And then the final step that solved it… 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s