none
MultiDataTrigger in style problem

    Question

  • Code Block
    <StackPanel Orientation="Horizontal" Margin="30,0,0,0">
      <StackPanel.Resources>
        <Style TargetType="StackPanel">
          <Style.Triggers>
            <MultiDataTrigger>
              <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding Path=LastName2}" Value="{x:Null}"/>
                <Condition Binding="{Binding Path=FirstName2}" Value="{x:Null}"/>
              </MultiDataTrigger.Conditions>
              <Setter Property="Visibility" Value="Collapsed"/>
            </MultiDataTrigger>
           </Style.Triggers>
        </Style>
      </StackPanel.Resources>
      <!-- ... -->
    </StackPanel>

     

     

    Why am I getting a run-time error during InitializeComponents() when using the above trigger?

     

    I have used a nearly identical MultiDataTrigger inside a DataTemplate before, and I can successfully use a single DataTrigger with the same Binding=, Value= on the StackPanel above.

     

     

    -- Exception Info -- (Line 138 Position 87 is the Value="{x:Null}" in the first condition above).

    System.Windows.Markup.XamlParseException occurred
      Message=" 'NullExtension' is not a valid trigger condition.  Error at object 'System.Windows.Condition' in markup file 'SBR Management Solution;component/contactwindow.xaml' Line 138 Position 87."
      Source="PresentationFramework"
      LineNumber=138
      LinePosition=87
      NameContext="Conditions"
      StackTrace:
           at System.Windows.Markup.XamlParseException.ThrowException(String message, Exception innerException, Int32 lineNumber, Int32 linePosition, Uri baseUri, XamlObjectIds currentXamlObjectIds, XamlObjectIds contextXamlObjectIds, Type objectType)
           at System.Windows.Markup.XamlParseException.ThrowException(ParserContext parserContext, Int32 lineNumber, Int32 linePosition, String message, Exception innerException)
           at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
           at System.Windows.Markup.StyleBamlRecordReader.ReadRecord(BamlRecord bamlRecord)
           at System.Windows.Markup.BamlRecordReader.Read(Boolean singleRecord)
           at System.Windows.Markup.StyleTreeBuilderBamlTranslator.ParseFragment()
           at System.Windows.Markup.TreeBuilder.Parse()
           at System.Windows.Markup.XamlStyleSerializer.ConvertBamlToObject(BamlRecordReader reader, BamlRecord bamlRecord, ParserContext context)
           at System.Windows.Markup.BamlRecordReader.ReadElementStartRecord(BamlElementStartRecord bamlElementRecord)
           at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
           at System.Windows.Markup.BamlRecordReader.ReadElement(Int64 startPosition, XamlObjectIds contextXamlObjectIds, Object dictionaryKey)
           at System.Windows.ResourceDictionary.CreateObject(Int32 valuePosition, Object key)
           at System.Windows.ResourceDictionary.RealizeDeferContent(Object key, Object& value, Boolean& canCache)
           at System.Windows.ResourceDictionary.GetValueWithoutLock(Object key, Boolean& canCache)
           at System.Windows.ResourceDictionary.GetValue(Object key, Boolean& canCache)
           at System.Windows.ResourceDictionary.FetchResource(Object resourceKey, Boolean allowDeferredResourceReference, Boolean mustReturnDeferredResourceReference, Boolean& canCache)
           at System.Windows.FrameworkElement.FindResourceOnSelf(Object resourceKey, Boolean allowDeferredResourceReference, Boolean mustReturnDeferredResourceReference)
           at System.Windows.FrameworkElement.FindResourceInTree(FrameworkElement feStart, FrameworkContentElement fceStart, DependencyProperty dp, Object resourceKey, Object unlinkedParent, Boolean allowDeferredResourceReference, Boolean mustReturnDeferredResourceReference, DependencyObject boundaryElement, InheritanceBehavior& inheritanceBehavior, Object& source)
           at System.Windows.FrameworkElement.FindResourceInternal(FrameworkElement fe, FrameworkContentElement fce, DependencyProperty dp, Object resourceKey, Object unlinkedParent, Boolean allowDeferredResourceReference, Boolean mustReturnDeferredResourceReference, DependencyObject boundaryElement, Boolean isImplicitStyleLookup, Object& source)
           at System.Windows.FrameworkElement.FindImplicitStyleResource(FrameworkElement fe, Object resourceKey, Object& source)
           at System.Windows.FrameworkElement.GetRawValue(DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry& entry)
           at System.Windows.FrameworkElement.EvaluateBaseValueCore(DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry& newEntry)
           at System.Windows.DependencyObject.EvaluateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry, OperationType operationType)
           at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, OperationType operationType)
           at System.Windows.DependencyObject.InvalidateProperty(DependencyProperty dp)
           at System.Windows.FrameworkElement.UpdateStyleProperty()
           at System.Windows.TreeWalkHelper.OnResourcesChanged(DependencyObject d, ResourcesChangeInfo info, Boolean raiseResourceChangedEvent)
           at System.Windows.TreeWalkHelper.InvalidateOnResourcesChange(FrameworkElement fe, FrameworkContentElement fce, ResourcesChangeInfo info)
           at System.Windows.ResourceDictionary.NotifyOwners(ResourcesChangeInfo info)
           at System.Windows.ResourceDictionary.SetKeys(ArrayList keyCollection, List`1 staticResourceValuesList, ParserContext context)
           at System.Windows.ResourceDictionary.SetDeferableContent(Byte[] buffer, ParserContext context, Object rootElement, ArrayList keyCollection, List`1 staticResourceValuesList)
           at System.Windows.Markup.BamlRecordReader.ReadDeferableContentStart(BamlDeferableContentStartRecord bamlRecord)
           at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
           at System.Windows.Markup.BamlRecordReader.Read(Boolean singleRecord)
           at System.Windows.Markup.TreeBuilderBamlTranslator.ParseFragment()
           at System.Windows.Markup.TreeBuilder.Parse()
           at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
           at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
           at ThisApp.ContactWindow.InitializeComponent() in d:\Projects\Projects.SBR\SBR Management Solution\ContactWindow.xaml:line 1
           at ThisApp.ContactWindow..ctor() in D:\Projects\Projects.SBR\SBR Management Solution\ContactWindow.xaml.cs:line 30
      InnerException: System.ArgumentException
           Message="'NullExtension' is not a valid trigger condition."
           Source="PresentationFramework"
           StackTrace:
                at System.Windows.Condition.set_Value(Object value)
                at System.Windows.Markup.StyleBamlRecordReader.ReadElementEndRecordForVisualTrigger()
                at System.Windows.Markup.StyleBamlRecordReader.ReadElementEndRecord(Boolean fromNestedBamlRecordReader)
                at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)
           InnerException:

    Wednesday, November 14, 2007 4:43 AM

