finally I got my custom button almost act like I want it to act.
The problem is the code :D (IsChecked is a DependencyProperty)
<ControlTemplate.Triggers>
<!-- EventTriggers for LMBUp/LMBDown/MouseEnter/MouseLeave -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}" Value="False"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MouseEnter}"/>
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MouseLeave}"/>
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="True"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter TargetName="LayoutRoot" Property="Background" Value="Red"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsMouseOver}" Value="False"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=IsChecked}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter TargetName="LayoutRoot" Property="Background" Value="Red"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
I think it is a bit "dirty" and it doesn't fully act like expected.
Everytime the user clicks on the button IsChecked gets switched from true to false or vice versa.
Now if the following happens
IsChecked == true
User clicks to switch IsChecked to false
All the time the mouse is ON the button
then I would expect that the button becomes $MouseOverColor$ instead of $ButtonNormalBackground$ because of the first MultiDataTrigger.
My questions are if it is possible to simplify the code and to correct the last problem?
Thanks in advance!
You probably have 'value resolution strategy' issue. DependencyProperty has a set strategy of how to resolve the value, see here.
If your animations set the value of Background of LayoutRoot then other setters will not have affect until you stop the animation (in this case the setters are treated as "Local Value").
You can use <StopStoryboard> element in EnterActions of second and third triggers. Note that this will write a warning in the output window if the storyboard is already stopped when the trigger was activated.
--Alternatively--
You can use FillBehavior="Stop" in your storyboard.
Related
I would like to enable button only when all specified ComboBoxes have values. However, as soon as I add a second condition, button is enabled from the start
Here is my code
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=SensorPartNumberComboBox, Path=SelectedValue}" Value="{x:Null}"/>
<Condition Binding="{Binding ElementName=SensorTypeComboBox, Path=SelectedValue}" Value="{x:Null}"/>
<Condition Binding="{Binding ElementName=SensorBrandNameComboBox, Path=SelectedValue}" Value="{x:Null}"/>
<Condition Binding="{Binding ElementName=DimmingProtocolComboBox, Path=SelectedValue}" Value="{x:Null}"/>
<Condition Binding="{Binding ElementName=Wired_WirelessComboBox, Path=SelectedValue}" Value="{x:Null}"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
I would appreciate any help!
MultiDataTrigger requires all of the conditions to be true for it to take effect. I.e. it's equivalent to a logical AND.
In your example, if any value is non-null, the trigger won't take effect and the button will remain enabled.
For a logical OR, instead of using MultiDataTrigger, just use multiple DataTrigger. If any condition of any trigger is true, then that trigger will take effect, with precedence over the default setter for the property in the style.
For example:
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SensorPartNumberComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
<DataTrigger/>
<DataTrigger Binding="{Binding ElementName=SensorTypeComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
<DataTrigger/>
<DataTrigger Binding="{Binding ElementName=SensorBrandNameComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
<DataTrigger/>
<DataTrigger Binding="{Binding ElementName=DimmingProtocolComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
<DataTrigger/>
<DataTrigger Binding="{Binding ElementName=Wired_WirelessComboBox, Path=SelectedValue}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
<DataTrigger/>
</Style.Triggers>
Alternatively, you might consider putting the logic in your view model, with a single bool property you bind to, and which is set according to the bound SelectedValue properties for the various ComboBox controls.
Yet another alternative would be to use MultiBinding to bind the five view model properties that are bound to the ComboBox.SelectedValue properties, with a IMultiValueConverter that implements the logic.
Of course, these last two options work only if you've got a proper view model with binding set up in the first place (something I strongly encourage, if you haven't already).
In a DataGrid, I need to apply a style based on a certain condition implemented in my data context. But it has to be applied to the cells of the first column only.
Since a user is allowed to reorder columns, I don't know which is the first one in advance. So I tried to implement it at the CellStyle level, with a condition on the DisplayIndex:
<Condition Binding="{Binding Column.DisplayIndex, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridCell}}}" Value="0" />
I put it into a MultiDataTrigger with the condition on the data, that is:
<Condition Binding="{Binding IsInEvidence}" Value="False" />
IsInEvidence is a bool property in my row-level view model. If I leave this only, it works fine, but it gets applied to all the cells.
<DataGrid Items={Binding Items}/>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsInEvidence}" Value="False" />
<Condition Binding="{Binding Column.DisplayIndex, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGridCell}}}" Value="0" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Yellow"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<!-- Column defintions and so on -->
</DataGrid>
Change your Condition binding to use RelativeSource={RelativeSource Self}, then it works.
<Condition Binding="{Binding Column.DisplayIndex, RelativeSource={RelativeSource Self}}" Value="0"/>
I have been trying to make single edit click for a WPF DataGrid and I have looked at a lot of solutions on stackoverflow but have an additional use case that has not been addressed. I need to setup a style trigger but I need it to not just set IsEditing to true all the time. I need it based off of a second condition. The problem I'm running into is that it only works once per cell.
Here is the XAML that almost works.
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelected}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.IsEditing}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEditing" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
The problem with this is that it only works once per cell. Now if I change the first condition to be based on IsFocused it works all the time. The problem with that is then I can't click in the textbox within the cell for obvious reasons. I am really at a loss for why IsSelected only works once per cell. By the way if I take the view out of edit mode and put it back in using a button that is also on the view it will work again...once for each cell.
Does anyone have any insight into why this odd behavior happens?
Ok. I do not understand why IsSelected doesn't work but I did find a property that does work AND let's me type into the textbox in the cell. The trick was to use IsKeyboardFocusWithin property. Here is the complete solution that works as expected:
<DataGrid.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsKeyboardFocusWithin}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.IsEditing}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEditing" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
I have a MultiDataTrigger. I can bind to a DependencyProperty (DP) of the control and a view model property like this
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="True"/>
<Condition Binding="{Binding PerformTextSearchesInCommentary}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="LightGray"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
Great. But now, I want to use my own custom DP "HasTextMatch", which is defined in a static class. I can do this with a standard trigger like
<Style.Triggers>
<Trigger Property="Helpers:DataGridTextSearch.HasTextMatch" Value="True">
<Setter Property="Background" Value="LightGray"/>
</Trigger>
</Style.Triggers>
But I now want to include another Property. I have tried
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!--<Condition Property="Helpers:DataGridTextSearch.HasTextMatch" Value="True"/>--> This obviously won't work.
<Condition Binding="{Binding Helpers:DataGridTextSearch.HasTextMatch, RelativeSource={RelativeSource Self}}" Value="True"/>
<Condition Binding="{Binding PerformTextSearchesInCommentary}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="LightGray"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
But this does not work. I have searched but cannot seem to find out how to do this. How can I get the binding to my custom DP?
Thanks for your time.
The DataGridTextSearch.HasTextMatch property is an attached property. Please use the following Condition:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=(Helpers:DataGridTextSearch.HasTextMatch), RelativeSource={RelativeSource Self}}>
...
Additional information about property path (including attached properties) can be found here: PropertyPath XAML Syntax, MSDN.
I have a ListView with AlternationCount set to 2. I have a ListViewItem style that currently sets the background color of the ListViewItem to alternating colors, and I would like to add a third trigger which triggers a ColorAnimation to animate the color between Red and White when a property of the bound ViewModel is true (in this case a property called "Locked").
I came across this post, and tried the xaml at the bottom:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/e7897cbd-71d9-45e6-9b17-0cd5bde5859f
But, the animation doesn't appear to trigger for me until I mouse over or select the item, then select or mouse over a different item. I get the alternating colors from the first two MultiDataTriggers, but the animation doesn't trigger when the item is added to the collection. Here is my XAML:
<Style x:Key="alternatingListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="White" />
<Style.Triggers>
<MultiDataTrigger >
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ItemsControl.AlternationIndex)}" Value="0" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ListViewItem.IsSelected)}" Value="False" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Panel.IsMouseOver)}" Value="False" />
<Condition Binding="{Binding Locked}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="DarkGray" />
</MultiDataTrigger>
<MultiDataTrigger >
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ItemsControl.AlternationIndex)}" Value="1" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ListViewItem.IsSelected)}" Value="False" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Panel.IsMouseOver)}" Value="False" />
<Condition Binding="{Binding Locked}" Value="False" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="SlateGray" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(ListViewItem.IsSelected)}"
Value="False" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Panel.IsMouseOver)}"
Value="False" />
<Condition Binding="{Binding Locked}"
Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<ColorAnimation
Storyboard.TargetProperty="(Control.Background).(SolidColorBrush.Color)"
From="Red" To="White" Duration="0:0:0.2"
AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</MultiDataTrigger.EnterActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
I don't have the specific syntax for you example but you might try putting the triggers directly on the ListView (not in resources) and use ListView.ItemContainerStyle rather than Syle. I could not make the simple code below work in Resouces but when I moved it directly to the ListView it worked. I probably had a syntax error when it was in resources e.g.
<ListView AlternationCount="2"
ItemsSource="{Binding Path=...}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<!-- setting up triggers for alternate background colors -->
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="Gainsboro"></Setter>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="2">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>