Welcome to MSDN Blogs Sign in | Join | Help

Mike Hillberg's Blog on Wpf (.Net and Silverlight)

Random, interesting things about Wpf/Silverlight.

Syndication

News

All postings are provided "As Is" with no warranties, and confers no rights. Opinions and views expressed here are not necessarily those of Microsoft Corporation.


Compact CheckBox Sample

I was creating a view of an object that had a bunch of boolean properties, but I wanted to keep the visual representation small.  So I created a look for a compact CheckBox that I liked enough to post.

As an example scenario, say I’m visualizing a the attributes of a file (hidden, system, archive, and read-only).  I could do it with a TextBlock and some CheckBoxes easy enough, to create a look like this:

 

image

 

… but I want it more compact.  I want just a letter for each attribute, and I just want the letter to change when it’s checked.  That got me this:

 

image

 

Here, the light gray italicized boxes are un-checked, and the un-italicized black letters with a gray background are checked.  (So before capturing this screenshot, I had checked the read-only and archive bits on the “readme.txt” file.)   This image doesn’t show it, but there’s also a tooltip for each CheckBox that shows the full name of the attribute.

 

Here’s the ControlTemplate for the CheckBox:

 

<ControlTemplate TargetType="CheckBox">

  <Border BorderThickness="{TemplateBinding BorderThickness}"

          BorderBrush="Gray" Background="LightGray" Padding="2"

          Name="_border" >

    <Grid Margin="1">

      <TextBlock Text="{TemplateBinding Content}"

                 FontStyle="Italic"

                 Foreground="LightGray"/>

      <TextBlock Text="{TemplateBinding Content}"

                 Name="_checkedTextBlock" />

    </Grid>

  </Border>

 

  <ControlTemplate.Triggers>

    <Trigger Property="IsChecked" Value="False">

      <Setter TargetName="_checkedTextBlock"

              Property="Opacity" Value="0" />

      <Setter TargetName="_border"

              Property="Background" Value="Transparent" />

    </Trigger>

  </ControlTemplate.Triggers>

</ControlTemplate>

 

The interesting thing here is that I created two TextBlocks to show the content of the CheckBox (e.g. the “R” for the read-only attribute CheckBox).  The first one shows the content in “unchecked” form, and the second one shows it in “checked” form.  The Trigger then picks which one shows up.  I had started with a single TextBlock, and had the Trigger set the FontStyle and Foreground.  The problem with this is that the italicized text is slightly wider than the normal text, and that caused the checkboxes to jiggle a little bit when you click on them.  There’s probably other ways to solve this, but a simple solution was to have both TextBlocks there and taking up space in layout, and then let the Trigger decide which one wins for render.

 

And here’s a full template with sample usage, again with some of the more interesting parts highlighted.  (I changed the trigger so that instead of a Setter to update the Background when IsChecked changes, it animates the change.)

 

