avatar

Creating a custom glass listbox with XAML in WFP

by on Saturday, August 8th, 2009, under Computer Programming, Tutorials

WPF XAML Glass ListBox

WPF XAML Glass ListBox

Our next adventure into the Windows Presentation Foundation (WPF) will take us through creating a glass style for a listbox with XAML.  Much of what we will do here is an extension of our first XAML tutorial about creating a glass button.  I you haven’t done that tutorial, I would recommend it as it will explain a lot of the stuff we are doing here.    Our list box requires considerably more effort to change its look because we don’t only style a single control like a button.  We have to style the list box itself, the list box items, the scroll buttons and the “thumb” between them, and build the layout template for the scrollbar.   So, without further ado, lets get started!

Add a new Resource Dictionary to your project and name it “GlassListBox.xaml”.  Now, add the following style for our ListBox inside of your ResourceDictionary tags in the GlassListBox.xaml file.

  <Style x:Key="GlassListBox" TargetType="{x:Type ListBox}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ListBox}">
          <Border BorderBrush="#99FFFFFF" 
                  Background="#88000000" 
                  BorderThickness="4" 
                  CornerRadius="20,20,20,20">
             <ScrollViewer Margin="0" Focusable="false">
                <StackPanel Margin="10" IsItemsHost="True">
                </StackPanel>
              </ScrollViewer>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

This will be the background of our list box that contains everything. We added a ScrollViewer control to allow scrolling when the contents within the control go beyond the controls borders. A StackPanel is then placed inside the scroll viewer and this will be the container that holds any items we bind to the list (items like “Metroid”, “Zelda”, etc. will stack up in the stack panel).

Next, add the following style for the ListBoxItem control just below your ListBox’s closing Style tag (but still inside the ResourceDictionary tags).

  <Style x:Key="{x:Type ListBoxItem}" 
         TargetType="{x:Type ListBoxItem}">
    <Setter Property="Height" Value="66" />
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type ListBoxItem}">
          <Button Name="ListItem" 
                  Style="{DynamicResource GlassButton}" 
                  Width="Auto" 
                  Height="Auto">
            <ContentPresenter Name="listItemContent" 
                              Grid.RowSpan="2" 
                              HorizontalAlignment="Left" 
                              Width="300" 
                              Margin="0,0,0,0" />
          </Button>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

Our list box will contain buttons so a user could scroll through options and click on one to proceed to the next screen. We greatly simplify our code here by calling upon the style we created in the GlassButton XAML tutorial. The last thing we need to add is the ContentPresenter which will display our ListItem text, which in the picture above, is the name of a video game.

NOTE: You may need to manually adjust the ContentPresenter Width to suit your layout needs as I have not found a way to make the list items automatically expand with the control yet. If you know how to do this, please leave a comment so I can update this post and give you creds!

Next, we add in a third style for the scroll up and scroll down buttons in the scroll bar.

  <Style x:Key="ScrollBarLineButton" TargetType="{x:Type RepeatButton}">
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type RepeatButton}">
          <Border x:Name="RepeatButtonBorder" 
                    CornerRadius="14,14,14,14" 
                    BorderThickness="4,4,4,4" 
                    RenderTransformOrigin="0.5,0.5" 
                    BorderBrush="#88FFFFFF">
            <Border Background="#88000000"  
                    CornerRadius="14,14,14,14" 
                    x:Name="background">
              <Grid>
                <Grid.RowDefinitions>
                  <RowDefinition Height="0.4*"/>
                  <RowDefinition Height="0.6*"/>
                </Grid.RowDefinitions>
 
                <Path HorizontalAlignment="Center"
                      Grid.RowSpan="2"
                      VerticalAlignment="Center"
                      Stretch="UniformToFill" 
                      Margin="10,18,10,18"
                      Fill="White" 
                      Data="{Binding Path=Content,
                      RelativeSource={RelativeSource TemplatedParent}}" />
              </Grid>
            </Border>
          </Border>
          <ControlTemplate.Triggers>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property="RenderTransform" 
                      TargetName="RepeatButtonBorder">
                <Setter.Value>
                  <TransformGroup>
                    <ScaleTransform ScaleX="0.9" ScaleY="0.9"/>
                  </TransformGroup>
                </Setter.Value>
              </Setter>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

