I want to show an icon in some of my buttons. Since aligning an image inside a button isn't exactly trivial, I though a user control, derived control or something would come in handy. So I googled, tried, compiled and now I came up with the following code.
Unfortunately, my properties are not bound to anything and I cannot see the text nor an image. How can I make this work? What's the missing piece? I'm using VS2010 and .NET 4.0.
XAML:
<Button
x:Class="MyNS.IconButton"
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">
<Button.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding IconSource}" Name="Icon" Width="{Binding IconSize}" Height="{Binding IconSize}" Margin="0,0,4,0"/>
<ContentPresenter/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsEnabled" Value="False">
<Setter Property="Image.Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
Code file:
public partial class IconButton : Button
{
public static DependencyProperty IconSourceProperty = DependencyProperty.Register(
"IconSource",
typeof(ImageSource),
typeof(IconButton));
public static DependencyProperty IconSizeProperty = DependencyProperty.Register(
"IconSize",
typeof(int),
typeof(IconButton),
new PropertyMetadata(11));
public ImageSource IconSource
{
get { return (ImageSource) GetValue(IconSourceProperty); }
set { SetValue(IconSourceProperty, value); }
}
public int IconSize
{
get { return (int) GetValue(IconSizeProperty); }
set { SetValue(IconSizeProperty, value); }
}
public IconButton()
{
InitializeComponent();
}
}
It looks like your data binding might be the source of the issues. With those changes I was able to get an image to display in another page using your IconButton.
I made the following changes:
<!-- Added x:Name="_this" -->
<Button x:Class="ButtonTest.IconButton"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="_this">
<Button.Template>
<ControlTemplate>
<StackPanel Orientation="Horizontal"
Background="Red">
<!-- Bindings Changed Here -->
<Image Source="{Binding ElementName=_this, Path=IconSource}"
Name="Icon"
Width="{Binding ElementName=_this, Path=IconSize}"
Height="{Binding ElementName=_this, Path=IconSize}" Margin="0,0,4,0"/>
<ContentPresenter />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Button.IsEnabled" Value="False">
<Setter Property="Image.Opacity" Value="0.5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
Related
For my custom control, I am using the Dynamic resource to set the Foreground property. Initially when i run the app the Foreground property was not set to my control. When I change value dynamically Foreground was applied correctly. How can I resolve this issue?
PS: Simple sample WpfApplication4
MainWindow.xaml
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="300"
Height="200">
<Window.Resources>
<SolidColorBrush x:Key="ForegroundBrush" Color="Red" />
<SolidColorBrush x:Key="BackgroundBrush" Color="Yellow" />
<Style x:Key="MyStyle" TargetType="ContentControl">
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource BackgroundBrush}" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Create and Add Custom Control With Style" />
<Button Click="ButtonBase_OnClick_1" Content="Change Color" />
</StackPanel>
<StackPanel x:Name="myPanel" Grid.Row="1" />
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var control = new MyControl {Style = this.Resources["MyStyle"] as Style};
control.CreateContent(string.Format("My Control {0}", myPanel.Children.Count + 1));
myPanel.Children.Add(control);
}
private void ButtonBase_OnClick_1(object sender, RoutedEventArgs e)
{
var brush = (SolidColorBrush)Resources["ForegroundBrush"];
Resources["ForegroundBrush"] = new SolidColorBrush(Color.Add(brush.Color, Color.FromRgb(0, 100, 100)));
var brush1 = (SolidColorBrush)Resources["BackgroundBrush"];
Resources["BackgroundBrush"] = new SolidColorBrush(Color.Add(brush1.Color, Color.FromRgb(0, 100, 100)));
}
}
public class MyControl : ContentControl
{
public MyControl()
{
DefaultStyleKey = typeof(MyControl);
}
public void CreateContent(string text)
{
this.Content = new TextBlock() {Text = text};
}
}
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4">
<Style TargetType="local:MyControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyControl">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you want to apply a Style to a control, then you have to apply the Style to that control (which you haven't done in the code example that you have provided). You have two choices here... either try this (wherever you use the control):
<local:MyControl Style="{StaticResource MyStyle}" />
Or define your Style like this, which will affect all instances of your control within scope:
<Style TargetType="ContentControl">
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}" />
<Setter Property="Background" Value="{DynamicResource BackgroundBrush}" />
</Style>
I am trying to create a custom control for a text block that when moused over, a border will appear. I am pretty new to WPF and have only made some very simple custom controls. I need to implement this in a XAML UserControl.
Any suggestions would be greatly appreciated. Thanks again, StackOverflow.
EDIT: I am going to have to bind a persistence property to several different controls, so I really need to do this in a custom control. This is what I have, and it isn't working:
xmlns:customControls="clr-namespace:****.CustomControls"
....
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
And the UserControl:
<UserControl
x:Class="****.MouseOverBorder"
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"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="MouseOverBorder" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" />
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Border Style="{DynamicResource MouseOverBorder}" BorderThickness="1" CornerRadius="3" SnapsToDevicePixels="True"/>
No need to make a UserControl. I've managed to accomplish this with the following markup:
<Border Style="{DynamicResource BorderStyle1}" BorderThickness="1" CornerRadius="3" >
<TextBlock Text="TextBlock" />
</Border>
Here's the style:
<Style x:Key="BorderStyle1" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="3"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#FF123BBA"/>
</Trigger>
</Style.Triggers>
</Style>
EDIT:
Still don't get it why do you need a UserControl (please don't call it custom control - these are different things), but let's consider your example.
When you write the following
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
you are actually setting MouseOverBorder.Content property. Originally it's Content is defined in MouseOverBorder.xaml file. So you are replacing all your UserControl structure with TextBlock. But still I got your idea and have solution for it.
First, add custom DependencyProperty and CLR wrapper for it to MouseOverBorder class:
public static readonly DependencyProperty MyContentTemplateProperty =
DependencyProperty.Register("MyContentTemplate", typeof(DataTemplate), typeof(MouseOverBorder), null);
[Browsable(true)]
[Category("Other")]
public DataTemplate MyContentTemplate
{
get { return (DataTemplate)GetValue(MyContentTemplateProperty); }
set { SetValue(MyContentTemplateProperty, value); }
}
Second, make something inside MouseOverBorder use this property, e.g.
<ContentPresenter ContentTemplate="{Binding MyContentTemplate, ElementName=userControl}"/>
<!-- userControl is the Name of MouseOverBorder, defined in xaml -->
At last, you can use your UserControl as following:
<customControls:MouseOverBorder>
<customControls:MouseOverBorder.MyContentTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</DataTemplate>
</customControls:MouseOverBorder.MyContentTemplate>
</customControls:MouseOverBorder>
How can I change a Button template dynamically?
I have a ComboBox where by changing his selected value I want to change a Button Template.
This is what I have been trying to do:
<Window.Resources>
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="#FF2D2D7A" Margin="7.5,9.5,8.5,11" Stroke="Black"
RadiusX="45" RadiusY="45" StrokeThickness="6"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate2" TargetType="{x:Type Button}">
<Grid>
<ed:RegularPolygon Fill="#FFE7F9C9" Height="Auto" InnerRadius="0.47211"
Margin="20.5,16,15.5,8" PointCount="5" Stretch="Fill"
Stroke="Black" StrokeThickness="6" Width="Auto"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ComboBox Name="GroupBoxHeaderComboBox" ItemsSource="{Binding Path=collection}"
DisplayMemberPath="Key" Height="52" Margin="211.5,60,230.5,0"
VerticalAlignment="Top" SelectedIndex="1"/>
<Button Content="Button" HorizontalAlignment="Left" Height="102" Margin="47.5,0,0,91"
VerticalAlignment="Bottom" Width="132"
Template="{DynamicResource ButtonControlTemplate2}"/>
<Button Content="Button" HorizontalAlignment="Right" Height="112.5" Margin="0,0,27.5,85"
VerticalAlignment="Bottom" Width="153"
Template="{DynamicResource ButtonControlTemplate1}"/>
<Button Content="Button" Height="102" Margin="239.5,0,252.5,13.5"
VerticalAlignment="Bottom"
Template="{Binding ElementName=GroupBoxHeaderComboBox, Path=SelectedItem.Value}"/>
</Grid>
And here are the associated Templates:
<Window.Resources>
<ControlTemplate x:Key="ButtonControlTemplate1" TargetType="{x:Type Button}">
<Grid>
<Rectangle Fill="#FF2D2D7A" Margin="7.5,9.5,8.5,11" Stroke="Black"
RadiusX="45" RadiusY="45" StrokeThickness="6"/>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="ButtonControlTemplate2" TargetType="{x:Type Button}">
<Grid>
<ed:RegularPolygon Fill="#FFE7F9C9" Height="Auto" InnerRadius="0.47211"
Margin="20.5,16,15.5,8" PointCount="5" Stretch="Fill"
Stroke="Black" StrokeThickness="6" Width="Auto"/>
</Grid>
</ControlTemplate>
</Window.Resources>
And the code behind:
public partial class MainWindow : Window
{
public Dictionary<string, string> collection
{
get;
private set;
}
public MainWindow()
{
this.InitializeComponent();
DataContext = this;
collection = new Dictionary<string, string>()
{
{ "DynamicResource ButtonControlTemplate2", "{DynamicResource ButtonControlTemplate2}"},
{ "DynamicResource ButtonControlTemplate1", "{DynamicResource ButtonControlTemplate2}"},
};
// Insert code required on object creation below this point.
}
}
Is there another genric way to acomplish this?... I want that most of the code would be xaml.
EDIT:
Is there a point to do it using a style? Let's say I want more then one object to act, otherwise is there a point to change the style and to do it all from there?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public Dictionary<string, ControlTemplate> collection
{
get
{
Dictionary<string, ControlTemplate> controlTemplates = new Dictionary<string, ControlTemplate>();
controlTemplates.Add("ButtonControlTemplate1", FindResource("ButtonControlTemplate1") as ControlTemplate);
controlTemplates.Add("ButtonControlTemplate2", FindResource("ButtonControlTemplate2") as ControlTemplate);
return controlTemplates;
}
}
}
Create a ControlTemplate in Windows resource,
<Window.Resources>
<ControlTemplate x:Key="GreenTemplate" TargetType="{x:Type Button}">
<Grid>
<Ellipse Fill="Green"/>
<ContentPresenter Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
Now in run time you can change the template property of button.
private void Button_Clicked(object sender, RoutedEventArgs e)
{
Button btn = e.OriginalSource as Button;
if (btn != null)
{
btn.Template = FindResource("GreenTemplate") as ControlTemplate;
}
}
You can use a data trigger and do it all in xaml.
This uses a tree but the concept is the same
<Window x:Class="WpfBindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfBindingTest"
Title="Window3" Height="300" Width="300" Name="win3" >
<Window.Resources>
<XmlDataProvider x:Key="treeData" XPath="*">
<x:XData>
<Items Name="Items" xmlns="">
<Item1/>
<Item2>
<Item22/>
<Item12/>
<Item13>
<Item131/>
<Item131/>
</Item13>
</Item2>
</Items>
</x:XData>
</XmlDataProvider>
<HierarchicalDataTemplate ItemsSource="{Binding XPath=child::*}" x:Key="template">
<TextBlock Name="textBlock" Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</Window.Resources>
<StackPanel>
<TreeView ItemTemplate="{StaticResource template}"
Name="treeView"
ItemsSource="{Binding Source={StaticResource treeData}}">
<TreeView.ItemContainerStyle>
<!--Using style setter to set the TreeViewItem.IsExpanded property to true, this will be applied
to all TreeViweItems when they are generated-->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<Button Width="120" Height="30">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="Default" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=treeView, Path=SelectedItem.Name}" Value="Item12">
<Setter Property="Content" Value="Now changed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
from here.
(I just googled to get an example faster)
The text is grayed out when the DatePicker is disabled and I want the content to be easier to read.
What I did on some TextBoxes was:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
It did make the text easier to read.
I do manage to change the Foreground colour on the DataPicker but it does not do the trick. The text was still grayed out.
Seems like there is another property I need to set to make the content of the disabled DatePicker easier to read.
So, how do I make the content of my disabled DatePicker easier to read?
Can you extend DatePicker by adding bool DependencyProperty called Editable.
I found a working example at the following link, note that I run this code in .NET 4.
Here is the DatePicker Control:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
namespace DatePickerStyle
{
public class ExtendedDatePicker : DatePicker
{
public static readonly DependencyProperty EditableProperty = DependencyProperty.Register("Editable", typeof(bool),
typeof(ExtendedDatePicker), new PropertyMetadata(true));
public bool Editable
{
get { return (bool)GetValue(EditableProperty); }
set { SetValue(EditableProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var textBox = GetTemplateChild("PART_TextBox") as DatePickerTextBox;
var binding = new Binding { Source = this, Path = new PropertyPath(ExtendedDatePicker.EditableProperty) };
textBox.SetBinding(UIElement.FocusableProperty, binding);
}
}
}
Here is the XAML:
<Window x:Class="DatePickerStyle.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:DatePickerStyle="clr-namespace:DatePickerStyle"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<DatePicker IsEnabled="True" Grid.Row="0" SelectedDate="2002/12/31"/>
<DatePicker IsEnabled="False" Grid.Row="1" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="True" Grid.Row="2" SelectedDate="2002/12/31"/>
<DatePickerStyle:ExtendedDatePicker Editable="False" Grid.Row="3" SelectedDate="2002/12/31"/>
</Grid>
</Window>
My I suggest this simpler, universal approach?
<ControlTemplate x:Key="MyDisabledDatePicker">
<Border BorderBrush="Black" BorderThickness="1">
<TextBlock
Text="{Binding Path=SelectedDate, StringFormat={}{0:d}, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center" HorizontalAlignment="Left" Padding="10,0,0,0"/>
</Border>
</ControlTemplate>
<Style TargetType="{x:Type DatePicker}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}}" Value="false">
<Setter Property="Template" Value="{StaticResource MyDisabledDatePicker}" />
</DataTrigger>
</Style.Triggers>
</Style>
Presto!
the above code w ExtendedDatePicker works, first I thought it didn't but that was because the dropdown could still change the text and the Editable="False" doesn't work on the dropdown
so don't forget to add the following to the ExtendedDatePicker
Editable="False" AllowDrop="False" IsDropDownOpen="False" IsHitTestVisible="False" IsManipulationEnabled="False"
I have a typical MVVM scenario:
I have a ListBox that is binded to a List of StepsViewModels.
I define a DataTemplate so that StepViewModels are rendered as StepViews.
The StepView UserControl have a set of labels and TextBoxs.
What I want to do is to select the ListBoxItem that is wrapping the StepView when a textBox is focused. I've tried to create a style for my TextBoxs with the following trigger:
<Trigger Property="IsFocused" Value="true">
<Setter TargetName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}" Property="IsSelected" Value="True"/>
</Trigger>
But I get an error telling me that TextBoxs don't have an IsSelected property. I now that but the Target is a ListBoxItem.
How can I make it work?
There is a read-only property IsKeyboardFocusWithin that will be set to true if any child is focused. You can use this to set ListBoxItem.IsSelected in a Trigger:
<ListBox ItemsSource="{Binding SomeCollection}" HorizontalAlignment="Left">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Width="100" Margin="5" Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As Jordan0Day correctly pointed out there can be indeed big problems using IsKeyboardFocusWithin solution. In my case a Button in a Toolbar which regards to the ListBox was also not working anymore. The same problem with focus. When clicking the button the ListBoxItem does loose the Focus and the Button updated its CanExecute method, which resulted in disabling the button just a moment before the button click command should be executed.
For me a much better solution was to use a ItemContainerStyle EventSetter as described in this post: ListboxItem selection when the controls inside are used
XAML:
<Style x:Key="MyItemContainer.Style" TargetType="{x:Type ListBoxItem}">
<Setter Property="Background" Value="LightGray"/>
<EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="backgroundBorder" Background="White">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="backgroundBorder" Property="Background" Value="#FFD7E6FC"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EventHandler in the code behind of the view:
private void OnListBoxItemContainerFocused(object sender, RoutedEventArgs e)
{
(sender as ListBoxItem).IsSelected = true;
}
One way to achieve that is by implementing a custom behavior using an attached property. Basically, the attached property would be applied to the ListBoxItem using a style, and would hook up to their GotFocus event. That even fires if any descendant of the control gets the focus, so it is suitable for this task. In the event handler, IsSelected is set to true.
I wrote up a small example for you:
The Behavior Class:
public class MyBehavior
{
public static bool GetSelectOnDescendantFocus(DependencyObject obj)
{
return (bool)obj.GetValue(SelectOnDescendantFocusProperty);
}
public static void SetSelectOnDescendantFocus(
DependencyObject obj, bool value)
{
obj.SetValue(SelectOnDescendantFocusProperty, value);
}
public static readonly DependencyProperty SelectOnDescendantFocusProperty =
DependencyProperty.RegisterAttached(
"SelectOnDescendantFocus",
typeof(bool),
typeof(MyBehavior),
new UIPropertyMetadata(false, OnSelectOnDescendantFocusChanged));
static void OnSelectOnDescendantFocusChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ListBoxItem lbi = d as ListBoxItem;
if (lbi == null) return;
bool ov = (bool)e.OldValue;
bool nv = (bool)e.NewValue;
if (ov == nv) return;
if (nv)
{
lbi.GotFocus += lbi_GotFocus;
}
else
{
lbi.GotFocus -= lbi_GotFocus;
}
}
static void lbi_GotFocus(object sender, RoutedEventArgs e)
{
ListBoxItem lbi = sender as ListBoxItem;
lbi.IsSelected = true;
}
}
The Window XAML:
<Window x:Class="q2960098.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:q2960098">
<Window.Resources>
<DataTemplate x:Key="UserControlItemTemplate">
<Border BorderBrush="Black" BorderThickness="5" Margin="10">
<my:UserControl1/>
</Border>
</DataTemplate>
<XmlDataProvider x:Key="data">
<x:XData>
<test xmlns="">
<item a1="1" a2="2" a3="3" a4="4">a</item>
<item a1="a" a2="b" a3="c" a4="d">b</item>
<item a1="A" a2="B" a3="C" a4="D">c</item>
</test>
</x:XData>
</XmlDataProvider>
<Style x:Key="MyBehaviorStyle" TargetType="ListBoxItem">
<Setter Property="my:MyBehavior.SelectOnDescendantFocus" Value="True"/>
</Style>
</Window.Resources>
<Grid>
<ListBox ItemTemplate="{StaticResource UserControlItemTemplate}"
ItemsSource="{Binding Source={StaticResource data}, XPath=//item}"
HorizontalContentAlignment="Stretch"
ItemContainerStyle="{StaticResource MyBehaviorStyle}">
</ListBox>
</Grid>
</Window>
The User Control XAML:
<UserControl x:Class="q2960098.UserControl1"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UniformGrid>
<TextBox Margin="10" Text="{Binding XPath=#a1}"/>
<TextBox Margin="10" Text="{Binding XPath=#a2}"/>
<TextBox Margin="10" Text="{Binding XPath=#a3}"/>
<TextBox Margin="10" Text="{Binding XPath=#a4}"/>
</UniformGrid>
</UserControl>
If you create a User Control and then use it as the DataTemplate It seems to work cleaner.
Then you don't have to use the dirty Style Triggers that Don't work 100% of the time.
Edit: Someone else already had the same answer on a different question: https://stackoverflow.com/a/7555852/2484737
Continuing on Maexs' answer, using an EventTrigger instead of an EventSetter removes the need for code-behind:
<Style.Triggers>
<EventTrigger RoutedEvent="GotKeyboardFocus">
<BeginStoryboard>
<Storyboard >
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected" >
<DiscreteBooleanKeyFrame Value="True" KeyTime="0:0:0"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>