Strange NullReferenceException when using Triggers - c#

I have a very simple TabItem template, and a single MultiTrigger with a SourceName attribute used on one Condition. The following XAML throws NullReferenceException when started, with no helpful info which would help me to fix the problem.
The strangest thing about this is the code works great if you remove the SourceName attribute. Or, if you leave the SourceName attribute, but remove MultiTrigger.EnterActions and use standard Setters instead, then it works as well. Only the combination of SourceName attribute and MultiTrigger.EnterActions throws NullReferenceException for no obvious reason. So what's wrong with this?
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" WindowStartupLocation="CenterScreen">
<Window.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<ControlTemplate.Resources>
<Storyboard x:Key="Storyboard_TabItem_Hover">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="background" Storyboard.TargetProperty="Opacity">
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="0.1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<Border x:Name="background" BorderBrush="Red" BorderThickness="1" Background="Yellow">
<Label Grid.Column="1" Content="{TemplateBinding Header}" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" SourceName="background" />
<Condition Property="IsSelected" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<BeginStoryboard x:Name="sbHover" Storyboard="{StaticResource Storyboard_TabItem_Hover}"/>
</MultiTrigger.EnterActions>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<TabControl Margin="10">
<TabItem Header="Tab 1" />
<TabItem Header="Tab 2" />
<TabItem Header="Tab 3" />
<TabItem Header="Tab 4" />
</TabControl>
</Window>
Update
As Greg Sansom pointed out, there is a simple workaround using MultiDataTrigger and Binding. However, I would still like to know why is the exception being thrown in the first place. I've searched Google and MSDN like crazy but haven't found anything. So what's the problem?

You can work around the problem by changing the MultiTrigger to a MultiDataTrigger, and specifying Binding instead of SourceName:
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=bg,Path=IsMouseOver}"
Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource Self},
Path=IsSelected}" Value="False" />
</MultiDataTrigger.Conditions>

Related

Keep Tooltip Open when Mouse Hover

