«

Aug
11

Using Style & Triggers For Complex Interactivity

Many times I try to take as much of the presentation logic out of the viewmodel as possible.  While using a MVVM pattern, sometimes it’s easy to be lazy and create boolean or visibility properties to handle logic on the front-end.  This week I’m showing a quick way to abstract that logic to the view only and not have to worry about cluttering your viewmodel.

Simply, I use Style.Trigger to handle the different ways I want my view to appear dependent on a DataBinding.  In WPF, this is incredibly easy using the DataTrigger class.  It allows you to bind to a datacontext property and then change the appearance of your object based on the property’s value.

<Style x:Key="ListControlStyle" TargetType="{x:Type ContentControl}">
	<Setter Property="Opacity" Value="0"/>
	<Setter Property="Content" Value=""/>
	<Setter Property="Margin" Value="20"/>
	<Setter Property="VerticalAlignment" Value="Center"/>
	<Setter Property="HorizontalAlignment" Value="Stretch"/>
	<Setter Property="HorizontalContentAlignment" Value="Center"/>
	<Setter Property="Padding" Value="8"/>
	<Setter Property="VerticalContentAlignment" Value="Center"/>
	<Setter Property="Background" Value="#FF337E83"/>
	<Setter Property="Foreground" Value="White"/>
	<Setter Property="FontWeight" Value="Bold"/>
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="{x:Type ContentControl}">
				<Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" CornerRadius="12">
					<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
				</Border>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
	<Style.Triggers>
		<DataTrigger Binding="{Binding ItemList}" Value="{x:Null}">
			<Setter Property="Content" Value="List Has Not Been Searched Yet [ItemList = null]" />
			<Setter Property="Opacity" Value="1" />
		</DataTrigger>
		<DataTrigger Binding="{Binding ItemList.Count}" Value="0">
			<Setter Property="Content" Value="Search Results Are Empty [ItemList.Count = 0]" />
			<Setter Property="Opacity" Value="1" />
		</DataTrigger>
	</Style.Triggers>
</Style>

The Style easily set some default values I wanted for my control and a ControlTemplate. In this example I wanted to create a rounded banner that shows in certain conditions:

  1. [DEFAULT] By default I want the Control to have an opacity of zero and empty Content
  2. [ListItems.Count = 0] If the List exists but it is empty, then I want the item to show and alert the user.  This usually happens if you complete a search and get a empty result back.  It’s also very informative to the user that their search did complete but returned nothing.
  3. [ListItems = null] If the List is null and never existed, then the user should know that they need to search.  It’s easy to have the List value null at the creation of the viewmodel and then populate the property on searches with a collection (either empty or with values)

The style properties appear near the top are the lowest precedence.  They are the “default” values in this scenario and the triggers override any of those properties when their conditions are met.  One easy mistake to make is to forget that if you set a value on the ContentControl itself (not in the style) then that would have precedence over the trigger.

Permanent link to this article: http://blogs.dotnetkicks.com/seesharprun/2011/08/11/using-style-triggers-for-complex-interactivity/