WPF: IsMouseOver trigger not working for datatemplate - wpf

I'm trying to get the IsMouseOver trigger working for datatemplate. For some reason it is not firing. I also tried the http://www.wpfmentor.com/2009/01/how-to-debug-triggers-using-trigger.html but I do not see anything in the trace. Here is the code:
XAML:
<Window x:Class="FirstSImpleDataApp.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window4" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="tmptemplate">
<Border x:Name="brd" BorderBrush="Black" BorderThickness="2">
<TextBlock x:Name="txt">my text box</TextBlock>
</Border>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="txt" Property="Background" Value="Red"></Setter>
<Setter TargetName="txt" Property="Foreground" Value="Green"></Setter>
<Setter TargetName="brd" Property="Background" Value="Green"></Setter>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Canvas x:Name="can" Loaded="can_Loaded">
</Canvas>
</Window>
Code behind:
public partial class Window4 : Window
{
public Window4()
{
InitializeComponent();
}
private void can_Loaded(object sender, RoutedEventArgs e)
{
var tmp = this.TryFindResource("tmptemplate") as DataTemplate;
var obj = (FrameworkElement)tmp.LoadContent();
can.Children.Add(obj);
}
}
Any help is appreciated!

Your triggers actually work just fine. The problem is with how you're instantiating the template and adding it to your Canvas programmatically.
Apply the template directly in your Xaml to see it work:
<Canvas x:Name="can">
<ContentControl ContentTemplate="{StaticResource tmptemplate}" />
</Canvas>
If you want to apply it programmatically, apply the template to a ContentControl or ContentPresenter and drop it in the Canvas instead:
private void can_Loaded(object sender, RoutedEventArgs e)
{
var tmp = this.TryFindResource("tmptemplate") as DataTemplate;
can.Children.Add(new ContentPresenter { ContentTemplate = tmp });
}

Here is an alternative using Style triggers. The border remains black, but with a green margin from the TextBlock.
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Margin" Value="2"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="Border">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Green"/>
</Trigger>
</Style.Triggers>
</Style>
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="My Text Block"/>
</Border>

Related

Using a DataTrigger in my Style