Most of this should look familiar if you have completed the GlassButton tutorial with the exception of the type of control used and how we display the controls contents. For the arrow buttons on the scroll bar, we are using a RepeatButton control. The actual contents of the button (the up arrow and down arrow) are set by using the “Path” tag (a control that lets us draw with coordinates, explained in detail later). The data attribute allows us to bind content in the ScrollBar ControlTemplate later. By doing this, we are able to use this same style for both buttons without hard-coding the contents.

Our next style is very simple since this style is designed to be invisible. I’ll explain below 🙂

  <Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type RepeatButton}">
          <Border Background="Transparent" />
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
Default Page Button

Default Page Button

This style is for the scroll bar page button, which is the area between the line buttons at the top and bottom, and the “thumb” (the button in the middle that you can click and drag). Its the area you click to scroll a page at a time. We want this to be transparent to give the appearance that you are clicking on the scroll bar itself, otherwise, it would appear that there is an actual button there which would be very unusual looking (see graphic on the right for a glimpse of the scroll bar without the transparent page button style applied).

Next, we define a style for the scroll bar thumb (the slider in the middle of the scroll bar).

  <Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
    <Setter Property="IsTabStop" Value="false"/>
    <Setter Property="Focusable" Value="false"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type Thumb}">
          <Border x:Name="Border" 
                          CornerRadius="14,14,14,14" 
                          BorderThickness="4,4,4,4" 
                          BorderBrush="#88FFFFFF">
            <Border Background="#88000000" 
                    CornerRadius="13,13,13,13" 
                    x:Name="background">
              <Grid>
                <Grid.RowDefinitions>
                  <RowDefinition Height="0.4*"/>
                  <RowDefinition Height="0.6*"/>
                </Grid.RowDefinitions>
 
                <Border Grid.Row="0" CornerRadius="13,13,0,0">
                  <Border.Background>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                      <GradientStop Color="#08FFFFFF" Offset="0"/>
                      <GradientStop Color="#88FFFFFF" Offset="1"/>
                    </LinearGradientBrush>
                  </Border.Background>
                </Border>
 
              </Grid>
            </Border>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

There is nothing new here that haven’t already covered, so lets move on to defining the scroll bar control template.

Our ControlTemplate will define how the scroll bar is built. It defines where the arrow buttons, page buttons, and the thumb controls are positioned, and it also establishes the content of the arrow buttons (the arrow image).

  <ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
    <Grid Width="60" >
      <Grid.RowDefinitions>
        <RowDefinition MaxHeight="60"/>
        <RowDefinition Height="0.00001*"/>
        <RowDefinition MaxHeight="60"/>
      </Grid.RowDefinitions>
      <Border Grid.RowSpan="3" CornerRadius="18" Background="#88000000" />
      <RepeatButton Grid.Row="0" 
              Command="ScrollBar.LineUpCommand"
              Style="{StaticResource ScrollBarLineButton}"
              Content="M 0 4 L 8 4 L 4 0 Z" />
      <Track Name="PART_Track" Grid.Row="1" IsDirectionReversed="true">
        <Track.DecreaseRepeatButton>
          <RepeatButton Style="{StaticResource ScrollBarPageButton}" 
                        Command="ScrollBar.PageUpCommand" />
        </Track.DecreaseRepeatButton>
        <Track.Resources>
          <!-- Thumb's minimum height is half of this metric -->
          <sys:Double x:Key="{x:Static 
                              SystemParameters.VerticalScrollBarButtonHeightKey}">
            120
          </sys:Double>
        </Track.Resources>
        <Track.Thumb>
          <Thumb Style="{StaticResource ScrollBarThumb}" />
        </Track.Thumb>
        <Track.IncreaseRepeatButton>
          <RepeatButton Style="{StaticResource ScrollBarPageButton}" 
                        Command="ScrollBar.PageDownCommand" />
        </Track.IncreaseRepeatButton>
      </Track>
      <RepeatButton Grid.Row="3"
              Style="{StaticResource ScrollBarLineButton}"
              Command="ScrollBar.LineDownCommand"
              Content="M 0 0 L 4 4 L 8 0 Z"/>
    </Grid>
  </ControlTemplate>

