Archive for the ‘Expression Blend’ Category.

How to have the TextBlock in a left column of a Grid in a ListView Template expand or shrink with text wrapping?

Ok, lets say you want to have a Grid where each item is a row of data in a Grid. The left most column should expand or shrink, and yes the text should wrap when it shrinks.

Not so easy…but it can be done if you use the right tools.

  • Use a DockPanel not a Grid
  • Make the left most column the last one added
<Window x:Class="ListBoxWithWrap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="242"
        d:DesignWidth="388"
        >
    <Grid>
        <DockPanel Name="MainGrid" HorizontalAlignment="Stretch">
            <!-- These four blocks will have other content eventually, but only need to be 45 wide -->
            <TextBlock Text="X" Grid.Column="1" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
            <TextBlock Text="X" Grid.Column="2" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
            <TextBlock Text="X" Grid.Column="3" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
            <TextBlock Text="X" Grid.Column="4" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
            <!-- This is the TextBlock that needs to wrap its content (and
                             change the height of the row (so the full content is still
                             visible) to whatever the available space is, but should not
                             make overall ListView wider than the parent's width. -->
            <TextBlock Text="A very long string that should wrap when the window is small." Padding="20,6,6,6" TextWrapping="Wrap" DockPanel.Dock="Right"/>
        </DockPanel>
    </Grid>
</Window>

You will see that this works as you desire.

Now put this in a ListView’s Template and set it to use Binding.

<Window x:Class="ListBoxWithWrap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="242"
        d:DesignWidth="388"
        SizeToContent="WidthAndHeight">
    <Grid>
        <ListView Name="lvWrap" ItemsSource="{Binding}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <DockPanel Name="MainGrid" HorizontalAlignment="Stretch">
                        <!-- These four blocks will have other content eventually, but only need to be 45 wide -->
                        <TextBlock Text="X" Grid.Column="1" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="2" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="3" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="4" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <!-- This is the TextBlock that needs to wrap its content (and
                             change the height of the row (so the full content is still
                             visible) to whatever the available space is, but should not
                             make overall ListView wider than the parent's width. -->
                        <TextBlock Text="{Binding Content}" Padding="20,6,6,6" TextWrapping="Wrap" DockPanel.Dock="Right"/>
                    </DockPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

Now give it some data to bind to.

using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace ListBoxWithWrap
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            List<SomeItem> list = new List<SomeItem>();
            list.Add(new SomeItem() { Content = "Some very long string with so many words there should be some wrapping going on to prevent a line of text that is too long" });
            list.Add(new SomeItem() { Content = "Some very long string with so many words there should be some wrapping going on to prevent a line of text that is too long" });
            list.Add(new SomeItem() { Content = "Some very long string with so many words there should be some wrapping going on to prevent a line of text that is too long" });
            list.Add(new SomeItem() { Content = "Some very long string with so many words there should be some wrapping going on to prevent a line of text that is too long" });
            list.Add(new SomeItem() { Content = "Some very long string with so many words there should be some wrapping going on to prevent a line of text that is too long" });

            lvWrap.DataContext = list;
        }

        public class SomeItem
        {
            public string Content { get; set; }
        }
    }
}

The shrink with text wrapping no longer works once inside of the ListView. So that tells you that something to do with the ListView is breaking the feature you want.

Here is how you fix this:

1. Open your project in Expression Blend. (If you don’t have Expression Blend, maybe just look at my code below and copy it)

2. Right-Click on the ListView in the Object and Timeline tab and choose Edit Template | Edit a Copy.

3. Click OK on the next Window.

This will create the following resource code.

<Window.Resources>
		<SolidColorBrush x:Key="ListBorder" Color="#828790"/>
		<Style x:Key="ListViewStyle1" TargetType="{x:Type ListView}">
			<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
			<Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
			<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
			<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
			<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
			<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
			<Setter Property="VerticalContentAlignment" Value="Center"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type ListView}">
						<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
							<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
								<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
							</ScrollViewer>
						</Border>
						<ControlTemplate.Triggers>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
							</Trigger>
							<Trigger Property="IsGrouping" Value="true">
								<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>

4. Now look at what is surrounding the ItemPresenter. Yes, you see the ScrollViewer, which is your problem. Delete it.

5. Build you project.

Success! Now your feature to both expand or shrink with text wrapping is back.

Here is the final XAML.

