Popup on image rollover - c#

Here is what I am trying to accomplish:
I have a series of small images (24x24) indicating various system statuses. When a user rolls over of the images (e.g. a system alert image) I want a popup to display the first 5 alerts and a More... option (if there are more than 5 alerts). While the list is displayed, I want the user to be able to select one of the alerts or the More option and display the details.
The problem I am having is:
I cannot prevent the popup from disappearing when the mouse leaves the image to select an item.
Here is a segment of XAML from my User Control I am using to display the popup.
<Image x:Name="SmallImage"
Source="{Binding ElementName=root, Path=ImageSource}"
Height="{Binding ElementName=root, Path=Height}"
Width="{Binding ElementName=root, Path=Width}"
Grid.Row="0" />
<Popup Name="ItemPopup"
AllowsTransparency="True"
PopupAnimation="Fade"
HorizontalOffset="-35"
VerticalOffset="0"
Grid.Row="1">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SmallImage, Path=IsMouseOver, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
<DataTrigger.Setters>
<Setter Property="IsOpen" Value="True" />
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<Border x:Name="MyBorder" BorderBrush="#72777F" Background="White" BorderThickness="1,1,1,1" CornerRadius="5,5,5,5">
<Grid>
<Label Content="This is your list of items."></Label>
</Grid>
</Border>
</Popup>

This is because your Trigger will fire everytime the IsMouseOver Property will change and in case Trigger condition fails it will set the Property to its default i.e false in this case. You will need two Triggers here, one for opening the popup in case of IsMouseOver is true and second is to keep popup open if IsMouseOver becomes false and Popup was already open.
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SmallImage, Path=IsMouseOver, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
<DataTrigger.Setters>
<Setter Property="IsOpen" Value="True" />
</DataTrigger.Setters>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=SmallImage, Path=IsMouseOver, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="False"/>
<Condition Binding="{Binding IsOpen, RelativeSource={RelativeSource Self}}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsOpen" Value="True" />
</MultiDataTrigger>
</Style.Triggers>

Related

Reusing style trigger in wpf

I have done some research on this but failed to find out on how to reuse the style trigger.
I have style trigger set on Label and I am using Data trigger to set content. I have multiple labels in same view and also in different view. The Data bound is of same type except its different property of same data context.
Consider following 2 different labels where I need to display performance of 2 person - PersonA and PersonB. The value displayed for both the labels will be based on Performance format selected.
<Label Grid.Row="5" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding DataModel.PersonA.Performance.Value.Fractional}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding DataModel.PersonA.Performance.Value.Decimal}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{DataModel.PersonA.Performance.Value.US}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
<Label Grid.Row="6" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<Label.Style>
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding DataModel.PersonB.Performance.Value.Fractional}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding DataModel.PersonB.Performance.Value.Decimal}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{DataModel.PersonB.Performance.Value.US}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
If you see, the only difference is the first trigger takes PersonA and the second trigger takes PersonB. I have 10 instances of such label spread in same and multiple views. Is there is way I can define this trigger once in Resources and bind whatever data I want to from each Label.
Looking forward for the solution.
Regards,
Abdyax
configurate setters to obtain values not from Label DataContext, but another Label property - Tag
<Style BasedOn="{StaticResource SomeGlobalStaticStyle}" TargetType="Label">
<Style.Triggers>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Fractional">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.Fractional, RelativeSource={RelativeSource Self}}}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="Decimal">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.Decimal, RelativeSource={RelativeSource Self}}" />
</DataTrigger>
<DataTrigger Binding="{Binding PersonModel.PerformanceFormat}" Value="US">
<Setter Property="Content" Value="{Binding Tag.Performance.Value.US, RelativeSource={RelativeSource Self}}}" />
</DataTrigger>
</Style.Triggers>
</Style>
and bind labels Tag to different properties:
<Label Grid.Row="5" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Tag="{Binding DataModel.PersonA}"/>
<Label Grid.Row="6" Grid.Column="9"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" Tag="{Binding DataModel.PersonB}"/>
both labels should pick our default style for TargetType="Label" with triggers
Is there is way I can define this trigger once in Resources and bind whatever data I want to from each Label.
Short answer: No, I am afraid not. You cannot replace the Value of a Setter but reuse the rest of the Style in XAML.
You could create the styles programmatically using the XamlReader.Parse method. Then you can simply replace the "PersonA" with "PersonB" in the string that you pass to the method.
But there is no way to do this generally in pure XAML.

Why is this WPF Button background not changing behind an image?