Answers

  • It's hoakie, but the Condition class explicitly denies the use of a markup extension to set the Value property.  (To me, this represents just plain laziness on someone's part!)  You'll get the same error if you try to use a StaticResource reference or any other markup extension for the Value property.

     

    The easiest solution, albeit a pain in the petutie, is to use a value converter in your bindings.  Just make sure you return a string representation for null ("***NULL***" or some such thing) and trigger off of that.

     

    It really would be nice to see this restriction removed (or maybe hear some convincing argument as to why markup extensions are precluded for the Value of Condition objects but not for the Value of DataTrigger objects... good luck with that).
    Wednesday, November 14, 2007 5:27 AM
  • Below is a little hack trick that takes advantage of the sequential nature of trigger evaluation and leverages a couple of borrowed attached properties to give you the ability to use a multidatatrigger in your scenario:

     

    Code Block

     

    <Style.Triggers>

      <DataTrigger Binding="{Binding Path=LastName2}"

          Value="{x:Null}">

        <Setter Property="TextSearch.TextPath"

            Value="***NULL***"/>

      </DataTrigger>

      <DataTrigger Binding="{Binding Path=FirstName2}"

          Value="{x:Null}">

        <Setter Property="Storyboard.TargetName"

            Value="***NULL***"/>

      </DataTrigger>

      <MultiTrigger>

        <MultiTrigger.Conditions>

          <Condition Property="TextSearch.TextPath"

              Value="***NULL***"/>

          <Condition Property="Storyboard.TargetName"

              Value="***NULL***"/>

        </MultiTrigger.Conditions>

        <Setter Property="Visibility" Value="Collapsed"/>

      </MultiTrigger>

    </Style.Triggers>

     

     

     

    These particular DPs should be safe to use in this scenario since they do not inherit in the tree and are not likely to be used directly on your styled object (unless its an ItemsControl, in which case you should use a different property than TextSearch.TextPath). 

     

    It should be noted that in this case, the required value converter would be one of the easiest converters to write... but maybe you're trying to use loose xaml and can't support any code behind or some such thing. Smile

     

    Wednesday, November 14, 2007 5:56 AM