I am trying to change the fill of a rectangle based upon a value in my ViewModel but despite having tried all the suggestions I have found online, it still isn't working.
The IsMouseOver trigger works fine but the DataTrigger is ignored despite the fact there is always either a 4 or 5 in my ViewModel property.
Could somebody show me where I may be going wrong?
Thanks.
This is my style:
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="4">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="5">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
EDIT: So it seems that my Binding was incorrect due to my incorrect assumption that the DataContext of the UserControl is where I should direct the binding.
The Rectangle is in the template for a ListBox and has "Items" as it's DataContext so by changing the "Binding" it now works.
Many thanks for all assistance though:
SOLUTION:
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding DataContext.Theme,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl} }" Value="4">
<Setter Property="Rectangle.Fill"
Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.Theme,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl} }" Value="5">
<Setter Property="Rectangle.Fill"
Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Change your TargetType="Rectangle"
Here is the code I used to test
<Style x:Key="FavouriteRectangleStyle" TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"/>
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Theme}" Value="4">
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Theme}" Value="5">
<Setter Property="Fill" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
And the implementation
<Rectangle Style="{StaticResource FavouriteRectangleStyle}"/>
Inside the constructor of your window or usercontrol what you haveused, you should set this.DataContext = this; Otherwise binding not properly worked for the corresponding window or usercontrol.
That probably because you miss to implement the INotifyPropertyChanged interface, update you Theme property to this:
private int _theme = 5;
public int Theme
{
get
{
return _theme;
}
set
{
if (_theme == value)
{
return;
}
_theme = value;
OnPropertyChanged();
}
}
and make sure your codebehind or your viewmodel is implementing the interface
public partial class MainWindow : Window,INotifyPropertyChanged
{
here a full working sample:
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<SolidColorBrush x:Key="content__star__hex646464__shadow" Color="Red"/>
<SolidColorBrush x:Key="content__star__favorit__hexebebeb__shadow" Color="Green"/>
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="4">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="5">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<Rectangle Style="{StaticResource FavouriteRectangleStyle}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="100"></Rectangle>
<Button Content="Change Theme" Click="ButtonBase_OnClick"></Button>
</StackPanel>
and the code behind :
public partial class MainWindow : Window,INotifyPropertyChanged
{
private int _theme = 5;
public int Theme
{
get
{
return _theme;
}
set
{
if (_theme == value)
{
return;
}
_theme = value;
OnPropertyChanged();
}
}
public MainWindow()
{
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Theme = Theme == 5 ? 4 : 5;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Answer in EDIT part of original question. Thanks for all assistance

How to change the background of tabitem by using trigger in xaml?

Here is my Xaml code
<Window.Resources>
<Style x:Key="SimpleTabControl" TargetType="{x:Type TabItem}">
<!--<Setter Property = "TabStripPlacement" Value = "Top"/>-->
<Setter Property = "Background" Value= "Gray"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="Width" Value="200"/>
<Setter Property="Height" Value="40"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="IsSelected" Value="False"/>
<Setter Property="Margin" Value="3,0"/>
<Style.Triggers>
<Trigger Property="TabItem.IsMouseOver" Value="true">
<Setter Property="Background" Value="LightGreen"></Setter>
</Trigger>
</Style.Triggers>
</Style>
<EventTrigger RoutedEvent="MouseEnter">
<EventTrigger.Actions>
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="Background" Value="LightGreen"></Setter>
</Style>
</Window.Resources>
<Grid Background="White" Height="1000" Width="auto" IsEnabled="True">
<TabControl Name="MainTab" Background="LightGray" SelectionChanged="MainTab_SelectionChanged">
<TabControl.Resources>
<Style TargetType="{x:Type TabPanel}">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Background" Value="White"/>
</Style>
</TabControl.Resources>
<TabItem Header="Input" x:Name="tabAlert1" Style="{StaticResource SimpleTabControl}" MouseEnter="TabItem_Enter" MouseLeave="TabItem_Leave" MouseDoubleClick="TabItem_Click" >
<!--<TabItem.Header>
<Border Padding="0,0">
<StackPanel Orientation="Horizontal" Background="Gray" HorizontalAlignment="Left" Width="200" Height="40" Margin="0,0">
<TextBlock Text="Input" Foreground="White" FontSize="15" TextAlignment="Right"/>
</StackPanel>
</Border>
</TabItem.Header>-->
<Label Content="Content goes here..." />
</TabItem>
<TabItem Header="Analysis" x:Name="tabAlert2" Style="{StaticResource SimpleTabControl}" />
<TabItem Header="Action???" x:Name="tabAlert3" Style="{StaticResource SimpleTabControl}"/>
</TabControl>
</Grid>
I don't know for some reason the trigger for isMouseOver is not triggering the back ground color change Change for Tab Item.
I have also tried the c# , code behind way , but with no success, but if change the foreground property, it works, but not he Background property.
Here is my C# Way
private void TabItem_Enter(object sender, System.Windows.Input.MouseEventArgs e)
{
//var tabItem = sender as TabItem;
//tabAlert2.Background = Brushes.Red;
//tabItem.Background = Brushes.Green;
var tabItem = sender as TabItem;
tabItem.Background = new SolidColorBrush(Colors.Gray);
}
I have Bound the mouse enter event to tab Item.this is very basic but am frustrated this not working?Any help much appreciated.
I'm not sure but I think you want to change the color of the Header?
If so, you can change the header template, try this code and if it works for you, add your other properties.
<Window.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Grid Name="Panel">
<Border Name="Border" BorderThickness="1,1,1,0" BorderBrush="Gainsboro" CornerRadius="4,4,0,0" Margin="2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightGreen"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="Border" Property="Background" Value="White" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Name="MainTab">
<TabItem Header="Input" x:Name="tabAlert1"/>
<TabItem Header="Analysis" x:Name="tabAlert2"/>
<TabItem Header="Action???" x:Name="tabAlert3"/>
</TabControl>
</Grid>
(Mouse is over second tab)

In WPF how do you put a border around the part of a window that has focus?

I have a Window that has two primary areas. One is a TextBox inside of a ScrollViewer and the other is a TabControl. I want to have a red border around the part that currently has focus, so I wrote the following code to do that
Xaml
<ScrollViewer BorderBrush="Red"
BorderThickness="0"
GotFocus="Border_GotFocus"
LostFocus="Border_LostFocus">
<TextBox/>
</ScrollViewer>
<TabControl BorderBrush="Red"
BorderThickness="0"
GotFocus="Border_GotFocus"
LostFocus="Border_LostFocus">
</TabControl>
Code
private void Border_LostFocus(object sender, RoutedEventArgs e)
{
var control = sender as Control;
if (control != null)
{
control.BorderThickness = new Thickness(0);
}
}
private void Border_GotFocus(object sender, RoutedEventArgs e)
{
var control = sender as Control;
if (control != null)
{
control.BorderThickness = new Thickness(2);
}
}
The problem is that if I click on the TextBox it does not update the border around the ScrollViewer. If I click on a Tab in the TabControl it updates the border so that I can see the border, but doesn't "remove" it when I click somewhere else. Is there some better way to do this?
First off, I'd highly recommend not using code and keeping this all in XAML.
Secondly, I'd also recommend using a Border to do this.
Third, I'd use IsKeyboardFocusedWithin in your style trigger.
<Window.Resources>
<Style x:Key="FocusedBorder" TargetType="Border">
<Setter Property="BorderThickness" Value="2"></Setter>
<Setter Property="BorderBrush" Value="Transparent"></Setter>
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel Width="400">
<ScrollViewer>
<Border Style="{StaticResource FocusedBorder}">
<TextBox>
</TextBox>
</Border>
</ScrollViewer>
<TabControl>
<TabItem Header="Foo">
<Border Style="{StaticResource FocusedBorder}">
<TextBox></TextBox>
</Border>
</TabItem>
<TabItem Header="Bar">
<Border Style="{StaticResource FocusedBorder}">
<TextBox></TextBox>
</Border>
</TabItem>
</TabControl>
</StackPanel>
The accepted answer did not work for me. However I figured out a way without using a border and still getting the same effect.
Below is the style used for the text box.
<Style x:Key="DefaultTextbox" TargetType="{x:Type TextBox}">
<Setter Property="FontSize" Value="14" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5 2 10 2" />
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontStyle" Value="Normal" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="Transparent" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="True" >
<Setter Property="BorderBrush" Value="#4E82EC" />
</Trigger>
</Style.Triggers>
</Style>
I did run into some trouble with the style. Originally I declared the border thickness and the color in the Trigger. The problem with that is when there was text in the box it seemed to jump. To get around jumping effect is to declare a border thickness by default and set the border to transparent so when the color is changed on the focus there is not a jumping effect.
Then you just have to set the style to your textbox:
<TextBox Style="{StaticResource DefaultTextbox}" />
And this is the effect you will see.

Multiple ToggleButton image with different highlight image in WPF

I am very new to WPF and needed some pointers as to why this is not working correctly.
I am trying to make a maximize button that will change to a restore button when clicked. I thought a toggle button with 2 different styles that would be changed in the code behind could work. I am first trying to get the maximize button working and have ran into a problem. I get the error 'System.Windows.Controls.Image' is not a valid value for the 'System.Windows.Controls.Image.Source' property on a Setter. in my xaml. I seem to be not understanding something completely. Any help would be most helpful :)
Ryan
<Style x:Key="Maximize" TargetType="{x:Type ToggleButton}">
<Style.Resources>
<Image x:Key="MaxButtonImg" Source="/Project;component/Images/maxbutton.png" />
<Image x:Key="MaxButtonHighlight" Source="/Project;component/Images/maxbutton-highlight.png" />
</Style.Resources>
<Setter Property="ContentTemplate">
<Setter.Value>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{DynamicResource MaxButtonImg}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="{DynamicResource MaxButtonHighlight}"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Setter.Value>
</Setter>
</Style>
<ToggleButton Name="MaxButton" Width="31" Height="31" BorderThickness="0" Click="MaxButton_Click" Margin="0,0,10,0" Tag="Max"
Style="{DynamicResource Maximize}" />
My code behind would do something simple like this:
private void MaxButton_Click(object sender, RoutedEventArgs e)
{
ToggleButton tg = (ToggleButton)sender;
if ( tg.IsChecked == true) {
tg.Style = (Style)FindResource("Restore");
this.WindowState = WindowState.Maximized;
} else {
tg.Style = (Style)FindResource("Maximize");
this.WindowState = WindowState.Normal;
}
}
You don't want to change the image on mouse over. I added my images to a folder called Images in the project and set build action on the images to Resource.
<Window.Resources>
<Image x:Key="minImage" Source="/Images/min.png" Height="16" Width="16" />
<Image x:Key="maxImage" Source="/Images/max.png" Height="16" Width="16" />
<Style TargetType="{x:Type ToggleButton}" x:Key="minMaxButtonStyle">
<Setter Property="Content" Value="{DynamicResource minImage}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource maxImage}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<ToggleButton Style="{StaticResource minMaxButtonStyle}" />
</StackPanel>
</Window>

Creating a common window look in WPF

How would I create a common window look in WPF? I'm not talking about just styling the window, I'm mean having a window that has a border, grid, and some other things in it.
Thanks.
You can create a ControlTemplate for the window. Here is a pretty basic example that has some controls and triggers. You can easily add more elements to make it fit your needs.
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type Window}">
<Border x:Name="WindowBorder" Style="{DynamicResource WindowBorderStyle}">
<Grid>
<Border Margin="4,4,4,4" Padding="0,0,0,0" x:Name="MarginBorder">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
<ResizeGrip Visibility="Collapsed" IsTabStop="false" HorizontalAlignment="Right" x:Name="WindowResizeGrip"
VerticalAlignment="Bottom" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ResizeMode" Value="CanResizeWithGrip"/>
<Condition Property="WindowState" Value="Normal"/>
</MultiTrigger.Conditions>
<Setter Property="Visibility" TargetName="WindowResizeGrip" Value="Visible"/>
<Setter Property="Margin" TargetName="MarginBorder" Value="4,4,4,20" />
</MultiTrigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="CornerRadius" TargetName="WindowBorder" Value="0,0,0,0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
You can use this ControlTemplate by setting the template property of the Window:
Template="{StaticResource MyWindowTemplate}"
You will want to use this in conjunction with a style like this:
<Style x:Key="MyWindowStyle" TargetType="{x:Type Window}">
<Setter Property="AllowsTransparency" Value="False" />
<Setter Property="WindowStyle" Value="SingleBorderWindow" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="ShowInTaskbar" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border>
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And set the style on your Window like this:
Style="{StaticResource MyWindowStyle}"
What I ended up doing was creating a base class that created the UI code that I wanted in every window. This allows me to set events for controls and have the event subscription in the base class. If there is a better way of doing this, like using xaml, I'd like to know.
public WindowBase()
{
Initialized += WindowBase_Initialized;
}
private void WindowBase_Initialized( object sender, EventArgs e )
{
Border border = new Border();
border.SetResourceReference( Control.StyleProperty, "WindowBorder" );
border.Child = new ContentPresenter { Content = this.Content};
this.Content = border;
}

Resources