<Window x:Class="ListBoxWithWrap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        mc:Ignorable="d"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        d:DesignHeight="242"
        d:DesignWidth="388"
        SizeToContent="WidthAndHeight">
    <Window.Resources>
        <SolidColorBrush x:Key="ListBorder" Color="#828790"/>
        <Style x:Key="ListViewStyle1" TargetType="{x:Type ListView}">
            <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
            <Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
            <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
            <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
            <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListView}">
                        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
                            <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
                            </Trigger>
                            <Trigger Property="IsGrouping" Value="true">
                                <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <ListView Name="lvWrap" ItemsSource="{Binding}" Style="{DynamicResource ListViewStyle1}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <DockPanel Name="MainGrid" HorizontalAlignment="Stretch">
                        <!-- These four blocks will have other content eventually, but only need to be 45 wide -->
                        <TextBlock Text="X" Grid.Column="1" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="2" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="3" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <TextBlock Text="X" Grid.Column="4" HorizontalAlignment="Center" Width="45" DockPanel.Dock="Right"/>
                        <!-- This is the TextBlock that needs to wrap its content (and
                             change the height of the row (so the full content is still
                             visible) to whatever the available space is, but should not
                             make overall ListView wider than the parent's width. -->
                        <TextBlock Text="{Binding Content}" Padding="20,6,6,6" TextWrapping="Wrap" DockPanel.Dock="Right"/>
                    </DockPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

You should now have a little bit more understanding of the ListView template and how to manipulate it, which should translate to other objects in WPF as well.

How to disable row selection in a WPF DataGrid?

Disabling row selection in the WPF DataGrid included in .NET Framework 4 is not really easy. It is extremely difficult to do, unless you have the right tools and know exactly how to do it.

But all the difficulty is in figuring out how to do it. Once you know how to do it, the steps are quite easy to perform.

First, you basically have to use a copy of the default DataGrid style. However, the easiest way to get a copy of the default DataGrid style is using Expression Blend.

Read more one my new WPF Sharp site here:

How to disable row selection in a WPF DataGrid?

How to create a copy of a control’s default style?

Sometimes you need to make some advanced styling changes to a default control, such as a RadioButton, ListBox, DataGrid, Button, etc. However, you may want to keep the majority of the default style the way it is.

In Expression Blend, this is easy to do. If you don’t have Expression Blend and you are developing in WPF, get it immediately. There is a trial version you can test out.

Here is how to get your copy of any control’s default style….

Read more on my new WPF Sharp site
How to create a copy of a control’s default style?

How to configure the WPF RadioButton’s circle bullet top aligned when there are multiple lines of text?

Today I had to solve a problem that appeared quite difficult, but turned out to not be so hard if I let Expression Blend do most the work and finish it up in the XAML. I ended up having to completely recreate the default template style and then modify it.

Note: This article could also be titled: How to change the default template style of a standard control?

I had a RadioButton with text that wraps and it wasn’t displaying exactly how my team wanted. Here is the XAML.

<Window x:Class="RadioButtonTopAligned.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <RadioButton GroupName="RadioButtonList">
            <Label>
                <AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
            </Label>
        </RadioButton>
    </Grid>
</Window>

The problem is that the circle bullet is center aligned like this:

Notice how the circle bullet is aligned in between the two lines of text. I need it to be top aligned like this:

Notice how the circle bullet is aligned with the top line of text. I need to get WPF to do this.

From a Visual Studio 2010 point of view, there is no easy way to do this. At first I thought it would be a simple dependency property, but it isn’t. An quick internet search led me to realize that I have to pretty much re-style the whole RadioButton. This sounds really hard and in fact, in Visual Studio, without help, it would be really hard. You would have to have the code for the default template style for the RadioButton control memorized.

Here is an forum post I found from MSDN: How do I make a RadioButton’s Bullet align top

While the post is exactly what I was looking for and has an answer, I didn’t at first grasp the answer. I wasn’t sure what was going on until one of my co-workers, Shawn, who is more skilled in Expression Blend, showed me. Now that I understand, I want to make sure the next person who finds the same forum post on MSDN can understand even easier by writing this article and adding it to the forum post.

This is where Expression Blend comes in. If you don’t have Expression Blend, don’t worry, you can still get through this article as I will include the the default style code that Expression Blend created for me right here in my post.

In Expression Blend, this is what to do.

  1. Create a blank WPF project in Expression Blend.
  2. Add a RadioButton.
  3. Right-click on the RadioButton and choose Edit Template | Edit a Copy…
  4. Click OK on the Create Style Resource window.

Here is what happens to your XAML and you can do this to the XAML in you project manually if you don’t have Expression Blend.

  1. The following reference is added to the project: PresentationFramework.Aero
  2. The same is referenced in the XAML (See line 4 of the XAML below)
  3. The default RadioButton style is copied to your XAML under the Window.Resources element.  (See lines 10-48 in the XAML below)
  4. The RadioButton is assigned the style created. (See line 51 in the XAML below)
<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
	x:Class="RadioButtonTopAligned_EB.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">

	<Window.Resources>
		<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F"/>
		<Style x:Key="CheckRadioFocusVisual">
			<Setter Property="Control.Template">
				<Setter.Value>
					<ControlTemplate>
						<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
		<Style x:Key="RadioButtonStyle1" TargetType="{x:Type RadioButton}">
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="Background" Value="#F4F4F4"/>
			<Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type RadioButton}">
						<BulletDecorator Background="Transparent">
							<BulletDecorator.Bullet>
								<Microsoft_Windows_Themes:BulletChrome BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" IsRound="true" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}"/>
							</BulletDecorator.Bullet>
							<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
						</BulletDecorator>
						<ControlTemplate.Triggers>
							<Trigger Property="HasContent" Value="true">
								<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
								<Setter Property="Padding" Value="4,0,0,0"/>
							</Trigger>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>

	<Grid x:Name="LayoutRoot">
		<RadioButton GroupName="RadioButtonList" Style="{DynamicResource RadioButtonStyle1}">
			<Label>
				<AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
			</Label>
		</RadioButton>
	</Grid>
