Feeds:
Posts
Comments

Archive for July, 2011

Many desktop applications have their own status bar embedded and docked to the bottom of the app. Recently I was working on an application where this was the case. Periodically, messages get sent to the status bar when things happen in the app (file saved, transferring data, error reported, etc). At first, these messages would remain in the status bar until a new message was received. However, I only wanted the message to be displayed for a short duration of time, say ten seconds. This is an MVVM application and my first thought was to just play a storyboard that sets the opacity of the message to zero after 10 seconds. When a new message arrives, set the opacity back to 1, then after 10 seconds fade to zero again. It sounded simple, but I was not sure of how to play the storyboard from my ViewModel in the MVVM structure.

After doing some digging around, I came to realize that I didn’t have to do anything in the ViewModel and a property I never used before came in very handy…. so I guess I’ll tell you what that property was, ūüėģ, Binding.NotifyOnTargetUpdated. The MSDN says it “gets or sets a value that indicates whether to raise the TargetUpdated event when a value is transferred from the binding source to the binding target.” Now, lets say I set this property to true on a TextBlock.Text’s binding. In layman’s terms, when the text changes, the TargetUpdated event is thrown and I can use that to run my storyboard. How simple!! Here is an example.

<TextBlock Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}">
    <TextBlock.Triggers>
        <EventTrigger RoutedEvent="Binding.TargetUpdated">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="1.0" />
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2" From="1.0" To="0.0" BeginTime="0:0:10" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </TextBlock.Triggers>
</TextBlock>

Now, when the binding updates, the opacity of the text is initially set to 1. Then, after ten seconds, the opacity will begin to fade to zero in a span of two seconds. Viola! The storyboard plays when the target text is updated and there is no need to muck up the ViewModel. Happy coding!

Advertisements

Read Full Post »

This article is intended for people who already have a solid understanding of data binding, data templates, converters, and WPF in general. In this article I will attempt to show you how to display a collection of items in multiple ways based on some user feedback. Fore example, say you had a collection of person objects and a toggle button on the UI to show a basic or detailed view of the people. Lets say the basic view will only show a person’s first name and photo, while the detailed view would show their last name, address, and special interests (no photo). How could something like this be easily accomplished? Try using DataTriggers and DataTemplates. The following example won’t demonstrate the situation described exactly as above but it will give you the foundation to just that (as well as some other pretty cool things). Now lets get to the code.

First is the MainWindow.cs where I will create two properties. One is a collection of people and the other is an enum value that will be bound to three radio buttons that will allow us to switch between our views of the person collection. For this example, we will allow the user to display each person item as a styled RadioButton, simple TextBlock, or simple CheckBox. Here is all of the code for the MainWindow.cs file.

namespace DataTemplateSwitchingTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
       public ObservableCollection<Person> People
       {
            get
            {
                if (_people == null)
                {
                    _people = new ObservableCollection<Person>()
                    {
                        new Person("Doe", "John", "Johnny"),
                        new Person("Doe", "Jane", "Janie"),
                        new Person("Smith", "Mike", "Big Mike"),
                        new Person("Simpson", "Mark", "El Capitan"),
                    };
                }
                return _people;
            }
        }private ObservableCollection<Person> _people;
        public Element ElementView
        {
            get { return _element; }
            set
            {
                if (value == _element) return;

                _element = value;
                this.OnPropertyChanged("ElementView");
            }
        }private Element _element;

        public MainWindow()
        {
            InitializeComponent();
            ElementView = Element.RadioButtons;
            mainWindow.DataContext = this;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion
    }
}

Our Element enum consists of three values, RadioButtons, TextBlocks, and CheckBoxes. This enum allows us to switch the view of our ItemsControl’s content. Below is Xaml for the layout of the page. It is very simple and contains only three radio buttons and the ItemsControl to display the people collection. I will comment on the DataTemplates following the code. I have also not included the SquareRadioButton style here as it is not pertinent to the article but you can find it in the link to the sample project at the end of the article. Here is the MainWindow.xaml.

<Window.Resources>
    <Local:EnumToBooleanConverter x:Key="EnumToBooleanConverter"/>

    <DataTemplate x:Key="RadioButtonTemplate">
        <RadioButton Style="{StaticResource SquareRadioButtonStyle}"
                     Content="{Binding FirstName}"
                     GroupName="People"/>
    </DataTemplate>

    <DataTemplate x:Key="TextBlockTemplate">
        <TextBlock Text="{Binding LastName}"
                   HorizontalAlignment="Center"/>
    </DataTemplate>

    <DataTemplate x:Key="CheckBoxTemplate">
        <CheckBox Content="{Binding Nickname}"
                  HorizontalAlignment="Center"
                  Margin="3">
        </CheckBox>
    </DataTemplate>

    <DataTemplate x:Key="PersonItemTemplate">
        <ContentPresenter x:Name="PeoplePresenter"
                          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"
                          ContentTemplate="{StaticResource RadioButtonTemplate}"/>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.RadioButtons}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource RadioButtonTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.TextBlocks}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource TextBlockTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.CheckBoxes}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource CheckBoxTemplate}"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <RadioButton Content="RadioButtons"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.RadioButtons}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="1"
                 Content="TextBlocks"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.TextBlocks}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="2"
                 Content="CheckBoxes"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.CheckBoxes}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

    <Grid Grid.Row="1" Grid.ColumnSpan="3" VerticalAlignment="Center" HorizontalAlignment="Center">
        <ItemsControl Width="200" Height="200" Margin="0"
                      ItemsSource="{Binding People}"
                      ItemTemplate="{StaticResource PersonItemTemplate}"/>
    </Grid>

</Grid>

As you can see, there are four data templates. The RadioButtonTemplate, TextBlockTemplate, and CheckBoxTemplate will  change the way a person item is displayed to a RadioButton, TextBlock, and CheckBox (obviously ;o)). Now comes the important part, the PersonItemTemplate. This template has a ContentPresenter that sets the default DataTemplate for a person item to the RadioButtonTemplate. Now, using DataTriggers and property setters, we can change the template to be used when the ElementView enum property changes. Thus, when the user clicks on the CheckBoxes radio button, the people items will then be displayed as check boxes showing just a nickname. The user can click on the TextBoxes radio button and will see only plain text showing last names. Finally, the default template shows each person item as styled radio button showing only first names on the button. Here are some screenshots showing the three views.

Here is a link to the sample project. Happy coding!!

Read Full Post »