WPF: IsPressed trigger of ControlTemplate not working - c#

I use a custom control named ImageButton to display a button with different images. ImageButton contains dependency properties:
public ImageSource DisabledImageSource
{
get { return (ImageSource)GetValue(DisabledImageSourceProperty); }
set { SetValue(DisabledImageSourceProperty, value); }
}
public ImageSource NormalImageSource
{
get { return (ImageSource)GetValue(NormalImageSourceProperty); }
set { SetValue(NormalImageSourceProperty, value); }
}
public ImageSource HoverImageSource
{
get { return (ImageSource)GetValue(HoverImageSourceProperty); }
set { SetValue(HoverImageSourceProperty, value); }
}
public ImageSource PushedImageSource
{
get { return (ImageSource)GetValue(PushedImageSourceProperty); }
set { SetValue(PushedImageSourceProperty, value); }
}
public static readonly DependencyProperty DisabledImageSourceProperty =
DependencyProperty.Register("DisabledImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata());
public static readonly DependencyProperty NormalImageSourceProperty =
DependencyProperty.Register("NormalImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata());
public static readonly DependencyProperty HoverImageSourceProperty =
DependencyProperty.Register("HoverImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata());
public static readonly DependencyProperty PushedImageSourceProperty =
DependencyProperty.Register("PushedImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata());
It's style is defined as follows:
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image Name="image" Source="{Binding NormalImageSource, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="image" Property="Source" Value="{Binding DisabledImageSource, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter TargetName="image" Property="Source" Value="{Binding PushedImageSource, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter TargetName="image" Property="Source" Value="{Binding HoverImageSource, RelativeSource={RelativeSource TemplatedParent}}"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Every thing works fine except IsPressed. I can never see the image I set in PushedImageSource and I can't figure out why.
Any help would be appriciated :)
EDIT 1:
Here's the xaml code that tests it
<Window x:Class="SMEClient.WPF.Tester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:is="clr-namespace:Project1.SearchBox;assembly=Project1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Project1;component/SearchBox/ImageButton.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel>
<is:ImageButton NormalImageSource="C:\Users\Public\Pictures\Sample Pictures\Koala.jpg"
PushedImageSource="C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum.jpg"
HoverImageSource="C:\Users\Public\Pictures\Sample Pictures\Jellyfish.jpg"/>
</StackPanel>
</Grid>
</Window>

Your triggers are not mutual exclusive, in this case the order in which triggers are added to the Triggers collection comes into play. Your last trigger IsMouseOver overrides the Source property after IsPressed trigger setts the correct image, since a button is Pressed only if the mouse is over (when using mouse of course).
Try setting the IsPressed trigger last in the Triggers collection, and the IsPressed trigger will be applied even though IsMouseOver is also true.

OK, so I found a workaround which is kind of ugly, but it works
<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Image Name="Normal" Source="{Binding NormalImageSource, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None" Visibility="Visible" />
<Image Name="Hover" Source="{Binding HoverImageSource, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None" Visibility="Hidden"/>
<Image Name="Clicked" Source="{Binding PushedImageSource, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None" Visibility="Hidden"/>
<Image Name="Disabled" Source="{Binding DisabledImageSource, RelativeSource={RelativeSource TemplatedParent}}" Stretch="None" Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Hover" Property="Visibility" Value="Visible"/>
<Setter TargetName="Clicked" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Disabled" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Hover" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Clicked" Property="Visibility" Value="Visible"/>
<Setter TargetName="Disabled" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Normal" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Hover" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Clicked" Property="Visibility" Value="Hidden"/>
<Setter TargetName="Disabled" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

Can I have a custom parameter linked to a style in my control?

I have a series of images, and I want them all the have MouseOver replacements. I could implement a style for each of them with something like:
<Image Source="C:\Image1.png" Style="{StaticResource Image1Style}"/>
...
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}" x:Key="Image1Style">
<Setter Property="Source" Value="C:\Image1.jpg"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="C:\Image2.jpg"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
But such an implementation would require a whole style block for every image. I'd rather want one unified style that I can then specify the IsMouseOver property in the tag of the actual control, like so:
<Image Source="C:\Image1.png" Style="{StaticResource Image1Style}" IsMouseOver="C:\Image1MouseOver.png" />
I thought I was on to something with Templating, but I've not been able to put the pieces together to make this work.
Write a style for all your image and binding image source to Tag property
<Style TargetType="{x:Type Image}" x:Key="Image1Style">
<Setter Property="Source" Value="{Binding Path=Tag,RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="{Binding Path=(local:AttachedProperty.AltSource),RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
<Image Tag="C:\Image1.jpg" local:AttachedProperty.AltSource="C:\Image2.jpg" Style="{StaticResource Image1Style}"/>
and write a attached property to binding alternative image source to it
public class AttachedProperty
{
public static readonly DependencyProperty AltSourceProperty =
DependencyProperty.RegisterAttached("AltSource",
typeof(string), typeof(AttachedProperty),
new PropertyMetadata());
public static string GetAltSource(DependencyObject obj)
{
return (string)obj.GetValue(AltSourceProperty);
}
public static void SetAltSource(DependencyObject obj, string value)
{
obj.SetValue(AltSourceProperty, value);
}
}
Add a new resource dictionary to your project and name it something like ImageStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:projectName">
<Style TargetType="{x:Type Image}" x:Key="Image1Style">
<Setter Property="Source" Value="C:\Image1.jpg"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="C:\Image2.jpg"/>
</Trigger>
</Style.Triggers>
</Style>
Then you can reference it from your xaml as a static resource enter code here
<image style={static resource="Image1Style"} />

TextPreview for Textbox

Reworked Question to clarify my needs:
I want to add a preview Text to Textboxes when they're empty, just like some of you may know it from Xamarin.
I have found this answer on SO.
This is the Style from the Answer I linked above.
<TextBlock Grid.Row="5"
Grid.Column="1"
VerticalAlignment="Center"
Text="Username:">
</TextBlock>
<TextBox Grid.Row="5"
Grid.Column="3">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Resources>
<VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="Test" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
I get the following result:
Since this is working nicely I want to apply it to every TextBox in that Window.
So my approach was to change this line:
<Label Content="Test" Foreground="LightGray" />
I thought maybe changing it to <Label Content="Test" Foreground="LightGray" /> would do the trick, but it is not working.
I guess it's something with the Tag Property and the Type of it (object instead of string).
Since the first approach is working like charm I don't really see why I should need a custom control for that...
So what I tried then is this:
<Window.Resources>
<Style TargetType="TextBox">
<Style.Resources>
<VisualBrush x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label Content="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
What am I missing - why isn't that working ?
For the reusable textbox, you need to create a custom control. Also for binding doesnot work well with visual brush, so you need some temp object to store the value. Refer my below code.
<Window x:Class="ChkList_Learning.Window4"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ChkList_Learning"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="Window4" Height="300" Width="300">
<Window.Resources>
<local:Temp x:Key="temp" Value="{Binding ElementName=Hostname, Path=Watermark}"/>
<Style TargetType="{x:Type local:WatermarkTextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Style.Resources>
<VisualBrush x:Key="WatermarkBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="{Binding Source={StaticResource temp}, Path=Value}" FontFamily="Segoe UI" FontSize="20" Foreground="LightGray" Padding="5" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource WatermarkBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource WatermarkBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<local:WatermarkTextBox x:Name="Hostname" Height="40" FontFamily="Segoe UI" FontSize="20" VerticalContentAlignment="Center" Watermark="Hello, world.">
</local:WatermarkTextBox>
</Grid>
</Window>
public class Temp : Freezable
{
// Dependency Property
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string),
typeof(Temp), new FrameworkPropertyMetadata(string.Empty));
// .NET Property wrapper
public string Value
{
get { return (string)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
protected override System.Windows.Freezable CreateInstanceCore()
{
return new Temp();
}
}
public class WatermarkTextBox : TextBox
{
static WatermarkTextBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(WatermarkTextBox), new FrameworkPropertyMetadata(typeof(WatermarkTextBox)));
}
public static readonly DependencyProperty WatermarkProperty = DependencyProperty.Register("Watermark", typeof(string), typeof(WatermarkTextBox));
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set { SetValue(WatermarkProperty, value); }
}
}

Hide TextBox without focus when empty Multi-Binding

I have a textbox which I am trying to abide by the following rules:
Textbox must always be visible when it has focus
Textbox must always hide when it doesnt have focus AND it is empty
I have a dependency property setup which allows me to refocus the textbox, so that it shows. This works "one-way" in that I can focus a textbox that is collapsed and it shows. But once I move the focus out of the textbox (and its empty) it always remains.
How can I collapse the textbox, once focus is lost AND it is empty? (Note: I also want to SHOW the box, if text is entered (it is bound to other text boxes, which may have text entered, this is bound two-way).
<Style x:Key="textBoxHider" TargetType="{x:Type TextBox}" BasedOn="{StaticResource storyForgeTextBox}">
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "BorderBrush" Value="LightCyan"/>
<Setter Property= "BorderThickness" Value="2"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property= "BorderBrush" Value="LightSkyBlue"/>
<Setter Property= "BorderThickness" Value="2"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value=""></Condition>
<Condition Property="IsFocused" Value="False"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Visibility" Value="Collapsed"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="True"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Visibility" Value="Visible"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
Edit: for clarity I have simplified it even further and its still not collapsing. Checking the value in Snoop. IsFocused is correctly being set to FALSE and TRUE when I am not focused/focused.
<Style x:Key="TextBoxHider" TargetType="{x:Type TextBox}" BasedOn="{StaticResource storyForgeTextBox}">
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "BorderBrush" Value="LightCyan"/>
<Setter Property= "BorderThickness" Value="2"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property= "BorderBrush" Value="LightSkyBlue"/>
<Setter Property= "BorderThickness" Value="2"/>
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</Trigger>
</Style.Triggers>
</Style>
edit:edit: I am now stumped. The border brush is being set to RED no problem. So the IsFocused is being fired, but its not collapsing the box?
<Style x:Key="storyForgeTextBoxHider" TargetType="{x:Type TextBox}" BasedOn="{StaticResource storyForgeTextBox}">
<Style.Triggers>
<Trigger Property ="IsMouseOver" Value="True">
<Setter Property= "BorderBrush" Value="LightCyan"/>
<Setter Property= "BorderThickness" Value="2"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property= "BorderBrush" Value="LightSkyBlue"/>
<Setter Property= "BorderThickness" Value="2"/>
<Setter Property="Visibility" Value="Visible"></Setter>
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Visibility" Value="Collapsed"></Setter>
<Setter Property="BorderBrush" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
Please check this code; I have made use of Height and Visibility property; Add the code run the application and try the suggestions based on your questions,
Operation 1: Textbox must always be visible when it has focus
Just play with 'Tab' key. When ever second textbox gets focus, it will be visible. When it loses focus, it will be collapsed. (I have used "Height" property)
Operation 2: Textbox must always hide when it doesnt have focus AND it is empty
Just delete the text in 1 textbox out of 2 available textbox. When its empty , second textbox will get collapsed. (I have used "Visibility" property)
XAML code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sampleApp="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<sampleApp:TextToVisibilityConverter x:Key="TextToVisibilityConverter"/>
</Window.Resources>
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBox x:Name="textBox1" Grid.Row="0" Text="{Binding Text,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="5" Height="50" Width="300"/>
<TextBox x:Name="textBox2" Grid.Row="1" Text="{Binding Text,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="300" Visibility="{Binding Text,Converter={StaticResource TextToVisibilityConverter}}"
BorderThickness="5">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Height" Value="50"/>
</Trigger>
<Trigger Property="IsFocused" Value="False">
<Setter Property="Height" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Grid.Row="2" VerticalAlignment="Center" Content="Just a Button"/>
</Grid>
C# Code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string m_Text = "Hello!!";
public string Text
{
get { return m_Text; }
set { m_Text = value; OnPropertyChanged("Text"); }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class TextToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
var text = value.ToString();
if (string.IsNullOrEmpty(text))
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I'm guessing you've got something along these lines in your XAML:
<TextBox Style="{StaticResource textBoxHider}" Visibility="Collapsed">
...
</TextBox>
The visibility won't be set by the trigger because it is set directly (inline in this case), and style setters by design do not affect properties which are set directly (inline, as XAML tag, in code-behind). In order to make it work you should set initial visibility value via style, for example:
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource textBoxHider}">
<Setter Property="Visibility" Value="Collapsed"/>
</Style>
</TextBox.Style>
...
</TextBox>
After you have set textBox.Visibility = Visibility.Visible via code, any triggers setting visibilty will have no effect.
Please see MSDN's Dependency Property Value Precedence for more information on that, in particular :
Be cautious of setting values for properties that have theme-level
trigger behaviors and make sure you are not unduly interfering with
the intended user experience for that control.

how to set ContentPresenter Contents Background

I have a ListBox which is Bound to an ObesvableCollection of dynamically created UserControls.
<ListBox x:Name="myListBox">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemsSource" Value="{Binding userControlsCollection}"/>
....
</Style>
</LIstBox.Style>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="Selector.Selected" Handler="ListBox_Selected"/>
<EventSetter Event="Selector.Unselected" Handler="ListBox_UnSelected"/>
<Setter Property="Background" Value="{DynamicResource DefaultBackground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="true"
Width="{Binding ActualWidth,RelativeSource={RelativeSource FindAncestor,AncestorType=ListBoxItem,AncestorLevel=1}}"
Height="{Binding ActualHeight,RelativeSource={RelativeSource FindAncestor,AncestorType=ListBoxItem, AncestorLevel=1}}"
/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox>
I would like to set the Background of the selected control should be something like that
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource SelectedBackground}"/>
</Trigger>
</Style.Triggers>
but that way I set the ListBoxItem Background and it doesn't propagate the the UserControls Background...
the way I solve it now is using the Selector.Selected/UnSelected event handlers like this
private void ListBox_Selected(object sender, RoutedEventArgs e)
{
var item = e.Source as ListBoxItem;
var ctrl= item.Content as myControl;
if (ctrl!= null)
{
ctrl.Background = new SolidColorBrush(DefaultSelectedBackground);
}
}
Any Idea would be greatly appriciated
Try to keep your ItemContainerStyle simple. If you need to mess with the Template of the item, use ItemTemplate or RelativeSource bindings to achieve what you need.
To get your requirement with RelativeSource Binding, I'd just have the ItemContainerStyle as something like:
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Background"
Value="BurlyWood" />
<Setter Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter Property="VerticalContentAlignment"
Value="Stretch" />
<Style.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter Property="Background"
Value="Tomato" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="True">
<Setter Property="Background"
Value="CadetBlue" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Now to get this Background in the UserControl, my UserControl xaml would be like:
<UserControl ...
Background="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=Background}">
That's it, we're sorted.
You can get the demo of this from: Here
Update:
If you're creating the UserControl from code-behind(not sure why you need to but anyways), Assign the Binding in code-behind too. Something like:
public UserControl CreateUserControl(string text) {
Binding binding = new Binding {
Path = new PropertyPath(BackgroundProperty),
RelativeSource = new RelativeSource() {
Mode = RelativeSourceMode.FindAncestor,
AncestorType = typeof(ListBoxItem)
}
};
var uc = new UserControl {
Content = new TextBlock {
Text = text,
FontSize = 24,
FontWeight = FontWeights.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
}
};
BindingOperations.SetBinding(uc, BackgroundProperty, binding);
return uc;
}
and the ItemContainerStyle would remain the same as before and you should be done.
Demo for this approach: Here
To acheive what you are saying, you could wrap your ContentPresenter in a panel (e.g. Grid) and use TemplateBinding to set the background .
Sample Control Template:
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter Content="{Binding}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Fuchsia" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Try setting the style trigger like this:
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelectedProperty}" Value="True">
<Setter Property="Control.Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsSelectedProperty}" Value="False">
<Setter Property="Control.Background" Value="Black" />
</DataTrigger>
</Style.Triggers>
Thanks