I created a ribbon in WPF using .NET 4.0 framework. I am trying to make use of ToolTipTitle, ToolTipDescription and ToolTipFooterDescription. Since my tooltips will have hyperlinks, how can I make it so that when the mouse hovers over the tooltip, the tooltip stays open?
<rib:RibbonMenuButton ToolTipTitle="Title" ToolTipDescription="My Description" ToolTipFooterDescription="My Footer With a Link">
A good example of this functionality is with Microsoft Excel. When you hover over a ribbon button, an advanced tool tip will display to the user, and if the user hovers over the tooltip, it will remain open. I am trying to mimic that functionality.
I've updated my answer, the below is a well tested solution to my problem and will mimic Microsoft Office's tooltip generation:
First, create the popup control:
<Popup x:Class="WPF.Tooltip" AllowsTransparency="True"
x:Name="TT_Popup_Control"
xmlns:WPF="clr-namespace:Project_Namespace.WPF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Project_Namespace"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="5" />
<Setter Property="FontFamily" Value="Sans Serif" />
<Setter Property="FontSize" Value="11" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style TargetType="{x:Type TextBlock}" x:Key="WrappingStyle">
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</Grid.Resources>
<Border BorderBrush="LightGray" BorderThickness="1,1,0,0">
<Border
BorderThickness="0,0,15,15"
BorderBrush="Transparent"
CornerRadius="0"
Margin="0"
>
<Border.Effect>
<DropShadowEffect BlurRadius="10" Opacity="0.8" ShadowDepth="5" Direction="-40" RenderingBias="Quality" />
</Border.Effect>
<StackPanel Background="#efefef">
<Grid Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<!-- Title -->
<TextBlock Grid.ColumnSpan="2" Grid.Row="0" FontWeight="Bold" Text="{Binding Title, ElementName=TT_Popup_Control}" />
<!-- Description -->
<TextBlock Grid.Column="0" Grid.Row="1">
<ContentPresenter Content="{Binding Image, ElementName=TT_Popup_Control}" />
</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1">
<ContentPresenter Content="{Binding Desc, ElementName=TT_Popup_Control}">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource WrappingStyle}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</TextBlock>
<Separator Grid.ColumnSpan="2" Grid.Row="2" />
<!-- Image -->
<TextBlock Grid.ColumnSpan="2" Grid.Row="3">
<Image Margin="0,0,10,0" Width="16" Source="pack://application:,,,/Path/To/Image/help_icon.png" />
<TextBlock Margin="0,-5,0,0">
<Hyperlink RequestNavigate="Hyperlink_RequestNavigate" NavigateUri="{Binding Footer, ElementName=TT_Popup_Control}">Tell Me More</Hyperlink>
</TextBlock>
</TextBlock>
</Grid>
</StackPanel>
</Border>
</Border>
</Grid>
</Popup>
Next, create the code behind for this control. For those of you who want the C# equivalent, you can head here. This is where I got the reference for creating a control template.
Imports System.Windows
Namespace WPF
Public Class Tooltip
Public Shared ReadOnly TitleProperty As DependencyProperty = DependencyProperty.Register("Title", GetType(Object), GetType(WPF.Tooltip), New PropertyMetadata(vbNull))
Public Shared ReadOnly DescProperty As DependencyProperty = DependencyProperty.Register("Desc", GetType(Object), GetType(WPF.Tooltip), New PropertyMetadata(vbNull))
Public Shared ReadOnly FooterProperty As DependencyProperty = DependencyProperty.Register("Footer", GetType(Object), GetType(WPF.Tooltip), New PropertyMetadata(vbNull))
Public Shared ReadOnly ImageProperty As DependencyProperty = DependencyProperty.Register("Image", GetType(Object), GetType(WPF.Tooltip), New PropertyMetadata(vbNull))
Public Property Title As Object
Get
Return GetValue(TitleProperty)
End Get
Set(value As Object)
SetValue(TitleProperty, value)
End Set
End Property
Public Property Desc As Object
Get
Return GetValue(DescProperty)
End Get
Set(value As Object)
SetValue(DescProperty, value)
End Set
End Property
Public Property Footer As Object
Get
Return GetValue(FooterProperty)
End Get
Set(value As Object)
SetValue(FooterProperty, value)
End Set
End Property
Public Property Image As Object
Get
Return GetValue(ImageProperty)
End Get
Set(value As Object)
SetValue(ImageProperty, value)
End Set
End Property
Private Sub Hyperlink_RequestNavigate(sender As Object, e As Navigation.RequestNavigateEventArgs)
System.Diagnostics.Process.Start(e.Uri.ToString())
End Sub
End Class
End Namespace
The third step is to create the popup functionality, which is best done via styles. I created my style as a resource dictionary (you can call it PopupStyle.xaml, but you can also embed your style directly within the popup xaml control markup:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Classic"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<Style x:Key="TT_Popup" TargetType="Popup">
<Setter Property="VerticalOffset" Value="20" />
<Setter Property="MaxWidth" Value="500" />
<Setter Property="MinWidth" Value="50" />
<Style.Triggers>
<DataTrigger Binding="{Binding PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="OpenPopupStoryBoard" >
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:1.00" Value="True"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<BeginStoryboard x:Name="ClosePopupStoryBoard">
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.35" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
<DataTrigger Binding="{Binding PlacementTarget.IsMouseCaptured, RelativeSource={RelativeSource Self}}" Value="True">
<DataTrigger.EnterActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<BeginStoryboard x:Name="CloseImmediatelyPopupStoryBoard" >
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsOpen" FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="0:0:0.0" Value="False"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding PlacementTarget.IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False">
<DataTrigger.ExitActions>
<RemoveStoryboard BeginStoryboardName="CloseImmediatelyPopupStoryBoard" />
</DataTrigger.ExitActions>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<PauseStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.EnterActions>
<Trigger.ExitActions>
<PauseStoryboard BeginStoryboardName="OpenPopupStoryBoard"/>
<ResumeStoryboard BeginStoryboardName="ClosePopupStoryBoard" />
</Trigger.ExitActions>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
And finally, you must call your newly created ToolTip control in your Xaml markup like so:
<UserControl xmlns:WPF="clr-namespace:Project_Namespace.WPF"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
>
<UserControl.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="PopupStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="button_name" />
<!-- Tooltips -->
<WPF:Tooltip Style="{StaticResource TT_Popup}" PlacementTarget="{Binding ElementName=button_name}">
<WPF:Tooltip.Title>
Tooltip Title
</WPF:Tooltip.Title>
<WPF:Tooltip.Image>
<Image Source="pack://application:,,,/Path/To/Image.png" />
</WPF:Tooltip.Image>
<WPF:Tooltip.Desc>
Your Description here
</WPF:Tooltip.Desc>
<WPF:Tooltip.Footer>
www.example.com
</WPF:Tooltip.Footer>
</WPF:Tooltip>
</Grid>
</UserControl
So this may be a complicated way to achieve this functionality, however once implemented, these tooltips are much better than the default tooltips, especially if you require user interaction.

Popup positioning in wpf using c#

I have placed a popup on a button mouseover.Each time when i mouse over that button my custom designed popup is being showed perfectly.Butit doesn't point the button perfectly. How to do so ..?
Now my popup looks like
I want that arrow mark to point that help button How to acheive it..
Here is my code for the button and popup in xaml
<telerik:RadButton Name="btnH" Grid.Column="1" HorizontalAlignment="Left" Margin="444,56,0,0" Grid.Row="2" VerticalAlignment="Top"
Width="23" Height="23" BorderThickness="6" BorderBrush="#4E4E4E">
<Image Source="Images/help.png" />
<telerik:RadButton.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard TargetName="TooltipPopup" TargetProperty="IsOpen">
<BooleanAnimationUsingKeyFrames FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
and below is the custom usercontrol xaml which amn calling in popup
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="WPFTest.UCToolTip"
mc:Ignorable="d" Height="231.493" Width="362.075"
Background="Transparent" >
<UserControl.Resources>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
</Style>
</UserControl.Resources>
<Grid Margin="10,0,0,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Background="red" Margin="0,0,182,133">
</Grid>
<Polygon
Points="0.5,0 15,0, 0,30" Stroke="Orange" Fill="Orange" Margin="0,98,0,101" />
</Grid>
use this style for your Popup:
<Style TargetType="Popup">
<Style.Triggers>
<Trigger Property="IsOpen" Value="true">
<Setter Property="PlacementTarget" Value="{Binding ElementName=btnH }" />
<Setter Property="Placement" Value="Top" />
<Setter Property="VerticalOffset" Value="-5" />
<Setter Property="HorizontalOffset" Value="5" />
</Trigger>
</Style.Triggers>
</Style>
I think that Kylo pointed you to the right answer. If you get rid of the margin in the usercontrol it should work.
This is the code for the usercontrol.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="WpfTest.UCToolTip"
mc:Ignorable="d" Height="130" Width="180"
Background="Transparent" >
<UserControl.Resources>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
</Style>
</UserControl.Resources>
<Grid Margin="10,0,0,0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Background="red" Margin="0,0,0,32">
</Grid>
<Polygon Points="0.5,0 15,0, 0,30" Stroke="Orange" Fill="Orange" Margin="0,98,0,0" />
</Grid>
</UserControl>
And the code for the window.
<Window x:Class="WpfTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
xmlns:uc="clr-namespace:WpfTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="Popup">
<Style.Triggers>
<Trigger Property="IsOpen" Value="true">
<Setter Property="PlacementTarget" Value="{Binding ElementName=btnH }" />
<Setter Property="Placement" Value="Top" />
<Setter Property="VerticalOffset" Value="0" />
<Setter Property="HorizontalOffset" Value="145" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<telerik:RadButton Name="btnH" Grid.Column="1" HorizontalAlignment="Left" Margin="300,175,0,0" Grid.Row="2" VerticalAlignment="Top"
Width="50" Height="23" BorderThickness="6" BorderBrush="#4E4E4E">
<Image Source="Images/help.png" />
<telerik:RadButton.Triggers>
<EventTrigger RoutedEvent="MouseEnter">
<BeginStoryboard>
<Storyboard TargetName="TooltipPopup" TargetProperty="IsOpen">
<BooleanAnimationUsingKeyFrames FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="MouseLeave">
<BeginStoryboard>
<Storyboard TargetName="TooltipPopup" TargetProperty="IsOpen">
<BooleanAnimationUsingKeyFrames FillBehavior="HoldEnd">
<DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</telerik:RadButton.Triggers>
</telerik:RadButton>
<Popup x:Name="TooltipPopup" AllowsTransparency="True">
<StackPanel>
<uc:UCToolTip></uc:UCToolTip>
</StackPanel>
</Popup>
</Grid>
</Window>
Here is a small library with balloons for WPF that I think does what you want.
Usage:
<geometry:Balloon ConnectorAngle="25"
CornerRadius="15"
PlacementOptions="Bottom, Center"
PlacementTarget="{Binding ElementName=Target}">
<!-- content here -->
</geometry:Balloon>

Receiving InvalidOperationException when a UserControl is created

My WPF application has a UserControl in it called AlarmItem. Here's the XAML:
<UserControl x:Class="CarSystem.CustomControls.AlarmItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cs="clr-namespace:CarSystem.CustomControls"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100"
DataContext="{Binding Path=Alarm, RelativeSource={RelativeSource Self}}">
<UserControl.Resources>
<Style TargetType="{x:Type cs:AlarmItem}">
<Setter Property="IsFlashing" Value="False" />
<Setter Property="FlashColor" Value="{DynamicResource NotPendingAlarmBorder}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExpired}" Value="True">
<Setter Property="FlashColor" Value="{DynamicResource ExpiredAlarmBorder}" />
<Setter Property="IsFlashing" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsPending}" Value="True">
<Setter Property="IsFlashing" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsWhiteListAlarm}" Value="True">
<Setter Property="FlashColor" Value="{DynamicResource WhiteListAlarmBorder}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="AlarmItemBorderStyle" TargetType="Border">
<Setter Property="BorderThickness" Value="2" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsExpired}" Value="True">
<Setter Property="BorderThickness" Value="4" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsPending}" Value="True">
<Setter Property="BorderThickness" Value="4" />
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Border HorizontalAlignment="Center"
Margin="5"
Height="100"
Name="Border"
Style="{StaticResource AlarmItemBorderStyle}"
VerticalAlignment="Center"
Width="100">
<Border.Resources>
<Storyboard x:Key="FlashingStoryboard" AutoReverse="False" RepeatBehavior="Forever">
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
Duration="00:00:01"
Storyboard.TargetName="Border"
Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)">
<DiscreteColorKeyFrame KeyTime="00:00:00" Value="Black" />
<DiscreteColorKeyFrame KeyTime="00:00:00.5" Value="{Binding Path=FlashColor, RelativeSource={RelativeSource AncestorType={x:Type cs:AlarmItem}}}" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Border.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="FlashStates">
<VisualState x:Name="FlashingOn"
Storyboard="{StaticResource ResourceKey=FlashingStoryboard}" />
<VisualState x:Name="FlashingOff" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Grid.Row="0"
Name="AlarmImage"
Source="{Binding Path=Image, RelativeSource={RelativeSource AncestorType={x:Type cs:AlarmItem}}}"
Stretch="Fill" />
<cs:ResponseTimer Expired="Timer_Expired"
Grid.Row="1"
HideIfExpired="True"
IsTabStop="False"
MinHeight="10"
x:Name="TheTimer"
TimeoutPeriod="00:02:30"
VerticalAlignment="Bottom" />
</Grid>
</Border>
</UserControl>
The code behind for this control has the following code in it that runs when the IsFlashing property changes:
private void OnIsFlashingChanged( object sender, bool flashNow ) {
if ( flashNow ) {
if ( !VisualStateManager.GoToElementState( Border, "FlashingOn", true ) ) {
Log.Debug( "AlarmItem.xaml.cs: Visual State Manager transition failed." );
}
} else {
if ( !VisualStateManager.GoToElementState( Border, "FlashingOff", true ) ) {
Log.Debug( "AlarmItem.xaml.cs: Visual State Manager transition failed." );
}
}
}
private static void OnIsFlashingInvalidated( DependencyObject d, DependencyPropertyChangedEventArgs e ) {
AlarmItem alarmItem = (AlarmItem) d;
alarmItem.OnIsFlashingChanged( d, (bool) e.NewValue );
}
The line that sets the visual state for the Border to FlashingOn throws the following InvalidOperationException when it executes:
System.InvalidOperationException: 'BorderBrush' property does not point to a DependencyObject in path '(0).(1)'.
What have I done wrong? I recently added the Style and the triggers that set the IsFlashing property; in the past, I did it in the code-behind and this error did not occur then.
After much research, trial & error, and heartache, the reason I'm getting the error is because the Border class's BorderBrush property is a Brush, not a SolidColorBrush. Brush is an abstract type that SolidColorBrush and all the various gradient brush classes descend from.

