Archive Page 2

More on XML Data Binding

In my last post I demonstrated how to bind elements from an XML document to WPF controls. I created small master/detail dual pane application to demonstrate this. Here I continue exploring XML data binding using the same XML document but with these important differences.

  1. The XML document will use namespaces.
  2. The data will be sorted in a different order.
  3. The view of the data will be flattened into a ListView.

Using XML Namespaces

Adding a namespace to the XML document itself is a simple matter. Once this is done, however, changes must be made to the code that accesses the now namespace scoped XML elements and attributes. First, here’s the updates XML document.

<pc:States xmlns:pc="https://patconroy.wordpress.com/States/">
    <pc:State pc:Name="Delaware" pc:Abbrev="DE">
        <pc:Capital>Dover</pc:Capital>
        <pc:Nickname>The First State</pc:Nickname>
        <pc:Bird>Blue Hen Chicken</pc:Bird>
        <pc:Flower>Peach Blossom</pc:Flower>
        <pc:Tree>American Holly</pc:Tree>
        <pc:Motto>Liberty and Independence</pc:Motto>
    </pc:State>
    <!-- Other states omitted for brevity -->
</pc:States>

The application no longer displays the data after making this change. It turns out that two changes must be made to the XAML source. First an XmlNamespaceMappingCollection must be associated with the XmlDataProvider. The XmlNamespaceMappingCollection creates a mapping between namespace URIs and their prefixes that are later used in XPath expressions. The XmlDataProvider.XmlNamespaceManager property must be set to an instance of XmlNamespaceMappingCollection. Here I create a mapping between the prefix “pc” and my namespace URI http://patconroy.workpress.com/States/.

<XmlNamespaceMappingCollection x:Key="StateDataNamespaceMapping">
    <XmlNamespaceMapping Uri="https://patconroy.wordpress.com/States/" Prefix="pc" />
</XmlNamespaceMappingCollection>
<XmlDataProvider x:Key="StateData" XmlNamespaceManager="{StaticResource StateDataNamespaceMapping}">
    <!-- etc. -->
</XmlDataProvider>

Next the XPath expressions used in the application must be updated to use the “pc” prefix. Without the prefix the XPath processor looks for elements and attributes in the global namespace, and this in no longer the case. The state XPath expression to access the state collection now becomes //pc:States/pc:State instead of //States/State. The expression to access the Name attribute of the State element now becomes @pc:Name.

Sorting the Data

In the XML document the states are presented in the order in which they ratified the Constitution for the United States of America. Any ItemsControl, including the ListBox used in the last example and the ListView used here, will display them in this order when bound to the XmlElement collection returned by the XPath expression //pc:States/pc:State. In this application I want them alphabetized by state abbreviation. WPF includes the CollectionViewSource class to do exactly this. CollectionViewSource can also do grouping and filtering. Check MSDN for the details. Here I create a CollectionViewSource in the main window’s resource dictionary to sort on the Abbrev XML attribute.

<CollectionViewSource x:Key="StateViewSource" Source="{Binding Source={StaticResource StateData},XPath=//pc:States/pc:State}">
    <CollectionViewSource.SortDescriptions>
        <cm:SortDescription PropertyName="@pc:Abbrev"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

The CollectionViewSource binds to the XmlDataProvider and applies an XPath expression to access the collection of State elements. A SortDescription is added to sort on the Abbrev attribute, which is again accessed using an XPath expression. The SortDescription class doesn’t exist in any of the default XAML namespaces, so the namespace declaration xmlns:cm="clr-namespace:System.ComponentModel;assembly=WindowsBase" must be added to the XAML. The ItemsControl displaying this data must bind to the CollectionViewSource, not the XmlDataProvider: ItemsSource="{Binding Source={StaticResource StateViewSource}}".

Displaying in a ListView