All replies

  • It's hoakie, but the Condition class explicitly denies the use of a markup extension to set the Value property.  (To me, this represents just plain laziness on someone's part!)  You'll get the same error if you try to use a StaticResource reference or any other markup extension for the Value property.

     

    The easiest solution, albeit a pain in the petutie, is to use a value converter in your bindings.  Just make sure you return a string representation for null ("***NULL***" or some such thing) and trigger off of that.

     

    It really would be nice to see this restriction removed (or maybe hear some convincing argument as to why markup extensions are precluded for the Value of Condition objects but not for the Value of DataTrigger objects... good luck with that).
    Wednesday, November 14, 2007 5:27 AM
  • Below is a little hack trick that takes advantage of the sequential nature of trigger evaluation and leverages a couple of borrowed attached properties to give you the ability to use a multidatatrigger in your scenario:

     

    Code Block

     

    <Style.Triggers>

      <DataTrigger Binding="{Binding Path=LastName2}"

          Value="{x:Null}">

        <Setter Property="TextSearch.TextPath"

            Value="***NULL***"/>

      </DataTrigger>

      <DataTrigger Binding="{Binding Path=FirstName2}"

          Value="{x:Null}">

        <Setter Property="Storyboard.TargetName"

            Value="***NULL***"/>

      </DataTrigger>

      <MultiTrigger>

        <MultiTrigger.Conditions>

          <Condition Property="TextSearch.TextPath"

              Value="***NULL***"/>

          <Condition Property="Storyboard.TargetName"

              Value="***NULL***"/>

        </MultiTrigger.Conditions>

        <Setter Property="Visibility" Value="Collapsed"/>

      </MultiTrigger>

    </Style.Triggers>

     

     

     

    These particular DPs should be safe to use in this scenario since they do not inherit in the tree and are not likely to be used directly on your styled object (unless its an ItemsControl, in which case you should use a different property than TextSearch.TextPath). 

     

    It should be noted that in this case, the required value converter would be one of the easiest converters to write... but maybe you're trying to use loose xaml and can't support any code behind or some such thing. Smile

     

    Wednesday, November 14, 2007 5:56 AM
  • I solved it by adding a property to the data context object "FirstAndLastName2IsNull" and used a single DataTrigger to work off of that. This is only possible because I created the object and was free to change it.

     

    Wednesday, November 14, 2007 7:05 PM
  • I just ran into this. Very frustrating. It works fine for a simple DataTrigger but not a MultiDataTrigger.... very strange.

    Matt
    • Proposed as answer by Bolli1983 Monday, May 03, 2010 7:50 AM
    Friday, July 18, 2008 2:16 AM
  • Instead of hacking around that with fake properties, just create a converter that will take a string and determine if it's null or not and return a boolean.

    In your converters..

        public class NullToBoolConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                // If Value is not null, return true
                if (parameter != null)
                { }

                if (value is string && string.IsNullOrEmpty((string)value))
                    return false;
                else if (value == null)
                    return false;
                else
                    return true;
            }

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

    Now in XAML just bind to it w/a converter.

    <local:NullToBoolConverter x:Key="NullToBoolConverter"/>

    ...
    <Condition Binding="{Binding Path=LastName2, Converter={StaticResource NullToBoolConverter}" Value="False"/>
    ...

    Eric Rodewald
    Thursday, August 07, 2008 8:18 PM
  • Make custom extension like this:

        public class MyNullExtension : MarkupExtension

        {

            public override object ProvideValue(IServiceProvider serviceProvider)

            {

                return null;

            }

        }

    And use it as {ns:MyNull}. I have resolved same problem with TypeExtension by creating MyTypeExtension. 

    Monday, September 13, 2010 10:35 AM