WPF Selected ListBoxItem with custom border

I'm trying to create a ListBoxItem template, that will be with rounded border at selection.
I got this xaml, which doesn't work on selection:
<ListBox x:Name="filtersListBox" Grid.Row="1"
Background="Transparent" BorderThickness="0"
ItemsSource="{Binding FilterItems}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center">
<Border CornerRadius="8" BorderThickness="0" BorderBrush="Orange"
Margin="2" Background="Transparent" Name="itemBorder"
Width="275" VerticalAlignment="Center"
FocusManager.IsFocusScope="True" Focusable="True">
<Border.Effect>
<DropShadowEffect BlurRadius="1" ShadowDepth="2" Color="DarkOrange" Opacity="0.3"/>
</Border.Effect>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="UIElement.IsFocused" Value="True">
<Setter Property="Background" Value="Blue"/>
</Trigger>
<EventTrigger RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.25"
To="2"
Storyboard.TargetProperty="BorderThickness"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.25"
To="0"
Storyboard.TargetProperty="BorderThickness"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Text}" Margin="10, 2"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So this is the ListBox that I'm working on.
The MouseEnter and MouseLeave events, work great!
However, the trigger of UIElement.IsFocused is not working.
Any advice would be very appreciated! :)
Thanks, Alex.
This is so easy to do, I'm quite surprised that nobody suggested this yet. Either define two DataTemplates or two ControlTemplates, one for the default look and one for the selected look. Then just add this Style (this first example uses DataTemplates):
<Style x:Key="SelectionStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
You would use it like this:
<ListBox ItemContainerStyle="{StaticResource SelectionStyle}" ... />
Here is the other example using two ControlTemplates (used in the same way):
<Style x:Key="SelectionStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Template" value="{StaticResource DefaultTemplate}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Template" value="{StaticResource SelectedTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
I'll leave you to define what you want the items to look like as you know that best. One last note... if you use this method (using ControlTemplates), make sure that you add a ContentPresenter so that the content of the items will still be shown. See the Control.Template Property page on MSDN for an example.
Have you tried setting Focusable property to true. By default the propery is false.
Take a look at this link:
http://msdn.microsoft.com/en-us/library/system.windows.uielement.focusable%28v=vs.110%29.aspx
If that doesnt help then maybe this approach will fit you more.
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="GotFocus" Handler="ListBoxItem_GotFocus"/>
<EventSetter Event="LostFocus" Handler="ListBoxItem_LostFocus"/>
</Style>
</ListBox.Resources>
Why dont you set the Template for ItemContainerStyle, then you can use the Trigger with property IsSelected = true. See code below:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<collections:ArrayList x:Key="StringArray">
<system:String>Hei</system:String>
<system:String>Hei</system:String>
<system:String>Hei</system:String>
<system:String>Hei</system:String>
</collections:ArrayList>
</Window.Resources>
<Grid>
<ListBox x:Name="filtersListBox" Grid.Row="1"
Background="Transparent" BorderThickness="0"
ItemsSource="{StaticResource StringArray}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border CornerRadius="8" BorderThickness="0" BorderBrush="Orange"
Margin="2" Background="Transparent" Name="itemBorder"
Width="275" VerticalAlignment="Center"
FocusManager.IsFocusScope="True" Focusable="True">
<Border.Effect>
<DropShadowEffect BlurRadius="1" ShadowDepth="2" Color="DarkOrange" Opacity="0.3"/>
</Border.Effect>
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="itemBorder" Property="Background" Value="Blue"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent"/>
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center">
<Border CornerRadius="8" BorderThickness="0" BorderBrush="Orange"
Margin="2" Background="Transparent" Name="itemBorder"
Width="275" VerticalAlignment="Center"
FocusManager.IsFocusScope="True" Focusable="True">
<Border.Effect>
<DropShadowEffect BlurRadius="1" ShadowDepth="2" Color="DarkOrange" Opacity="0.3"/>
</Border.Effect>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="UIElement.IsFocused" Value="True">
<Setter Property="Background" Value="Blue"/>
</Trigger>
<EventTrigger RoutedEvent="Border.MouseEnter">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.25"
To="2"
Storyboard.TargetProperty="BorderThickness"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="Border.MouseLeave">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="0:0:0.25"
To="0"
Storyboard.TargetProperty="BorderThickness"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding }" Margin="10, 2"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>