In previous posts I re-templated the ListView control. As part of this I implemented alternating row colors to achieve an accounting ledger look. The accounting ledger look can be achieved far more simply than this; it isn’t necessary to re-template the entire control. When working with .Net 3.5 SP1 apply the AlternationCount attribute to the ListView itself. Then add an ItemContainerStyle containing the property triggers on ItemsControl.AlternationIndex as shown in the previous post. I had to create a DataTemplate for the first column in order to display the state flag there. A value converter is used to obtain the image file path. Thanks again to folks at 3DFlags.com for the flag images. Here’s the complete ListView XAML for this project.

<ListView x:Name="_stateList" Margin="4"
          AlternationCount="2"
          ItemsSource="{Binding Source={StaticResource StateViewSource}}">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Style.Triggers>
                <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                    <Setter Property="Background" Value="Khaki"/>
                </Trigger>
                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                    <Setter Property="Background" Value="Beige"/>
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="LightBlue"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.View>
        <GridView AllowsColumnReorder="true">
            <GridViewColumn Width="36">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <Image Source="{Binding XPath=@pc:Abbrev, Converter={StaticResource ImageNameConverter}}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Width="48" DisplayMemberBinding="{Binding Path=@pc:Abbrev}" Header="Abbrev"/>
            <GridViewColumn Width="100" DisplayMemberBinding="{Binding Path=@pc:Name}" Header="Name"/>
            <GridViewColumn Width="120" DisplayMemberBinding="{Binding XPath=pc:Nickname}" Header="Nickname"/>
            <GridViewColumn Width="120" DisplayMemberBinding="{Binding XPath=pc:Capital}" Header="Capltal City"/>
            <GridViewColumn Width="120" DisplayMemberBinding="{Binding XPath=pc:Bird}" Header="State Bird"/>
            <GridViewColumn Width="120" DisplayMemberBinding="{Binding XPath=pc:Flower}" Header="State Flower"/>
            <GridViewColumn Width="120" DisplayMemberBinding="{Binding XPath=pc:Tree}" Header="State Tree"/>
            <GridViewColumn Width="200" DisplayMemberBinding="{Binding XPath=pc:Motto}" Header="Motto"/>
        </GridView>
    </ListView.View>
</ListView>

Here’s how the finished application looks.
xmllistview

Source code can be downloaded here.

XML Data Binding in WPF

Data binding is a powerful and time saving feature of the WPF framework. It allows a depencency property in the application’s view layer, such as a TextBox’s Text property, to be bound to a property in the application’s underlying data model. Data model in this instance means .NET classes or, as I’ll show later, an XML document. Data binding saves us from the tedious task of writing code to move data to and from WPF controls on the screen.

In my last two posts about styling the ListView I bound a ListView control to an underlying collection of type ObservableCollection<string>. Most real-world applications will bind ItemsControls to a collection of objects more complex than string. The screen controls then access properties of the underlying objects using data binding. More visually complex representations can be created by using data templates.

The XmlDataProvider in conjunction with the Binding class enable binding to elements and attributes in an XML DOM using XPath expressions. XmlDataProvider can load an XML document from a local system disk, from a remote system, or, as the example here does, from an XML data island in the application itself. XmlDataProviders can be easily created in a resource dictionary using XAML.

<Application.Resources>
    <XmlDataProvider x:Key="Xml1" Source="c:\documents\xml\automobiles.xml"/>
    <XmlDataProvider x:Key="Xml2" Source="http://www.somecompany.com/finance/Q3results.xml"/>

The sample application here displays various facts about the states of the United States of America. The data is limited to the first 13 states as all 50 aren’t needed for the example. They are listed in the order that they ratified the Constitution for the United States of America. The XML schema for the document is listed below.

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:complexType name="StateType">
    <xs:all>
      <xs:element name="Capital" type="xs:string" />
      <xs:element name="Nickname" type="xs:string" />
      <xs:element name="Bird" type="xs:string" />
      <xs:element name="Flower" type="xs:string" />
      <xs:element name="Tree" type="xs:string" />
      <xs:element name="Motto" type="xs:string" />
    </xs:all>
    <xs:attribute name="Name" type="xs:string" use="required" />
    <xs:attribute name="Abbrev" type="xs:string" use="required" />
  </xs:complexType>
  <xs:complexType name="StatesType">
    <xs:sequence maxOccurs="unbounded">
      <xs:element name="State" type="StateType" />
    </xs:sequence>
  </xs:complexType>
  <xs:element name="States" type="StatesType" />
