October 2007 - Posts
The second series of Stumbling Through WPF is going to focus on something that should be very familiar to any web developer, but has long been missing in windows development (in an integrated fashion): Style. Style in WPF has been implemented similarly to CSS for web development, though one could make the case that WPF styles are even more powerful than CSS for a few reasons that we will get into later in this post. First off, I’d like to discuss the goal of this series. I ultimately want to create a separate project to house my styles, creating a ‘library of style’ (kinda sounds like a show on the E! channel, not that I’ve ever watched the E! channel of course) that can be consumed by any other WPF application. I’ve expanded a little bit on the transparent listbox style that was developed in my series of blogs on WPF transparency, and reorganized them into one XAML file called HotTrackListBox.xaml, which looks like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="HotTrackListBoxStyle" TargetType="{x:Type ListBox}" >
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value="True"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource HotTrackListBoxItemStyle}"/>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
</Style.Resources>
</Style>
<Style x:Key="HotTrackListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="ListBoxItem.BitmapEffect">
<Setter.Value>
<OuterGlowBitmapEffect GlowColor="AliceBlue" GlowSize="4"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ListBoxItem.BitmapEffect">
<Setter.Value>
<OuterGlowBitmapEffect GlowColor="AliceBlue" GlowSize="14"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}" />
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</ResourceDictionary>
I’ll do my best to explain each line of this style, but what it does is make a listbox transparent and multi-line (as in the previous transparency blog) and it also lights up the options as the user mouses over them, keeping the current selection brighter than the others.
Starting with the first node, we see that this entire style is a ‘Resource Dictionary’. This means that it can be referenced the same as any other dictionary, that is, each item will have a key which is used to retrieve its definition from the dictionary when it is needed. The usefulness of this will become more clear when we actually attempt to consume this style.
The next two lines, the xmlns lines, define the default namespaces that will be used throughout the style definition. Hopefully there is nothing new to you here.
Now we are getting into the guts of things with the ‘Style’ tag. Here, we define values for two attributes: Key, and TargetType. Key is how we will reference this style when it is being consumed, it identifies the style uniquely in this dictionary. TargetType allows us to narrow down what type of controls this style can be applied to. In this case, we are specifying that only types of ListBox can use this style.
The next four lines are ‘setters’, which simply set properties of the control to the values specified here. If you’ve followed my previous blogs on transparency, then the first two setters should look familiar, disabling the horizontal scroll bar and making the background transparent by default. The ‘Is Synchronized with Current Item’ property is something used by databinding and warrants a more detailed explanation , which will occur in future posts. It is here only because I continually forget to set it for my list boxes and wonder why databinding isn’t working. The final property setter is telling the style that any list items that appear will have the ‘Hot Track List Item’ style, which oddly enough is defined later in this very dictionary. Pretty cool that it doesn’t care which order you define things in. The next section, Style.Resources, should again look familiar from my previous posts on transparency. This is the part that makes the list selection transparent by overriding the highlight brush and control brush.
Next, we are defining another style with the key of HotTrackListItemStyle that can only be applied to ListBoxItems. The first section is two triggers, which can set values for list box item properties when certain conditions are met. In this case, we are capturing the ‘On Mouse Over’ event to set the ‘bitmap effect’ property to a new outer glow bitmap effect. Note that it is not necessary to turn off the glow in an ‘On Mouse Out’ event, it seems to be smart enough to do that for you. The second trigger gives the brighter outer glow bitmap effect with the item is selected. I’m no CSS expert, but this trigger stuff seems like something that, if possible, would be very difficult to implement in a CSS.
Finally, we have our TextBlock style definition that essentially sets all text blocks to wrap based on their parent’s scroll panel, which in this case, is all the text blocks rendering text in our list box. I feel that this was a bit of a hack, because won’t this style be applied to ALL text boxes, even those not a part of a list box? Time and testing will tell, but the alternative of defining a content template just blew my newbie mind at this time.
In the next post, I’ll get into creating our ‘Visual Foundation’ project that will house our library of style. I wanted to call the project ‘Presentation Foundation’, but some jerk already had dibbs on that name. I’ll also discuss how to reference styles from another project and use them to display our ‘hot track list box’ item defined above in any WPF application.
Finally, we are on the last leg of our transparency journey: Making the selector transparent. The way to do this is a little confusing, as we need to create a custom ‘SolidColorBrush’ (with a color of Transparent) and apply it to the listbox’s HighlightBrushKey as a resource. Thankfully, listboxes can have their own resources defined beneath them in the XAML, which override any default resources that may already exist. In our case, the following section under the XAML will do the trick and I’ll explain why afterwards:
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
</ListBox.Resources>
What this does is create a new transparent Solid Color Brush object that overrides the existing Solid Color Brush resource at the ‘Highlight Brush Key’ location. The result is as we’d expect, a transparent selection highlight:
I thought I’d be happy when I got this to work, but I’m not… there are so many more tools out there that can make a default list box really shine, and I feel like I’ve only scratched the surface here. Not to mention the fact that the way I’ve done this is not very reproducible short of copying and pasting all that code into every listbox, the correct way would have been to implement a ‘style’, which I will be delving into next. Here is the listbox XAML in its entirety:
<ListBox Margin="79,68,79,94" Name="listBox1" Background="Transparent" FontSize="16" Foreground="White" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
</ListBox.Resources>
<ListBoxItem>
<TextBlock Text="Me" TextWrapping="Wrap"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="You" TextWrapping="Wrap"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="Somebody else entirely" TextWrapping="Wrap" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}"/>
</ListBoxItem>
</ListBox>
There are now two issues remaining with my transparent list box: Multi-line list items (to remove that horizontal scrollbar) and a transparent or otherwise pretty-looking row selection indicator. The first task will probably be the easier one, so I’ll tackle that one first. While spinning through the available properties of a text block, I had noticed a property for ‘TextWrapping’… surely, that won’t work in a list box but what the heck, I’ll give it a go and define my list box like this (I made the text white as well, so it is more visible against the background):
<ListBox Margin="79,68,79,94" Name="listBox1" Background="Transparent" FontSize="16" Foreground="White">
<ListBoxItem>
<TextBlock Text="Me" TextWrapping="Wrap"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="You" TextWrapping="Wrap"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="Somebody else entirely" TextWrapping="Wrap" />
</ListBoxItem>
</ListBox>
Running the app now does… nothing. Absolutely nothing. I knew that was too easy, but maybe it isn’t that much further from this. Thinking about it, setting text to wrap would wrap it when it sees that it is at the end of a line… there is no width specified for these text blocks, so how would it ever know where the end of the line is? I want to specify the width of the text block to match the width of its container, the list box. Now I can do this manually, but then if the width of my list box changes I’d have to go and update the width of each list item. That is definitely not something I want to worry about, so I looked up how to bind the width of my text block to its parent width, so that they will always match. This is what I came up with:
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=ViewportWidth}"
Now there is a little bit of magic going on in here that I don’t yet entirely understand. Sure, it makes sense in that I am binding the value of width to the first ancestor of type ScollViewer’s ViewportWidth value, but I would assume that I would be binding to the first ancestor of type ListBox’s Width property, but this line:
Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Width}"
Doesn’t work. I’ll just have to accept it for now, because it seems to do what I want… almost:
Why is that darn horizontal scrollbar unnecessarily there? I want to turn it off entirely, which thankfully is a built-in property of the listbox:
<ListBox Margin="79,68,79,94" Name="listBox1" Background="Transparent" FontSize="16" Foreground="White" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
Success! Even the selection highlight, though ultimately not what I want, knows to select the multiple lines of text:
We left off last time with a simple form that has an image as its background. Not too impressive, and not what I set out to do with transparency! Let’s drag a listbox out there and plunk it down right in the center:
Ah, yes. That is exactly the look I want to avoid in this project. In order to see if our transparency is working, we’ll need to put a few items into the list. Normally, I would click it and use the properties explorer to find ‘Items’ and add to it that way, but I’ve already been disappointed by the property explorer one too many times so I will instead attempt it through the XAML. Logically, I would assume that list items would appear within the list box tag, so I open it up and see what intellisense tells me. That is one heck of a list of stuff that can be under a listbox, thankfully I spot ListBoxItem fairly quickly and add it. Now I was expecting the ListBoxItem to contain a text attribute, but one did not exist. Instead, I noticed a TextBlock attribute, which indicated to me that a ListBoxItem can be made up of, among other things, TextBlocks (WPF’s equivalent to labels in WinForms). So I add three ListBoxItems made up of text blocks to look like this:
<ListBox Margin="79,68,79,94" Name="listBox1" >
<ListBoxItem>
<TextBlock Text="Me"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="You"/>
</ListBoxItem>
<ListBoxItem>
<TextBlock Text="Somebody else entirely"/>
</ListBoxItem>
</ListBox>
Now that my list box is set up, let’s see what it takes to make it transparent. When trying to set the background of my window, I noticed that ‘transparent’ was an option there. Could it be so simple that I just tell the list box’s background to be transparent?
<ListBox Margin="79,68,79,94" Name="listBox1" Background="Transparent">
Holy cow, it worked!
I didn’t have to override a paint event or anything! I’m starting to like this WPF thing… However, we are not quite out of the woods. For one thing, my goal was to make a multi-line listbox which, if we increase the font size, we see that ugly horizontal scrollbar. Secondly, when we select an item, the whole transparency ‘coolness’ is obliterated by the default list box selection bar:
The next step is to correct both of those issues.
My Goal: A transparent, multi-line list box
My Tools: Visual Studio 2008 RC1
When a form has a nice background image, nothing is more visibly jarring then a big, white, clunky listbox plopped down right in the middle – especially if that listbox contains long text values giving it both a horizontal and vertical scrollbar. While it is possible to make a WinForms listbox that has transparency and multi-line list items, I’ve never been happy with the amount of work required and the end result just never seems quite right. Maybe there is a better way out there somewhere, but I’ve always had to make a custom control, override the paint event, capture the image behind the listbox, do some crazy stuff with scrolling, blah, blah, etc. Since WPF is supposed to make these fancy, cool-looking interfaces (that will likely have extravagant background images), let’s see if it makes it any easier to make transparent multi-line listboxes.
The first order of business after firing up Visual Studio 2008 RC1 is to create a new project to house my stumbling efforts. In the new project dialog, I see four project templates geared towards WPF: WPF Application, WPF User Control Library, WPF Browser Application, and WPF Custom Control Library. I am guessing that the ‘WPF Application’ template is a windows application utilizing WPF, so I’ll select that and name my project ‘StumblingThroughWPFPartI’, in the new ‘StumblingThrough’ solution:

So let’s see what this project template created for me… I see that my project has the normal ‘Properties’ section, and that it has added a few unusual references by default: ‘PresentationCore’, ‘PresentationFramework’, and ‘WindowsBase’. I’m guessing those are the key WPF assemblies. It also added to files to my project: App.xaml and Window1.xaml:

Now I realize that Window1.xaml is probably the default form that most new project templates give you, but what the heck is App.xaml? I’ll dig into that in future posts, I hope, but it doesn’t seem to have anything to do with my quest for the perfect transparent listbox as long as it is set up to run Window1 as the default window, which this line says it is:
StartupUri="Window1.xaml"
All right, now on to my window itself. First things first, I need a background to the window so I can tell if the listbox I eventually add is indeed transparent. I’ve scoured my hard drive, which is still a relatively clean install, and found ‘Greenstone.bmp’, which is probably one of the default windows backgrounds. I’ve copied it into my project folder and added it to the project:

Now the quest becomes referencing this image in the background of my window. My WinForms instincts have me click the window in the designer, and then look for a ‘Background’ or ‘Background Image’ property in the property explorer. Sadly, the ‘Background’ property seems to deal only with colors, not images. Looks like it isn’t going to be as easy as I thought… after searching a bit online, I’ve made the discovery that the ‘Background’ property of a window can be set to a ‘brush’ object, which can be pre-populated with the image that I want to paint. Now I see why this wasn’t available in the property explorer; I need to instantiate an object for the property value!
Again, my WinForms instincts (hereafter referred to as my ‘WinForms Sense’, so I can say ‘My WinForms Senses are tingling’) are telling me to make a form_load event handler, create the brush object in there and set the background property that way. However, I’ve already learned not to trust my WinForms senses so I look for a better way. Turns out that the window design is made up of XAML, or, eXtensible Application Markup Language. Beginners call it X-A-M-L, while those that think they know what they are talking about call it ‘Zamel’. Whatever you decide to call it, it is a rather powerful design tool that is practically another language in itself. I can tell I have a lot to learn here, but I did manage to find out how to set the window’s background equal to a new brush object all within the XAML, here is how:
<Window.Background>
<ImageBrush ImageSource="Greenstone.bmp" />
</Window.Background>
The ‘<Window.Background>’ tag is telling the XAML that whatever I say within that tag is the value for the Window’s Background property. This syntax is particularly useful when you want to set the value of a property to an object, as we need to do in this case. The <ImageBrush ImageSource="Greenstone.bmp" /> tag instantiates an Image Brush object, setting its source to our image file. This is that value then given to the window’s background property, and we can see the results immediately in the designer:

