Home   Subscribe   Linkedin
  Archive Contact  

WPF Triggers

When a style is applied, the settings described in a setter elements are applied unconditionally.
On the other hand, triggers are a way to wrap one or more setter elements in a condition.

There are three types of Triggers available.

    PropertyTrigger
    Data Trigger
    EventTrigger

Property Trigger

Property trigger basically identifies the property we are watching and the value that we are waiting for.
When this value occurs, the condition becomes true and corresponding setters will execute

<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">  
  <Style.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
      <Setter Property="Foreground" Value="Red" />
    </Trigger>
  </Style.Triggers>
 </Style>

In the above code whenever mouse is over button then the foreground becomes Red

MultiTrigger

MultiTrigger is used to set action on Multiple Property change. It will execute when all condition are satisfy within MulitTrigger.Condition.

<MultiTrigger>
      <MultiTrigger.Conditions>
        <Condition Property="IsFocused" Value="True" />
        <Condition Property="Content" Value="{x:Null}" />
</MultiTrigger.Conditions> <Setter Property="ToolTip" Value="click or spacebar to move here" /></MultiTrigger>

Data Triggers
Unlike property triggers, which check only WPF dependency properties, data triggers can check any thing to which we can
bind. Property triggers are generally used to check WPF visual element properties, data triggers are normally used to check the properties of nonvisual objects used as content

<DataTemplate>
 <TextBlock Text="{Binding Path=PlayerName}"/>
    <DataTemplate.Triggers>
      <DataTrigger Binding="{Binding Path=PlayerName}" Value="Sachin">
        <Setter Property="Foreground" Value="Green" />
      </DataTrigger>
      <DataTrigger Binding="{Binding Path=PlayerName}" Value="O">
        <Setter Property="Foreground" Value="Red" />
      </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

Multicondition Data Triggers
Just as we can combine property triggers into “and” conditions similarly
we can combine datatriggers using the Multi DataTrigger element.

<Stylex:Key="MyStyle" TargetType="{x:Type TextBlock}">
  <Style.Triggers>
    <MultiDataTrigger>
      <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Path=PlayerName}" Value="X" />
        <Condition Binding="{Binding Path=IsPartOfWin}" Value="True" />
      </MultiDataTrigger.Conditions>
      <Setter Property="Foregroung" Value ="Red"/>
    </MultiDataTrigger>
  </Style.Triggers>
</Style>

Event Triggers

Whereas property triggers check for values on dependency properties and data triggers
check for values on CLR properties, event triggers watch for events. When an event (like a Click event ) happens,
an event trigger responds by raising an animation-related event.

<Style TargetType="{x:Type Button}">
  <Style.Triggers>
    <EventTrigger RoutedEvent="Click">
      <BeginStoryboard>
        <Storyboard>
          <DoubleAnimation Storyboard.TargetProperty="Opacity"
                           From="0" To="1" Duration="0:0:2" />
        </Storyboard>
      </BeginStoryboard>
    </EventTrigger>
  </Style.Triggers>
</Style>
Posted by: Mohd Ahmed

Categories: Triggers, WPF, Xaml

Tags: , , ,

Multilevel Converter in WPF

Converters basically provides a translation between your binding source and destination.

Generally we use single level converters like (Bool to Visibility Converter), (Color to Brush Converter), (Enum to Bool converter) and list is endless..

Now consider a scenario in which we require Multi-level conversion i.e from A => B => C.

Lets take an example, Suppose we have Bool to Visibility Converter and Color to Bool converter, and we want a translation like Color to Visibility Conversion.

So instead of making Color To Visibility converter we can make some generic converter that allows us to do multilevel Conversion.

Below is the Code of that Multi level Converter