Our ScrollBar control template uses a grid with 3 cells. The first contains the “scroll up” RepeatButton, the second cell contains a Track control for page scrolling and the thumb control, and the third cell contains the scroll down RepeatButton. Take a look at the first RepeatButton defined in the grid, the scroll up button, and I will explain the three attributes we are setting:

  1. Style: The style we defined earlier as ScrollBarLineButton
  2. Command: ScrollBar.LineUpCommand tells the control that when we click this button, it should scroll the list up by the amount set in the “SmallChange” property of the control
  3. Path Markup Example

    Path Markup Example

  4. Content: M 0 4 L 8 4 L 4 0 Z will display an up arrow. I know what you are thinking… What the?!? Well, this is actually drawing a shape using XAML path markup! The letters M, L, and Z will define a starting point (M), create a line to the next point (L), and end the drawing by connecting the current point to the starting point (Z). So, we are starting our drawing for the up arrow with M at coordinate 0,4 (M 0 4), then we draw a line to coordinate 8,4 (L 8 4), followed by a line to coordinate 4,0 (L 4 0), and finally we close up the shape (Z). If you would like to learn more about XAML Path Markup, take a look at this MSDN article:
    XAML Path Markup Syntax

You might remember in our style for the ScrollBarLineButton, we set the Data attribute to use a “RelativeSource” of the TemplatedParent’s Content to supply the control content. This is fulfilled by the Content attribute we just assigned.

In the second cell, we define a Track element with three parts. A page up button, the thumb, and a page down button. Remember that the style we use for the page up and page down buttons is simply a transparent style so that you don’t see an actual button. The command attribute tells the control what it should do when each button is clicked, and the rest this control should be pretty self explanatory.

In the third cell, we define the scroll down button. Again, nothing new here that we didn’t discuss with the scroll up button.

Finally, we define the style that lays out the ScrollBar onto the ListBox control.

  <Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Style.Triggers>
      <Trigger Property="Orientation" Value="Vertical">
        <Setter Property="Width" Value="60"/>
        <Setter Property="Height" Value="Auto" />
        <Setter Property="Template" Value="{StaticResource VerticalScrollBar}" />
      </Trigger>
    </Style.Triggers>
  </Style>

This should all be pretty self explanatory as well :).

To use the control, you will need to add a reference into your App.xaml file so the program knows about the control. Inside of the <ResourceDictionary.MergedDictionaries> tags, just add the following line:

<ResourceDictionary Source="GlassListBox.xaml"/>

That’s it, your GlassListBox is ready to be used. Just add it to your form and roll. But remember, you might need to manually adjust the ListBoxItem width as I had mentioned above.

You can download this project here!

Tags: , , , , ,

Saturday, August 8th, 2009 Computer Programming, Tutorials

5 Comments to Creating a custom glass listbox with XAML in WFP

  • avatar Aditya says:

    Guys,
    A very good sample . I liked ur app a lot. Infact I started working on WPF after this ex.. 🙂
    Cheers
    Aditya

  • avatar Willy says:

    Looks nice, but I cannot seem to get it to work properly with calling the code-behind upon selection of an item.

  • avatar Emily says:

    Looks nice, but I cannot seem to get it to work properly with calling the code-behind upon selection of an item.

  • avatar Eugenio says:

    hi im usin you listbox glass, and y liked deploy itn in a proyect but i cant or i dont know how to link the glass listbox on click to a new windoss.

    do you can help me??

  • avatar worker jeans says:

    Greetings. I actually did some spider’s web surfing and found this blog. I indisputable not later than something like a collapse of this blog ask for up and it is quite incredible.I indubitably genuinely enjoy your website.Perfectly, the chunk of posting is in guarantee the entirely finest on this genuinely worth allowing subject. I added it and i

  • Leave a Reply