</xs:schema>

The XmlDataProvider containing the XML document itself is created in the application resource dictionary. Note that the data island is contained within an <x:XData> element.

<XmlDataProvider x:Key="StateData">
    <x:XData>
        <States xmlns="">
            <State Name="Delaware" Abbrev="DE">
                <Capital>Dover</Capital>
                <Nickname>The First State</Nickname>
                <Bird>Blue Hen Chicken</Bird>
                <Flower>Peach Blossom</Flower>
                <Tree>American Holly</Tree>
                <Motto>Liberty and Independence</Motto>
            </State>
            <!-- Remaining states omitted for brevity -->
        </States>
    </x:XData>
</XmlDataProvider>

The application window consists of two panes. The left-hand pane contains a ListBox that lists the states from the XML document. This isn’t a simple list of state names. A DataTemplate is used to display an image of the state’s flag along with its name. The GIF images of the state flags used in this example were provided by the kind folks at 3DFlags.com.

xmldata

The DataTemplate is created in the application resource dictionary. See the XAML source below. Note that a value converter is used to construct the name of the flag image file. The GIF images are included in the Visual Studio project in a folder named Images.

<local:ImageNameConverter x:Key="ImageNameConverter"/>
<DataTemplate x:Key="StateListTemplate">
    <StackPanel Orientation="Horizontal">
        <Image Width="64" Source="{Binding XPath=@Abbrev,Converter={StaticResource ImageNameConverter}}"/>
        <TextBlock Margin="6,12,0,0" Text="{Binding XPath=@Name}" FontSize="14"/>
    </StackPanel>
</DataTemplate>

Here’s the source for the value converter.

class ImageNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value is string)
        {
            return @"Images\" + (value as string) + ".gif";
        }
        else
        {
            throw new ArgumentException();
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

In the main Window I establish the data source for the window by binding to the XmlDataSource. An XPath expression is used to access the collection of <State> elements in the document.

<Window x:Class="XMLData.Window1"
    DataContext="{Binding Source={StaticResource StateData},XPath=//States/State}">
</Window>

The ListBox ItemTemplate property references the DataTemplate shown earlier.

<ListBox x:Name="_stateList" Grid.Column="0" Margin="2,2,6,2" 
         Background="Beige"
         ItemsSource="{Binding}" ItemTemplate="{StaticResource StateListTemplate}"
         IsSynchronizedWithCurrentItem="True"/>

The rest of the application references the ListBox’s SelectedItem. For example the status bar displays the name of the state currently selected in the ListBox. I did this by binding the Text property of a TextBlock to the SelectedItem property of the ListBox. A value converter is used once again to get the desired text.

<StatusBarItem>
    <TextBlock Text="{Binding ElementName=_stateList,Path=SelectedItem,Converter={StaticResource SelectedItemConverter}}"/>
</StatusBarItem>

The right-hand pane of the application displays far more information than just the state name. A FlowDocumentScrollViewer is used to display a FlowDocument that contains the relevant information. Within the FlowDocument instances of TextBlock have their Text properties bound to various elements within the currently selected <State> element. Again, XPath expressions are used to obtain the desired data. By using data binding I avoid writing any C# code whatsoever to update the details pane when a new state is selected in the list box. The WPF binding framework handles this automatically. In fact there is very little code behind in this application at all. All data movement is handled through binding. I only created two value converters to coerce some of the data into a more displayable form.

<FlowDocumentScrollViewer Grid.Column="1" Margin="2">
    <FlowDocument Background="Beige" DataContext="{Binding ElementName=_stateList,Path=SelectedItem}">
        <Paragraph FontSize="16" FontStyle="Italic" FontWeight="Bold" TextAlignment="Center">
            <TextBlock Text="{Binding XPath=@Abbrev}"/>
            <Run Text=" - "/>
            <TextBlock Text="{Binding XPath=@Name}"/>
        </Paragraph>
        <Table FontSize="12" CellSpacing="8">
            <Table.Columns>
                <TableColumn Width="Auto"/>
                <TableColumn/>
            </Table.Columns>
            <TableRowGroup>
                <TableRow>
                    <TableCell><Paragraph>Capital</Paragraph></TableCell>
                    <TableCell>
                        <Paragraph>
                            <TextBlock Text="{Binding XPath=Capital}"/>
                        </Paragraph>
                    </TableCell>
                </TableRow>
                <!-- Other rows omitted for brevity -->
            </TableRowGroup>
        </Table>
    </FlowDocument>
</FlowDocumentScrollViewer>

All source code may be downloaded here.

More on ListView styles

In my last post I described how to display a ListView control with alternating row colors. This gives the ListView the look of an accounting ledger. I ended by mentioning that there is a new feature in the .NET 3.5 SP1 libraries that makes this much easier. Microsoft added a new property to ItemsControl named AlternationCount. This technique works for any ItemsControl, not just the ListView demonstrated here.

Set AlternationCount to the number of rows that are to partake in the pattern. In this example a 2 row pattern is specified but any number may be used. You only must specify display properties for each row in the pattern. More on that later.

<Style x:Key="{x:Type ListView}" TargetType="{x:Type ListView}">
  <Setter Property="AlternationCount" Value="2"/>
…
</Style>

The ItemsControl, a ListView in this example, attaches the property ItemsControl.AlternationIndex to each of its item containers. The value of ItemsControl.AlternationIndex is the row ordinal mod n, where n is the value specified for AlternationCount. In the example here it will be set to 0 for even numbered rows and 1 for the odd numbered ones. Now simply add property triggers on ItemsControl.AlternationIndex to the item container style. The item container in this example is a ListViewItem.

<Style x:Key="{x:Type ListViewItem}" TargetType="ListViewItem">
  <Style.Triggers>
    <Trigger Property="ItemsControl.AlternationIndex" Value="0">
      <Setter Property="Background" Value="{StaticResource ListViewLine2Brush}"/>
    </Trigger>
    <Trigger Property="ItemsControl.AlternationIndex" Value="1">
      <Setter Property="Background" Value="{StaticResource ListViewLine1Brush}"/>
    </Trigger>
  </Style.Triggers>
</Style>

This technique does not suffer from the same problem detailed in my last post. The ItemsControl automatically updates AlternationIndex on all items affected by an adds or deletes on the underlying collection. No special workaround code needs to be written like in the last post. All source code for both of my ListView posts is located here.

Alternating row background color in WPF ListView

In yesterday’s post you’ll notice that the ListView control displays using different colors for alternating rows. I implemented this effect by using a style selector. A style selector is basically a class that inherits from StyleSelector and overrides the SelectStyle method. Here’s my implementation.

public class ListViewItemStyleSelector : StyleSelector
{
    public override Style SelectStyle(object item, DependencyObject container)
    {
        ListView listView = ItemsControl.ItemsControlFromItemContainer(container) as ListView;

        Style st;
        int index = listView.ItemContainerGenerator.IndexFromContainer(container);
        if (index % 2 == 0)
        {
            st = (Style)listView.FindResource("ListViewItemRow2");
        }
        else
        {
            st = (Style)listView.FindResource("ListViewItemRow1");
        }
        return st;
    }
}

Notice that the SelectStyle method first determines the index of the item being rendered. It uses this index to select a Style instance from the application’s static resources. I’ll get to these resources shortly.

Next I create an instance of this class in the application resource dictionary.

<controls:ListViewItemStyleSelector x:Key="ListViewItemStyleSelector"/>

The ListView property ItemContainerStyleSelector must be set to this or any other instance of my ListViewItemStyleSelector class. Actually ItemContainerStyleSelector is a property of ItemsControl, so this technique can be used on any ItemsControl, such as ListBox, not just the ListView used here.

<Style x:Key="{x:Type ListView}" TargetType="{x:Type ListView}">
  <Setter Property="ItemContainerStyleSelector" Value="{StaticResource ListViewItemStyleSelector}"/>
…
</Style>

Finally the two styles referenced by SelectStyle must be inserted into the application resource dictionary.

<Style x:Key="ListViewItemBase" TargetType="{x:Type ListViewItem}">
  <Setter Property="SnapsToDevicePixels" Value="true"/>
  <Setter Property="OverridesDefaultStyle" Value="true"/>
  <Setter Property="Foreground" Value="{StaticResource DarkForegroundBrush}"/>
  <Setter Property="Template" Value="{StaticResource ListViewItemTemplate}"/>
  <Setter Property="Margin" Value="2,0,2,1"/>
</Style>
  
<Style x:Key="ListViewItemRow1" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource ListViewItemBase}">
    <Setter Property="Background" Value="{StaticResource ListViewLine1Brush}"/>