public class ValueConverterGroup : IValueConverter
  {
    #region Data
    /// <summary>
    /// Stores the list of values, on which the converter has to be applied.
    /// </summary>
    private readonly ObservableCollection<IValueConverter> converters = new ObservableCollection<IValueConverter>();

    /// <summary>
    /// Stores the dictionary, mapping of value with its conversion attribute.
    /// </summary>
    private readonly Dictionary<IValueConverter, ValueConversionAttribute> cachedAttributes = new Dictionary<IValueConverter, ValueConversionAttribute>();

    #endregion

    #region Constructor
    /// <summary>
    /// 
    /// </summary>
    public ValueConverterGroup()
    {
      this.converters.CollectionChanged += this.OnConvertersCollectionChanged;
    }

    #endregion

    #region Converters

    /// <summary>
    /// Returns the list of IValueConverters contained in this converter.
    /// </summary>
    public ObservableCollection<IValueConverter> Converters
    {
      get { return this.converters; }
    }

    #endregion

    #region IValueConverter Members

    object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      object output = value;

      for (int i = 0; i < this.Converters.Count; ++i)
      {
        IValueConverter converter = this.Converters[i];
        Type currentTargetType = this.GetTargetType(i, targetType, true);
        output = converter.Convert(output, currentTargetType, parameter, culture);

        // If the converter returns 'DoNothing' then the binding operation should terminate.
        if (output == Binding.DoNothing)
          break;
      }

      return output;
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      object output = value;

      for (int i = this.Converters.Count - 1; i > -1; --i)
      {
        IValueConverter converter = this.Converters[i];
        Type currentTargetType = this.GetTargetType(i, targetType, false);
        output = converter.ConvertBack(output, currentTargetType, parameter, culture);

        // When a converter returns 'DoNothing' the binding operation should terminate.
        if (output == Binding.DoNothing)
          break;
      }

      return output;
    }

    #endregion

    #region Private Helpers

    #region GetTargetType

    /// <summary>
    /// Returns the target type for a conversion operation.
    /// </summary>
    protected virtual Type GetTargetType(int converterIndex, Type finalTargetType, bool convert)
    {
      // If the current converter is not the last/first in the list, 
      // get a reference to the next/previous converter.
      IValueConverter nextConverter = null;
      if (convert)
      {
        if (converterIndex < this.Converters.Count - 1)
        {
          nextConverter = this.Converters[converterIndex + 1];
          if (nextConverter == null)
            throw new InvalidOperationException("The Converters collection of the ValueConverterGroup contains a null reference at index: " + (converterIndex + 1));
        }
      }
      else
      {
        if (converterIndex > 0)
        {
          nextConverter = this.Converters[converterIndex - 1];
          if (nextConverter == null)
            throw new InvalidOperationException("The Converters collection of the ValueConverterGroup contains a null reference at index: " + (converterIndex - 1));
        }
      }

      if (nextConverter != null)
      {
        ValueConversionAttribute conversionAttribute = cachedAttributes[nextConverter];

        // If the Convert method is going to be called, we need to use the SourceType of the next 
        // converter in the list.  If ConvertBack is called, use the TargetType.
        return convert ? conversionAttribute.SourceType : conversionAttribute.TargetType;
      }

      // If the current converter is the last one to be executed return the target type passed into the conversion method.
      return finalTargetType;
    }

    #endregion // GetTargetType

    #region OnConvertersCollectionChanged

    void OnConvertersCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      // The 'Converters' collection has been modified, so validate that each value converter it now
      // contains is decorated with ValueConversionAttribute and then cache the attribute value.

      IList convertersToProcess = null;
      if (e.Action == NotifyCollectionChangedAction.Add ||
        e.Action == NotifyCollectionChangedAction.Replace)
      {
        convertersToProcess = e.NewItems;
      }
      else if (e.Action == NotifyCollectionChangedAction.Remove)
      {
        foreach (IValueConverter converter in e.OldItems)
          this.cachedAttributes.Remove(converter);
      }
      else if (e.Action == NotifyCollectionChangedAction.Reset)
      {
        this.cachedAttributes.Clear();
        convertersToProcess = this.converters;
      }

      if (convertersToProcess != null && convertersToProcess.Count > 0)
      {
        foreach (IValueConverter converter in convertersToProcess)
        {
          object[] attributes = converter.GetType().GetCustomAttributes(typeof(ValueConversionAttribute), false);

          if (attributes.Length != 1)
            throw new InvalidOperationException("All value converters added to a ValueConverterGroup must be decorated with the ValueConversionAttribute attribute exactly once.");

          this.cachedAttributes.Add(converter, attributes[0] as ValueConversionAttribute);
        }
      }
    }
    #endregion
    #endregion
  }

And in Xaml simply define your Converters in a required order, like

<convert:ValueConverterGroup x:Key="colorToVisibilityConverter">
        <convert:ColorToBooleanConverter />
        <convert:BooleanToVisibilityConverter />
      </convert:ValueConverterGroup>

 and thats all. You can have n levels of conversion A => B => C => D.....N levels

Posted by: Mohd Ahmed

Categories: C#, Prism, Silverlight, WPF, Xaml

Tags: , , , ,