views:

1082

answers:

3

I've been struggling with this code for some time now and can't seem to find any complete answers to my question. I've created a small sample to illustrate the problem:

<ListView >
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
        <StackPanel Margin="0,0,20,0" IsItemsHost="True" />
    </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.Items>
        <TextBlock>Test1</TextBlock>
        <TextBlock>Test2</TextBlock>
        <TextBlock>Test3</TextBlock>
        <TextBlock>Test4</TextBlock>
        <TextBlock>Test5</TextBlock>
    </ListView.Items>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListViewItem}">
                    <Grid>
                        <ContentPresenter/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                 <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True" />
                                 <Condition Property="IsSelected" Value="True"/>
                            </MultiTrigger.Conditions>
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </MultiTrigger>
                    </ControlTemplate.Triggers>
                 </ControlTemplate>
             </Setter.Value>
         </Setter>
         </Style>
     </ListView.ItemContainerStyle>
 </ListView>

According to the MultiTrigger settings, the selected item should reappear when the mouse is no longer over the selected item. This code, however, produces an InvalidOperationException with the message "Must have non-null value for 'Property'." If you remove the Condition that uses the "Binding" attribute the exception is not thrown. In the MSDN documentation it states that you must have either the Property or Binding attribute set. The above code functions like the Binding attribute is not set. In fact, in all my test cases, it doesn't matter what the Binding attribute is set to; the exception is still thrown. Any thoughts?

+6  A: 

This is one of those times when you have to suck it up and admit that you've made a bonehead mistake. However, to save some other unlucky soul from the same fate, I'll reveal my epiphany.

First, if I had read all of the documentation I would have read the part that said if you're using the condition's "Binding" attribute, it needs to be included in a MultiDataTrigger element (instead of the MutiTrigger element in my posted example).

Second, upon making those changes, the MultiTrigger element is replace with the following code:

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True" />
        <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}" Value="True"/>
    </MultiDataTrigger.Conditions>
    <Setter Property="Visibility" Value="Collapsed"/>
</MultiDataTrigger>

Now the example works but because the selected item is collapsed, the trigger condition switches back and forth causing the selected item to flicker in and out of view. Makes sense but admittedly not what I intended.

At any rate, hope this helps someone from making the same bonehead mistake!

dan
+1 For admitting boneheadedness and providing a solution. Someone else will run up against this. Missing the word "Data" is pretty easy to overlook.
Anderson Imes
+1 For leading me to the solution much faster than actually hunting down and reading through documentation. The error should have explained this instead of relying on documentation.
jpierson
A: 

On a very similar note, pulling IsMouseOver from a border as the main data template content, and pulling the IsSelected from the Ancestor. Its interesting that both conditions have to have a relative path, I would assume that the default path would be the local datacontext. Thanks for the above solution.

Broken Code

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
                   Value="True" />
        <Condition SourceName="Border"
                   Property="IsMouseOver"
                   Value="True" />
    </MultiDataTrigger.Conditions>
    <Setter TargetName="Border"
            Property="Background"
            Value="{StaticResource OnBrushSelected}" />
</MultiDataTrigger>

Working Code

<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}, Path=IsMouseOver}"
                   Value="True" />
        <Condition Binding="{Binding Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
                   Value="True" />
    </MultiDataTrigger.Conditions>
    <Setter TargetName="Border"
            Property="Background"
            Value="{StaticResource OnBrushSelected}" />
</MultiDataTrigger>
Gauthier
A: 

Hi,

Hope you don't mind I answer this question so lately. I've kind of the same situation as you do, but I want to have my code compatible with Silverlight. However, Silverlight doesn't support Triggers. Does anyone knows an alternative for this?

Thank you!

Peter van Kekem