Well that took a whole heck of a lot longer than I thought it would, I haven’t even started the listbox yet! All I have achieved is a simple form with an image for a background, but that is why these blogs are titled ‘Stumbling Through’. I will get to that listbox next time, I need to absorb what I’ve learned here today.
Welcome to the first of what I hope will be many 'Stumbling Through' blog topics. The story behind the 'Stumbling Through' title is that, for the last four years, I have been a Microsoft developer trapped in an Oracle developer's body... that is, I have kept up with the main Microsoft technologies (like Visual Studio) in my free time but my actual work as a java/Oracle apps developer caused some of the cool new Microsoft technologies to fly under my radar. Now that I am back to my roots as a 'Microsoft Guy', I have the official charge of learning these technologies such as Windows Presentation Foundation (WPF) and Language Integrated Query (LINQ) and the best way for me to learn these things is to set unrealistic goals for myself and stumble through the technology until I make it achieve my goals. While I may not always come up with the most elegant or original solution, I hope that the experience will be of some interest to any readers out there. If nothing else, maybe some experts out there may be able to show me how my 10k line user control could have been done in one line of XAML and everybody would learn from that (most importantly, myself).
So that brings me to my first 'Stumbling Through' topic: Blogging. I've never blogged before; I've always had the intent to do so, but you know I [Insert Excuse Here]. So my goal for this first topic is to: Post a Blog. Perhaps that was a bit too small of a goal, because, er, I think I'm already done. I have a feeling my first few posts are going to be a little plain, but I'll stumble my way through some of the more advanced options as I blog on and if I come across anything that perhaps 99.9% of the blogging community doesn't already know about, I will share it here. One thing I am going to try and do is enable 'TextParts', so that any time I say Windows Presentation Foundation or Language Integrated Query, a link to their homepage will appear along with the full version of the acronym. Problem is, I won't know if it worked until I make the post (Preview doesn't seem to show it) so here we go...