</Window>

Now we can edit the XAML. Below is the same XAML as above with the following edits:

  1. Inside the BulletDecorator.Bullet element on line 30, create a DockPanel around the BulletChrome element.
  2. The ControlPresenter is moved to be inside the DockPanel.
  3. Add the following XAML atrributes to the BulletChrome element:
    VerticalAlignment=”Top” Margin=”0,8,0,0″ Height=”{TemplateBinding FontSize}” Width=”{TemplateBinding FontSize}”

    Note: If you change the font of the text content in the RadioButton, you should change the Margin in the style as well. I haven’t figured out how to make it always match the top line without manually tweaking it when you change the font. Also, if you don’t want the BulletChrome element to be the same size as the font, you will have to tweak Width and Height too.

<Window
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
	xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
	x:Class="RadioButtonTopAligned_EB.MainWindow"
	x:Name="Window"
	Title="MainWindow"
	Width="640" Height="480">

	<Window.Resources>
		<SolidColorBrush x:Key="CheckBoxStroke" Color="#8E8F8F"/>
		<Style x:Key="CheckRadioFocusVisual">
			<Setter Property="Control.Template">
				<Setter.Value>
					<ControlTemplate>
						<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
		<Style x:Key="RadioButtonStyle1" TargetType="{x:Type RadioButton}">
			<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
			<Setter Property="Background" Value="#F4F4F4"/>
			<Setter Property="BorderBrush" Value="{StaticResource CheckBoxStroke}"/>
			<Setter Property="BorderThickness" Value="1"/>
			<Setter Property="Template">
				<Setter.Value>
					<ControlTemplate TargetType="{x:Type RadioButton}">
						<BulletDecorator Background="Transparent">
							<BulletDecorator.Bullet>
								<DockPanel>
									<Microsoft_Windows_Themes:BulletChrome VerticalAlignment="Top" Margin="0,8,0,0" Height="{TemplateBinding FontSize}" Width="{TemplateBinding FontSize}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" IsChecked="{TemplateBinding IsChecked}" IsRound="true" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" />
									<ContentPresenter RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
								</DockPanel>
							</BulletDecorator.Bullet>
							</BulletDecorator>
						<ControlTemplate.Triggers>
							<Trigger Property="HasContent" Value="true">
								<Setter Property="FocusVisualStyle" Value="{StaticResource CheckRadioFocusVisual}"/>
								<Setter Property="Padding" Value="4,0,0,0"/>
							</Trigger>
							<Trigger Property="IsEnabled" Value="false">
								<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
							</Trigger>
						</ControlTemplate.Triggers>
					</ControlTemplate>
				</Setter.Value>
			</Setter>
		</Style>
	</Window.Resources>

	<Grid x:Name="LayoutRoot">
		<RadioButton GroupName="RadioButtonList" Style="{DynamicResource RadioButtonStyle1}">
			<Label>
				<AccessText TextWrapping="Wrap" Text="_This is a very long radio button control line of text that should wrap." MaxWidth="300"/>
			</Label>
		</RadioButton>
	</Grid>
</Window>

I hope this posts clarifies how to completely recreate a template style for a default control to modify something that at first doesn’t appear modifiable.