Microsoft Dot Net Master

Microsoft Dot Net Master
Microsoft Dot Net Master

Saturday, July 7, 2012

WPF TextBlock Control

How to change the line height within a TextBlock

To change the line hight within a TextBlock, you have to set the LineHeight to the desired height (in logical units) and also the LineStackingStrategy to BlockLineHeight, because otherwhise you will not see any effect.
 
<TextBlock Text="This is a 
multiline text." 
           LineHeight="25" LineStackingStrategy="BlockLineHeight" />

WPF ListView Control

How to Hide the Header of a ListView

To hide the header of a ListView you can modify the Visibility property of the ColumnHeaderContainer by overriding the style locally.
<ListView>
    <ListView.View>
        <GridView>
            <GridView.ColumnHeaderContainerStyle>
               <Style>
                   <Setter Property="FrameworkElement.Visibility" Value="Collapsed"/>
               </Style>
            </GridView.ColumnHeaderContainerStyle>
            <GridView.Columns>
                ...
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

Change the arrangement of items in a listbox

All WPF controls deriving from ItemsControl provide an ItemsPanel property that allows you to replace the internal layout panel that arranges the items.

Horizontal

We override the ItemsPanel property and set it to a StackPanel layout manager with an orientation set to Horizontal. We use an VirtualizingStackPanel that works just like a normal StackPanel, except that is does only create the items that are visible. If you scroll it automatically creates the new items that become visible and recycles the hidden.
 
<ListBox>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>
 
 

Wrapped

Using a WrapPanel to layout the items row by row is a bit more complex. Since the layout panel is wrapped by a ScrollContentPresenter (as seen by the scrolbar of the exmple above) the available width is infinite. So the WrapPanel does not see a reason to wrap to a new line.
What we need to do is to set the width of the warp panel to the ActualWidth if the internal ScrollContentPresenter. The ScrollContentPresenter can be found by using the FindAncestor mode of the relative source extension.
 
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapPanel />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

Change the Background of a selected ListBox Item

If you select an item in a listbox it gets the default selection color (usually blue) as background. Even if you specify a custom data template. The reason is that the blue background (or gray if the control is not focussed) is drawn outside of the data template. So you have no chance to override it from within the data template.
The color used to draw the blue and gray background are system colors. So the easiest way to get rid of these backgrounds is to locally override the highlight and control brushes of the system colors.
The best way to do this is to create a style for the listbox. Place the style in the resources of a parent element. For e.g. Window.Resources
 
<Style x:Key="myListboxStyle">
    <Style.Resources>
        <!-- Background of selected item when focussed -->
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red" />                
        <!-- Background of selected item when not focussed -->
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Green" />
    </Style.Resources>
</Style>
 
 
The following XAML snippet shows how to apply the style to the listbox.
 
<Grid x:Name="LayoutRoot">
  <ListBox Style="{StaticResource myListboxStyle}" />
</Grid>

How to apply a DataTemplate to a ListBox

Introducing DataTemplates

All controls deriving from ItemsControl have a DataTemplate that specifies how an object bound to an item is presented to the user. The default template renders a single line of text per item - as we know a listbox from HTML or WinForms.
But WPF allows us to put whatever we like into a DataTemplate. If we want to display a list of customers we can provide a data template that shows an image for each customer next to its name and age. In my opinion this is one of the most powerful features in WPF.

In the following steps I will show you how to create such an DataTemplate

Bind the data to the ListBox

We bind a list of employees to the listbox. Because the listbox does not know anything about our employee class, it calls the ToString() method. The result is not yet very pleasing.
 
<Grid x:Name="LayoutRoot">
   <ListBox Margin="10" ItemsSource="{Binding}"/>
</Grid>
 
 

Override the ToString() method to improve the display

What we do next is to overriding the ToString() method to provide some useful information instead of just the type name. But a string is not really what we call a cool user experience.
 
public override string ToString()
{
    return string.Format("{0} {1} ({2}), {3})", 
                      Firstname, Lastname, Age, Role);
}
 
 

