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.
Related
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"} />
I have a DataGrid where I'm trying to set the background of each element depending on what text is being displayed in that grid cell. The grid cells are defined as follows and accessing them works correctly:
<Window.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="MouseDown" Handler="Clicked" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
The part that doesn't work is that I can't set the a style for the DataGridTextColumn, none of my setters below are doing anything. The DataGrid itself is being displayed corrrectly.
<DataGrid Name="dataGrid1" AutoGenerateColumns="False" IsReadOnly="true">
<DataGridTextColumn>
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="Text" Value="None">
<Setter Property="Background" Value="Aquamarine"/>
</Trigger>
<Trigger Property="Text" Value="First">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid>
Thanks in advance for any help.
EDIT 1
Updated with Answers to NETscape's questions.
1) My DataGrid is showing correctly, I'm even clicking on items in the grid and modifying the data behind it. However, I don't have a DataSource set in my XAML, just a simple DataContext in my view that points to the DataGrid.
2) I don't think that the problem is with my variable names since the setter property below that doesn't have a trigger also isn't showing:
<Setter Property="HorizontalAlignment" Value="Center" />
EDIT 2
Again in response to NETscape's questions/comments. I didn't post my code-behind originally because, since all my objects were displaying correctly, I wasn't looking for the problem there. But I think I was mistaken. The data binding for my grid and for the columns in the grid are different. The DataContext for my grid is an array of RowObjects. The datacontext for each DataGridTextColumn is one entry in my RowObject tied to a particular day of the week.
DataGridTextColumn cSunday = new DataGridTextColumn();
DataGridTextColumn cMonday = new DataGridTextColumn();
public TimeSlice[] TimeArray = new TimeSlice[48];
MyWnd.dataGrid1.DataContext = TimeArray; //sets DataContext for the DataGrid
CultureInfo cu = new CultureInfo(App.CultureID);
//creates one column
cSunday.Header = cu.DateTimeFormat.GetDayName(DayOfWeek.Sunday);
cSunday.Binding = new Binding("StatusSunday");
Wnd.dataGrid1.Columns.Add(cSunday);
cMonday.Header = cu.DateTimeFormat.GetDayName(DayOfWeek.Monday);
cMonday.Binding = new Binding("StatusMonday");
Wnd.dataGrid1.Columns.Add(cMonday);
EDIT 3
For completeness sake, adding the working solution derived fron NETscape's answer. XAML:
<Window
<Window.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="MouseDown" Handler="Clicked" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MyDataGridTextColumnElementStyle"
TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="Text" Value="None">
<Setter Property="Background" Value="Aquamarine"/>
</Trigger>
<Trigger Property="Text" Value="First">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid Name="MainView" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row ="0">
</Border>
<Border Grid.Row="1">
<DataGrid Name="dataGrid1" AutoGenerateColumns="False" IsReadOnly="true"/>
</Border>
</Grid>
</Window>
And the code in my view model for setting up the first 2 days/columns:
public TimeSlice[] TimeArray = new TimeSlice[48];
public ViewModelr(WndCalendar Wnd)
{
MyWnd = Wnd;
MyWnd.DataContext = this;
MyWnd.dataGrid1.DataContext = TimeArray;
cTime.Header = "";
cTime.Binding = new Binding("TimeofDay");
Wnd.dataGrid1.Columns.Add(cTime);
CultureInfo cu = new CultureInfo(App.CultureID);
cSunday.Header = cu.DateTimeFormat.GetDayName(DayOfWeek.Sunday);
cSunday.Binding = new Binding("StatusSunday");
Wnd.dataGrid1.Columns.Add(cSunday);
cSunday.ElementStyle = MyWnd.FindResource("MyDataGridTextColumnElementStyle") as Style;
cMonday.Header = cu.DateTimeFormat.GetDayName(DayOfWeek.Monday);
cMonday.Binding = new Binding("StatusMonday");
Wnd.dataGrid1.Columns.Add(cMonday);
cMonday.ElementStyle = MyWnd.FindResource("MyDataGridTextColumnElementStyle") as Style;
Frank E, your Edit 2 might have given away your issue; it's always helpful to know when you're doing things programmatically!
Setting the Style inside of Windows.Resources is saying, "hey, for any DataGridCell in this control, apply this style to it." That is why it's working properly, because after all the information is added, the styles still get applied; which is why it seems to be working.
Now notice you are defining a DataGridTextColumn and it's ElementStyle. That isn't being applied to all the DataGridTextColumn's which may be what you were thinking. Instead, that is being applied to that single DataGridTextColumn! Because you're not defining anything for that DataGridTextColumn it's actually being drawn in the DataGrid, but since there is no content for that column, it's Width = 0.
Since you're creating the other columns programmatically, you need to apply the Style programmatically. So you should have something like this:
<Window.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="MouseDown" Handler="Clicked" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MyDataGridTextColumnElementStyle"
TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="Text" Value="None">
<Setter Property="Background" Value="Aquamarine"/>
</Trigger>
<Trigger Property="Text" Value="First">
<Setter Property="Background" Value="Blue"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
Then when you're creating your columns in code behind, do something like this:
cSunday.ElementStyle = MyWnd.FindResource("MyDataGridTextColumnElementStyle") as Style;
for each column that you are creating.
From what I understand, not assigning a x:Key will apply the style to all elements that are of type TargetType, so since we assign a x:Key to the 2nd style, not all TextBlock will be assigned that style, only the ones we specify.
Let me know if this helps.
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>
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
I have a combobox filled with a list of Objects. I like to highlight an item in a combobox based on a IsHighlighted property of the Object.
I've tried writing my own style but no real success...
<Style x:Key="SimpleComboBoxItem" TargetType="ComboBoxItem">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="#FFCCCCCC"/>
</Trigger>
<Trigger Property="Tag" Value="Highlight" SourceName="contentPresenter">
<Setter Property="Background" TargetName="Border" Value="#FFAAF3A0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanx in advance
This should work fine with a simple DataTrigger.
Your Object Class:
public class TestObject
{
public string Name { get; set; }
public bool IsHighlighted { get; set; }
public override string ToString()
{
return this.Name;
}
}
Xaml:
<Window x:Class="TestWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWPF"
Title="MainWindow">
<Grid>
<StackPanel>
<ComboBox>
<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Background" Value="Gray" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Resources>
<local:Employee Name="Nick" />
<local:Employee Name="Bob" IsHighlighted="True" />
<local:Employee Name="Fred" />
</ComboBox>
</StackPanel>
</Grid>
</Window>
Note: Unlike the sample above, I'm guessing in your code you're binding the combobox's ItemsSource... which should work just the same. One thing to be careful of though, is if your object's 'IsHighlighted' property can change, you should be implementing INotifyProperty changed to ensure that changing the value will notify the UI that the triggers should refresh.
You might wanna re-define the HighlightBrushKey, overriding default highlight style:
<ComboBox.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="FFAAF3A0" />
</ComboBox.Resources>
Should do the trick for you.
(if it is more general, put it directly in the UserControl.Resources / Window.Resources )