Failed Binding in WPF Data Validation

I'm trying to bind to a property from my control style to a property defined in the control. The control inherits from TextBox and is called ChangeTextBoxWithRangeUnits. I'm trying to bind to them from a ValidationRule class that I created. Here is the Setter for where I'm trying to validate the text
<Setter Property="Text">
<Setter.Value>
<Binding RelativeSource="{RelativeSource Self}"
Path="Text"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<basic:DoubleValidationRule>
<basic:DoubleValidationRule.MinMaxDependencyObject>
<basic:MinMaxDependencyObject Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MinimumValue}"
Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MaximumValue}" />
</basic:DoubleValidationRule.MinMaxDependencyObject>
</basic:DoubleValidationRule>
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
I don't understand why it says that the source binding cannot be found. Here is the error:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Frasca.Simplicity.Controls.UnitControls.ChangeTextBoxWithRangeUnits', AncestorLevel='1''. BindingExpression:Path=MinimumValue; DataItem=null; target element is 'MinMaxDependencyObject' (HashCode=16320155); target property is 'Minimum' (type 'Double')
EDIT Complete XAML
<Style x:Key="ChangeTextBoxWithRangeUnits"
TargetType="{x:Type local:ChangeTextBoxWithRangeUnits}"
BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Validation.ErrorTemplate"
Value="{x:Null}" />
<Setter Property="Height"
Value="64" />
<Setter Property="FontSize"
Value="22" />
<Setter Property="Margin"
Value="5" />
<Setter Property="Background"
Value="LightGray" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="KeyboardNavigation.TabNavigation"
Value="None" />
<Setter Property="AllowDrop"
Value="true" />
<Setter Property="ScrollViewer.PanningMode"
Value="VerticalFirst" />
<Setter Property="Stylus.IsFlicksEnabled"
Value="False" />
<Setter Property="Text">
<Setter.Value>
<Binding RelativeSource="{RelativeSource Self}"
Path="Text"
NotifyOnValidationError="True">
<Binding.ValidationRules>
<basic:DoubleValidationRule>
<basic:DoubleValidationRule.MinMaxDependencyObject>
<basic:MinMaxDependencyObject Minimum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MinimumValue}"
Maximum="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithRangeUnits}}, Path=MaximumValue}" />
</basic:DoubleValidationRule.MinMaxDependencyObject>
</basic:DoubleValidationRule>
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ChangeTextBoxWithUnits}">
<Grid>
<Border Name="bg"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Width="{TemplateBinding Width}"
CornerRadius="15"
SnapsToDevicePixels="true">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal"
Grid.Row="0"
Name="mValueStackPanel"
VerticalAlignment="Bottom"
HorizontalAlignment="Center">
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<TextBox Name="PART_UnitTextBlock"
Style="{DynamicResource InlineTextBox}"
Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=Units}"
FontSize="14"
Margin="3,0,0,0"
Foreground="{TemplateBinding Foreground}" />
</StackPanel>
<StackPanel Orientation="Horizontal"
Grid.Row="2"
HorizontalAlignment="Center"
VerticalAlignment="Top">
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=Label}"
FontSize="14"
Foreground="{TemplateBinding Foreground}" />
</StackPanel>
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:ChangeTextBoxWithUnits}}, Path=DisabledText}"
Foreground="{TemplateBinding Foreground}"
Grid.Row="0"
FontSize="14"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Visibility="Collapsed"
x:Name="mDisabledTextBlock" />
<!-- Horizontal line -->
<Rectangle Height="1"
Margin="10,0"
Grid.Row="1"
Opacity="0.15"
SnapsToDevicePixels="True"
Fill="{TemplateBinding Foreground}" />
<!-- Object which flashes when the textbox is selected -->
<Border Grid.RowSpan="3"
Background="White"
Name="FlashObject"
CornerRadius="15"
Opacity="0">
<Border.Effect>
<BlurEffect Radius="20" />
</Border.Effect>
</Border>
<!-- Object which flashes when the textbox has a validation error-->
<Border Grid.RowSpan="3"
Grid.ColumnSpan="2"
Background="Red"
Name="ErrorFlashObject"
CornerRadius="15"
Opacity="0">
<Border.Effect>
<BlurEffect Radius="20" />
</Border.Effect>
</Border>
</Grid>
</Border>
<!-- Object which glows when the user makes a change to the text value. -->
<Border Width="{Binding ElementName=bg, Path=ActualWidth}"
Height="{Binding ElementName=bg, Path=ActualHeight}"
CornerRadius="15"
Background="#FFF066"
Name="ChangeGlowObject"
Panel.ZIndex="-1"
Visibility="Collapsed">
<Border.Effect>
<BlurEffect Radius="20" />
</Border.Effect>
</Border>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasChangedValue"
Value="True" />
<Condition Property="IsEnabled"
Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Visibility"
TargetName="ChangeGlowObject"
Value="Visible" />
</MultiTrigger>
<Trigger Property="IsEnabled"
Value="False">
<Setter Property="Background"
Value="#686868" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled"
Value="False" />
<!--<Condition Property="ShowTextWhenDisabled"
Value="False" />-->
</MultiTrigger.Conditions>
<Setter Property="Visibility"
TargetName="mDisabledTextBlock"
Value="Visible" />
<Setter Property="Visibility"
TargetName="mValueStackPanel"
Value="Collapsed" />
</MultiTrigger>
<!-- trigger to flash the object when the textbox has an error -->
<Trigger Property="Validation.HasError"
Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.2"
From="0"
To="1"
Storyboard.TargetName="ErrorFlashObject"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation.EasingFunction>
<PowerEase Power="2"
EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation BeginTime="00:00:00.2"
Duration="00:00:00.5"
From="1"
To="0"
Storyboard.TargetName="ErrorFlashObject"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation.EasingFunction>
<PowerEase Power="2"
EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<!-- trigger to flash the object when the textbox is selected -->
<EventTrigger RoutedEvent="FocusManager.GotFocus">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation BeginTime="00:00:00"
Duration="00:00:00.2"
From="0"
To="1"
Storyboard.TargetName="FlashObject"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation.EasingFunction>
<PowerEase Power="2"
EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation BeginTime="00:00:00.2"
Duration="00:00:00.5"
From="1"
To="0"
Storyboard.TargetName="FlashObject"
Storyboard.TargetProperty="Opacity">
<DoubleAnimation.EasingFunction>
<PowerEase Power="2"
EasingMode="EaseIn" />
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
RelativeSource bindings don't walk up your XML, they walk up the Visual Tree. That means you can only use it whenever the element you are binding is within that tree.
In this case, your DoubleValidationRule is not a UIElement, and it is not in the visual tree, so RelativeSouce bindings on this rule will fail.
Unfortunately, without knowing the implementation or purpose of your DoubleValidationRule.MinMaxDependencyObject, its hard to say what you should do. You might be able to get around this limitation by altering the implementation of the validation rule.
One way that will work is that you can refer to an element in the tree by its x:Name value
Minimum="{Binding MinimumValue, ElementName=TheMinimumTarget}
But that probably means you can't do this using a Style, as each use will (probably) be bound to a different element on your form.
Looking at your edit... Wat? You're binding the text of the control to the text of the control in order to attach a validation rule? That's... I don't even.
I'd suggest subclassing the control in order to add validation logic (e.g., override the property metadata for the Text dependency property), and then apply your template to this new type.

Categories