Create a DataTemplate

The ultimate flexibility is to create your own DataTemplate. A data template can consist of multiple controls like images, textblocks, etc. It han have a flexible width and height, background color and shape. There are no limits for your creativity.
An easy way to create a data template is to use Expression Blend. Open the context menu of your list box and choose "Edit Other Templates" -> "Edit Generated Items" -> "Create Empty".
Blend will ask you to specify a name for your DataTemplate because it will define it in the resources of either the Application, the current document of an external resource dictionary. The name you specify is the key for the resource dictionary.
After you press OK, you will be in the editor to design the data template. The item data (in our sample an employee) is set to the DataContext, so you can easily access all properties of the employee by using databinding.

 
<DataTemplate x:Key="EmployeeDataTemplate">
   <Grid>
      <Grid.ColumnDefinitions>
         <ColumnDefinition Width="60"/>
         <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
   <Border Margin="5" BorderBrush="Black" BorderThickness="1">
      <Image Source="{Binding Path=Image}" Stretch="Fill" Width="50" Height="50" />
   </Border>
   <StackPanel Grid.Column="1" Margin="5">
      <StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" >
         <TextBlock Text="{Binding Path=Firstname, FallbackValue=FirstName}" />
         <TextBlock Text="{Binding Path=Lastname, FallbackValue=LastName}" Padding="3,0,0,0"/>
      </StackPanel>
      <TextBlock Text="{Binding Path=Age, FallbackValue=Age}" />
      <TextBlock Text="{Binding Path=Role, FallbackValue=Role}" />
   </StackPanel>
  </Grid>
</DataTemplate>

How to strech an WPF ListBox Item to span the whole width

If you create a data template with a right-aligned element (like the age in our exmple), it only spans over the needed width, because the content of a listbox is left-aligned by default.
This is the DataTemplate I created for this example.
 
<Style TargetType="ListBox" x:Key="strechedItemStyle">
    <Setter Property="ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid Background="#330000FF">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Name}" HorizontalAlignment="Left"  Grid.Column="0"/>
                    <TextBlock Text="{Binding Age}" HorizontalAlignment="Right" Grid.Column="1"/>
                </Grid>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>
 
 
To make the listbox item span the whole width of the listbox you need to set the HorizontalContentAlignment property to Stretch.
This is the way to set it directly on the listbox element:
 
<ListBox x:Name="listBox" Margin="20" 
         Style="{StaticResource strechedItemStyle}" 
         HorizontalContentAlignment="Stretch" />
 
 
If you want to set this property within a style you can do it with the following lines of code
 
<Style TargetType="ListBox" x:Key="strechedItemStyle">
  <Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>

Thursday, June 28, 2012

WPF ListBox Control

WPF ListBox Control

Introduction

The ListBox control displays a list of items. The user can select one or multiple items depending on the selection mode. The typical usage of a listbox in WPF is to bind its items to a list of business objects and display them by applying a data template.

 
<ListBox Margin="20">
    <ListBoxItem>New York</ListBoxItem>
    <ListBoxItem>Los Angeles</ListBoxItem>
    <ListBoxItem>Paris</ListBoxItem>
    <ListBoxItem>Zürich</ListBoxItem>
</ListBox>
 
 

How to define a Trigger for IsSelected in the DataTemplate

If you want to change the appearance of a ListBoxItem when it is selected, you have to bind the IsSelected property of the ListBoxItem. But this is a bit tricky, you have to use a relative source with FindAcestor to navigate up the visual tree until you reach the ListBoxItem.
 
<DataTemplate x:Key="myDataTemplate">
    <Border x:Name="border" Height="50">
        <TextBlock Text="{Binding Text}" />
    </Border>
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding RelativeSource=
            {RelativeSource Mode=FindAncestor, AncestorType=
                {x:Type ListBoxItem}},Path=IsSelected}" Value="True">
            <Setter TargetName="border" Property="Height" Value="100"/>
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>