</Style>

<Style x:Key="ListViewItemRow2" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource ListViewItemBase}">
  <Setter Property="Background" Value="{StaticResource ListViewLine2Brush}"/>
</Style>

Here I factored all of the common property settings into a base instance of Style named ListViewItemBase. The two Style instances used to render the ListView rows, ListViewItemRow1 and ListViewItemRow2, inherit these base settings using the BasedOn property: BasedOn=”{StaticResource ListViewItemBase}”.

This approach works great when the collection bound to ListView.ItemsSource is static, as it is in my test window. Everything breaks down, however, when items are added or deleted to the underlying collection.

I created a new Visual Studio project to isolate the ListView. In this project I bind ListView.ItemsSource to an ObservableCollection instead of specifying a collection of ListViewItems in my main window XAML as I did in the last example. Here’s the ListView in the new test window.

<ListView x:Name="_list" Margin="8" Height="120" Width="240" ItemsSource="{Binding}">
    <ListView.View>
        <GridView AllowsColumnReorder="true">
            <GridViewColumn DisplayMemberBinding="{Binding}" Header="Content" Width="100"/>
            <GridViewColumn DisplayMemberBinding="{Binding Path=Length}" Header="Length" Width="100"/>
        </GridView>
    </ListView.View>
</ListView>

And the relevant C# code in the code behind file.

private ObservableCollection<string> _items = new ObservableCollection<string>();
public Window1()
{
    _items.Add("First Item");
    _items.Add("Second Item");
    _items.Add("Third Item");
    _items.Add("Fourth Item");
    _items.Add("Fifth Item");
    _items.Add("Sixth Item");
    _items.Add("Seventh Item");
    _items.Add("Eighth Item");
    InitializeComponent();
    DataContext = _items;
}

I also added some buttons and text boxes to the test window to facilitate adding and deleting of collection items. Here are the relevant Button.Click event handlers.

private void DelButton_Click(object sender, RoutedEventArgs e)
{
    int n = 0;
    if (Int32.TryParse(_delItemNum.Text, out n))
    {
        if (n >= 1 && n <= _items.Count)
        {
            _items.RemoveAt(n-1);
        }
    }
}

private void AddButton_Click(object sender, RoutedEventArgs e)
{
    int n = 0;
    if (Int32.TryParse(_addItemNum.Text, out n) && String.IsNullOrEmpty(_addItemText.Text))
    {
        if (n >= 1 && n <= _items.Count)
        {
            _items.Insert(n - 1, _addItemText.Text);
        }
        else
        {
            _items.Add(_addItemText.Text);
        }
    }
}&#91;/sourcecode&#93;

The screenshot below shows what happens after deleting the third collection item.  Notice that the individual row background colors were not reset after deleting the item.  “Second Item” and “Fourth Item” are both the darker shade of gray.

<img src="https://patconroy.files.wordpress.com/2009/01/badlistview.jpg" alt="badlistview" title="badlistview" width="375" height="375" class="alignnone size-full wp-image-51" />

It turns out that once the item style is selected it will never be modified regardless of how the underlying data collection changes.  A workaround for this is to refresh the ListView’s collection view.  This can be done just after adding or deleting an item.