Setting HotTracking for a tab control

I am attempting to implement HotTracking for a tab control in Wpf. My understanding is this was not included in the wpf tabcontrol and I would like to use it.
For my benefit HotTracking = When mouseover an unselected tab the tab will change color(usually to something between selected and not selected)
I used a bit of my own knowledge and this post How to set MouseOver event/trigger for border in XAML? but I can't seem to make it work.
This is everything.
<Window x:Class="TestingWpF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280">
<Window.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
CornerRadius="6,6,0,0" >
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="12,2,12,2"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="AliceBlue" />
</Trigger>
<Trigger Property=" Border.IsMouseOver" Value="True">
<Setter Property="Border.Background" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TabControl Grid.ColumnSpan="2" Grid.RowSpan="2" Height="309" HorizontalAlignment="Left" Name="tabControl1" VerticalAlignment="Top" Width="781" Padding="0">
<TabItem Header="tabItem1" >
</TabItem>
<TabItem Header="tabItem2" >
</TabItem>
</TabControl>
</Grid>
</Window>
I figured it out, my problem was this section
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="AliceBlue" />
</Trigger>
<Trigger Property=" Border.IsMouseOver" Value="True">
//Change this line
//<Setter Property="Border.Background" Value="Green" />
//To This
<Setter TargetName="Border" Property="Background" Value="Green" />
</Trigger>
The last Trigger was the one not working. And if you notice I used different properties in the setter. I changesd it to match the other two and it worked

Categories

Resources