My squares are either SaddleBrown or WhiteSmoke in color, shifting to DarkTurquoise when selected. This works when there is no image on top of the squares. When I have a (PNG) image on top of the square the original SaddleBrown/WhiteSmoke color shows behind it but when the color is supposed to change to DarkTurquoise nothing happens to the background color of the square.
What could be the problem?
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="Square"
Command="{Binding DataContext.BoardGUI.SquareClickCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Grid Background="{TemplateBinding Background}">
<Image Source="{Binding Source, Converter={StaticResource NullImageConverter}}"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding SquareColor}" Value="Dark">
<Setter TargetName="Square" Property="Background" Value="SaddleBrown"/>
</DataTrigger>
<DataTrigger Binding="{Binding SquareColor}" Value="White">
<Setter TargetName="Square" Property="Background" Value="WhiteSmoke"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="Square" Property="Background" Value="DarkTurquoise"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
As determined in the comments, based on the screenshot and the definition of the triggers, it appears that "SquareColor" is likely getting set or changed after IsSelected is set.

Why do multiple DataTriggers on same control not work?

I want to create an image button with different highlighting effects on IsMouseOver and on IsPressed events. The code I'm using:
<Button Style="{StaticResource AccentedSquareButtonStyle}" x:Name="button" Background="Transparent" BorderThickness="0" Foreground="Transparent" Grid.Column="1" HorizontalAlignment="Left" Margin="142,-2,0,0" VerticalAlignment="Top" Width="75">
<Grid Background="Transparent">
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="Content" Value="{StaticResource printIcon}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsPressed}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconClicked}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconHighlighted}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Button>
The code works only if I use the DataTriggers separately, but in this form only the MouseOver highlighting effect is used. I'm using MahApps framework. What am I doing wrong?
As you have found out, this has to do with the order in which your Triggers have been defined. Since you're binding to two different paths (both in which are related to the mouse), the order that they are defined is important as it will determine which one is triggered first.
When you click a button, the order of events are:
Mouse hovers over
Button is pressed
So, what happens with your current trigger:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsPressed}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconClicked}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconHighlighted}"/>
</DataTrigger>
The mouse hover is the only one that actuality is doing the setting because your mouse pointer is "over" the button while it's pressed. This implementation should work though if you press the button then "drag away" while still holding down left-click. This is not ideal though.
To fix, just swap the order if the data-triggers (since order does matter)
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsMouseOver}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconHighlighted}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=IsPressed}" Value="True" >
<Setter Property="Content" Value="{StaticResource printIconClicked}"/>
</DataTrigger>
Please see Wpf tutorials blog for detailed explanation about multi triggers. Here is the link:
http://www.wpf-tutorial.com/styles/multi-triggers-multitrigger-multidatatrigger/
Let me know if this answers your question.

ListBox Loading Animation

I have a list box that should be filed when user click on Button,
sometimes the data loaded is quickly, and sometimes it take some time... Is there a simple way to load some animation like a clock, or something which can give the user indication that the process is running?
I use mvvm with button commands
<ListBox Width="30"
Visibility="{Binding IsDataLoaded,
Converter= {StaticResource BooleanToVisibilityConverter}}"
ItemsSource="{Binding Collection}"
FontSize ="15"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="DarkGray" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
<Button Content="Go" Command="{Binding GoCommand}" IsEnabled="{Binding IsGoEnabled}" IsDefault="True" Width="60"
/>
Sounds like you're looking for a BusyIndicator like in the Extended Toolkit.

Change Label content with IsMouseOver on checkbox

Hei,
Im creating a button what has a checkbox on top of it. Now when user hovers over the checkbox with a mouse i would like to change the text on the button.
Here's what i have currently:
<Button Grid.Row="1" Margin="0,0,136,12" Name="btnRefresh" Height="28" VerticalAlignment="Bottom" HorizontalAlignment="Right" Width="120" Click="btnRefresh_Click">
<Grid Width="111">
<CheckBox Height="16" HorizontalAlignment="Left" Margin="0,2,0,0" Name="cbAutoRefresh" VerticalContentAlignment="Center">
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Content" Value="Auto Refresh" />
</Trigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<Label Name="btnLabel" Margin="32,-4,25,-4">Refresh</Label>
</Grid>
</Button>
But like this it changes the content of the checkbox, how can i change the btnLabel content?
Currently you're just modifing the style of the ComboBox, thats why the Trigger just changes the Content of your ComboBox.
To listen to other controls properties in a Trigger, you can use Databinding like so:
<Label Name="btnLabel" Margin="32,-4,25,-4">
<Label.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=comboBoxName, Path=IsMouseOver}" Value="False"
<Setter Property="Content" Value="Refresh"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=comboBoxName, Path=IsMouseOver}" Value="True"
<Setter Property="Content" Value="Auto Refresh"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>

Categories