_items.RemoveAt(n-1);
System.ComponentModel.ICollectionView dataView =
    CollectionViewSource.GetDefaultView(_list.ItemsSource);
dataView.Refresh();

A better place to put this is in an event handler for ObservableCollection.CollectionChanged.

private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    System.ComponentModel.ICollectionView dataView =
        CollectionViewSource.GetDefaultView(_list.ItemsSource);
    dataView.Refresh();
}

Now the individual row backgrounds get reset correctly after items are added or removed from the data collection, and the alternating background color pattern is maintained.

The technique described here works for all ItemsControls in all versions of .NET that include WPF, 3.0, 3.1 and 3.1 SP1. A new feature was introduced in 3.5 SP1 that simplifies this a great deal. I’ll describe that in my next post. Stay tuned.

Restyling all WPF Controls

In my last post I detailed how I restyled a WPF expander control. I started with the restyled expander control from the Simple Styles SDK sample and altered it to display using the Vista black or deep gray color scheme. Well I wasn’t satidfied having just an expander in this color scheme, so I went ahead and restyled all of the controls in the Simple Styles sample. I added IsMouseOver property triggers to a number of the visual elements to give the user more feedback when something is clickable. Now I have a nice toolbox full of controls in this color scheme that I can use to build applications. Here are screen shots of my results. Click on an image to see it in full size.

mystyles1
mystyles2

All source code for this including the Visual Studio 2008 project is located Here. You may use any of this code in your own applications as you wish, as long as you don’t violate whatever code use argrements accompany the Microsoft SDK. This code did after all originate in the SDK samples.

Restyling the WPF Expander control

With minimal effort any of the supplied Windows Presentation Foundation (WPF) controls can be visually restyled to suit an application’s needs.  With only a little more effort the control’s entire visual tree can be replaced using templates without affecting the underlying behavior of the control itself.

I recently found myself wishing the the toggle button on the Expander control was on the right hand side rather than the left.  The following XAML can be pasted into XAMLPad to show the default look of the Expander control.  I personally use Kaxaml for such purposes.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <StackPanel>
    <Expander Width="200" Header="This is the Header">
      <Border Height="100">
        <StackPanel>
          <TextBlock>TextBlock text</TextBlock>
          <Label>Label text</Label>
          <Label IsEnabled="False">Disabled label</Label>
        </StackPanel>
      </Border>
    </Expander>
  </StackPanel>
</Page>

WPF applies the default control template for the current theme. On Windows Vista the above XAML will produce this.

Unstyled Expander

A Style can set any of the public properties of a control. Of course the same public properties can be set in the control declaration itself, but the use of styles facilitates reuse and helps achieve a common look and feel across the application. Styles are typically declared as static resources in a window or application resource dictionary. Styles may be applied to a control either explicitly or implicitly. Each style declaration must specify to which control type it applies, and it may only apply to one type.

  • A style must be explicitly applied to a control if the style if given a key in the resource dictionary. A style declared thusly, <Style k:Key="ExpanderStyle" TargetType="{x:Type Expander}">...</Style>, must be referenced in the control declaration like this: <Expander Style="{StaticResource ExpanderStyle}">...</Expander>.
  • By omitting the Key from the Style declaration the style is implicitly applied to all controls of the appropriate type under the scope of the dictionary. For example if this is placed in the application resource dictionary, <Style TargetType="{x:Type Expander}">...</Style>, then this style will apply to all instances of Expander in the application.

The following XAML shows how to implicitly style the Expander.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Resources>
    <Style TargetType="{x:Type Expander}">
      <Setter Property="BorderThickness" Value="1"/>
      <Setter Property="BorderBrush" Value="DarkGray"/>
      <Setter Property="Foreground" Value="#202020"/>
      <Setter Property="Background" Value="#D0D0D0"/>
    </Style>
  </Page.Resources>
  <StackPanel>  
    <Expander Width="200"
              Margin="0,8,0,0"
              Header="This is the Header">
      <Border Height="100">
        <StackPanel>
          <TextBlock>TextBlock text</TextBlock>
          <Label>Label text</Label>
          <Label IsEnabled="False">Disabled label</Label>
        </StackPanel>
      </Border>
    </Expander>
  </StackPanel>