<Page

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

 

  <Grid>

   

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width="Auto" />

    </Grid.ColumnDefinitions>

 

    <Grid.Resources>

     

      <!-- Implicit style to be applied to all CheckBoxs in this Window

           (this sets the new template) -->

 

      <Style TargetType="CheckBox">

       

        <Setter Property="BorderThickness" Value="1,0,0,0" />

        <Setter Property="Template">

          <Setter.Value>

           

            <ControlTemplate TargetType="CheckBox">

              <Border BorderThickness="{TemplateBinding BorderThickness}"

                      BorderBrush="Gray" Background="LightGray" Padding="2"

                      Name="_border" >

                <Grid Margin="1">

                  <TextBlock Text="{TemplateBinding Content}"

                             FontStyle="Italic"

                             Foreground="LightGray"/>

                  <TextBlock Text="{TemplateBinding Content}"

                             Name="_checkedTextBlock" />

                </Grid>

              </Border>

 

              <ControlTemplate.Triggers>

                <Trigger Property="IsChecked" Value="False">

                 

                  <Setter TargetName="_checkedTextBlock"

                          Property="Opacity" Value="0" />

                 

                  <!-- Instead of a Setter to update the background color,

                       use Trigger.EnterActions/ExitActions to animate it

                  <Setter TargetName="_border"

                          Property="Background" Value="Transparent" /> -->

 

                  <Trigger.EnterActions>

                    <BeginStoryboard>

                      <Storyboard TargetName="_border" TargetProperty="Background.Color">

                        <ColorAnimation Duration="0:0:0.5" To="Transparent" />

                      </Storyboard>

                    </BeginStoryboard>

                  </Trigger.EnterActions>

 

                  <Trigger.ExitActions>

                    <BeginStoryboard>

                      <Storyboard TargetName="_border" TargetProperty="Background.Color">

                        <ColorAnimation Duration="0:0:0.5" />

                      </Storyboard>

                    </BeginStoryboard>

                  </Trigger.ExitActions>

                 

                </Trigger>

              </ControlTemplate.Triggers>

            </ControlTemplate>

           

          </Setter.Value>

        </Setter>

      </Style>

 

      <!-- This DataTemplate is used to display attributes for one file -->

     

      <DataTemplate x:Key="FileTemplate">

        <Border BorderThickness="1" BorderBrush="Black"

                Margin="4" CornerRadius="5,5,0,0" Background="#FF83AFEA">

          <StackPanel >

           

            <!-- Show the file name -->

            <Border Padding="2,2,2,0" >

              <TextBlock Text="{Binding XPath=@Name}" HorizontalAlignment="Center" Margin="1"/>

            </Border>

 

            <!--  Show the attributes in a row -->

            <StackPanel Orientation="Horizontal" Background="White">

              <CheckBox BorderThickness="0" ToolTip="Hidden"

                        IsChecked="{Binding XPath=@Hidden, Mode=TwoWay}">

                H</CheckBox>

              <CheckBox ToolTip="System"

                        IsChecked="{Binding XPath=@System, Mode=TwoWay}">

                S</CheckBox>

              <CheckBox ToolTip="Read only"

                        IsChecked="{Binding XPath=@ReadOnly, Mode=TwoWay}">

                R</CheckBox>

              <CheckBox BorderThickness="1,0,1,0" ToolTip="Archive"

                        IsChecked="{Binding XPath=@Archive, Mode=TwoWay}">

                A</CheckBox>

            </StackPanel>

           

          </StackPanel>

        </Border>

      </DataTemplate>

 

      <!-- Sample data -->

 

      <XmlDataProvider x:Key="SampleData" XPath="Files">

        <x:XData >

          <Files xmlns="">

            <File Name="autoexec.bat" Archive="False" System="False"

                  ReadOnly="True" Hidden="True" />

            <File Name="readme.txt" Archive="False" System="True"

                  ReadOnly="False" Hidden="False" />

          </Files>

        </x:XData>

      </XmlDataProvider>

 

    </Grid.Resources>

 

    <!-- To test it out, show the sample data -->

   

    <ItemsControl ItemsSource="{Binding XPath=File, Source={StaticResource SampleData}}"

                  ItemTemplate="{StaticResource FileTemplate}" />

 

  </Grid>

</Page>

 

 

 

Posted Friday, June 13, 2008 3:17 PM by MikeHillberg | 0 Comments

Trying out Binding.StringFormat

 

StringFormat is a new property in .Net 3.5 SP1, which is currently in Beta.  See Scott’s blog for more info on the beta.

 

 

When you bind data into a property on an element, it’s automatically type converted for you.  For example, this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>

    </StackPanel.DataContext>

   

    <TextBlock Text="{Binding}" /> <!-- Simply bind to the DataContext -->

   

</StackPanel>

 

… shows a TextBlock with the text “123”.

 

If you want to have more control over the conversion of the value during binding, you can write a value converter for it.  For example, with this code:

 

public class TestConverter : IValueConverter

{

 

    public object Convert(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        return String.Format(culture, "Cost: {0:C}", value);

    }

 

    public object ConvertBack(object value, Type targetType, object parameter,

                          System.Globalization.CultureInfo culture)

    {

        throw new NotImplementedException();

    }

 

}

 

… and this markup:

 

<StackPanel xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <StackPanel.Resources>

        <local:TestConverter x:Key="testConverter" />

    </StackPanel.Resources>

    <StackPanel.DataContext>

        <sys:Int32>123</sys:Int32>