</Page>

Styled Expander

This Style changes the Expander to be visually more to my liking, however the toggle button is still on the left hand side. I want an Expander with the button on the right. There is no Expander public property to move the toggle button. Luckily the SimpleStyles sample in the Windows SDK does have such an Expander. Examination of the SDK sample shows that a control template must be used. I modified the SDK sample to my own liking. In addition to moving the button I wanted black/dark gray color scheme that is so trendy these days since the launch of Vista. The XAML below shows what I have done so far.

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Page.Background>
    <!-- Window background brush removed for brevity-->
  </Page.Background>
  <Page.Resources>
    <!-- Color brushes removed for brevity.  Download code to see. -->

    <!-- Expander toogle button template.
           Removed for brevity.  Please download code to see. -->

    <!-- Expander style -->  
    <Style TargetType="Expander">
      <Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
      <Setter Property="Template">
        <Setter.Value>
          <!-- Control template for expander -->
          <ControlTemplate TargetType="Expander">
            <Grid>
              <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Name="ContentRow" Height="0"/>
              </Grid.RowDefinitions>
              <Border 
                Name="Border" 
                Grid.Row="0" 
                Background="{StaticResource HeaderBrush}"
                BorderBrush="{StaticResource NormalBorderBrush}"
                BorderThickness="1" 
                CornerRadius="4,4,0,0" >
                <Grid>
                  <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="20" />
                  </Grid.ColumnDefinitions>
                  <!-- The following puts the toggle button in the right hand column, just like I want! -->
                  <ToggleButton
                    Grid.Column="1"
                    IsChecked="{Binding Path=IsExpanded,Mode=TwoWay,
                                        RelativeSource={RelativeSource TemplatedParent}}"
                    OverridesDefaultStyle="True" 
                    Template="{StaticResource ExpanderToggleButton}" 
                    Background="{StaticResource NormalBrush}" />
                  <ContentPresenter 
                    Grid.Column="0"
                    Margin="4" 
                    ContentSource="Header" 
                    RecognizesAccessKey="True" />
                </Grid>
              </Border>
              <Border 
                Name="Content" 
                Grid.Row="1" 
                Background="{StaticResource GroupBackgroundBrush}"
                BorderBrush="{StaticResource OpenGroupBorderBrush}" 
                BorderThickness="1,0,1,1" 
                CornerRadius="0,0,4,4" >
                <ContentPresenter Margin="4" />
              </Border>
            </Grid>
            <ControlTemplate.Triggers>
              <Trigger Property="IsExpanded" Value="True">
                <Setter TargetName="ContentRow" Property="Height"
                        Value="{Binding ElementName=Content,Path=DesiredHeight}" />
                <Setter TargetName="Border" Property="BorderBrush"
                        Value="{StaticResource OpenHeaderBorderBrush}"/>
              </Trigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
    </Style>
  </Page.Resources>
  
  <!-- Test : Removed for brevity-->
</Page>

Templated Expander

I had to cut much of the code out because it is too much to list here. Download this XAML code here to see it all. Load it into XAMLPad to see how it works.

Notice how the expander control template has an outer grid and an inner grid. The outer grid has two rows, one for the header and one for the content. The content row has a height of zero and is thus not visible by default. A property trigger <Trigger Property="IsExpanded" Value="True"> sets this row’s height when the control is expanded. The header row contains another grid with two columns. The header content is placed into the left-hand column and the toggle button into the right. The button is now where I desire it to be.

Hello world!

Welcome to WordPress.com. This is your first post. Edit or delete it and start blogging!


February 2017
M T W T F S S
« Apr    
 12345
6789101112
13141516171819
20212223242526
2728  
I am a part of all that I have met;
Yet all exprience is an arch whitherthro'
Gleams that untravell'd world, whose margin fades
For ever and for ever when I move.
How dull it is to pause, to make an end,
To rust unburnish'd, not to shine in use!
Alfred, Lord Tennyson