This small code sample will demonstrate how to assign a class to a ResourceDictionary
for globally handling events from certain styles. You do this by using the x:Class
attribute in your resource dictionary .xaml file and then have your class file
created as a partial class that inherits ResourceDictionary.
As part of that demonstration, you'll learn how to perform TextBox .SelectAll for
every textbox that implements the style (you could do this automatically but
I don't like automatic control styles wired to events) and how to react to the
MouseEnter event of a specific TabItem.
As I'm sure you've already discovered, the TabItem MouseEnter fires when the mouse
is over any part of the entire TabItem and controls on it. In my case, I wanted
to isolate the TabItem header specifically and that isn't supported by default
in XAML.
I picked these two scenarios because they both fit into a common tab oriented application
business rule. If a user is on a TextBox filling out information, we may want
to automatically trigger a save on the TextBox.Text value prior to permitting
the selected tab from being processed (see WPF - XAML TabControl SelectionChanged
article for more specifics). If our TextBox is set to perform an auto save on
LostFocus, we want to make sure that gets triggered prior to the TabItem SelectionChanged
event being fired.
Notice in the TextBox .Text binding that I opted to use an IValueConverter versus
the StringFormat property. The IValueConverter in this sample converts improper
numeric entries into properly formatted zero representation. The StringFormat
property added with .NET 3.5 SP1 does not do this for you.
In my opinion, you will want to reserve the concept of global reacting to events
like I've done here to specific instances where you "absolutely need"
it. Wherever possible, you'll want to continue to handle events in a more isolated
manner.
Download Source Code
// Window.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Threading;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Diagnostics;
using BusinessLogic;
using BusinessLogic.Enums;
namespace WPFTutorial
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial
class Window1 : Window, INotifyPropertyChanged
{
private Candidate _candidate =
new Candidate(1,
"John",
"McCain",
(int)PoliticalPartyTypes.Republican,
50000.75,
5000);
public Candidate Candidate
{
get {
return _candidate; }
set { _candidate = value; }
}
public Window1()
{
InitializeComponent();
this.Loaded +=
new RoutedEventHandler(Window1_Loaded);
this.DataContext = Candidate;
}
private void Window1_Loaded(object sender, RoutedEventArgs e)
{
this.CurrencyTextBox.Focus();
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged ==
null) {
return; }
PropertyChanged(
this,
new PropertyChangedEventArgs(propertyName));
}
private void NumberTextBox_LostFocus(object sender, RoutedEventArgs e)
{
MessageBox.Show(
"Number TextBox Forced Loss of Focus");
}
private void CurrencyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
MessageBox.Show(
"Currency TextBox Forced Loss of Focus");
}
}
}
// Window1.xaml
<Window x:Class="WPFTutorial.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="518" Width="658" WindowStartupLocation="CenterScreen"
xmlns:XAMLConverters="clr-namespace:XAMLConverters;assembly=XAMLConverters"
>
<Window.Resources>
<XAMLConverters:NumberConverter x:Key="numberConverter" />
</Window.Resources>
<TabControl IsSynchronizedWithCurrentItem="True" x:Name="MainTabControl"
Margin="0,5,0,0">
<TabItem Header="Tab1" Style="{StaticResource TabItem}">
<StackPanel VerticalAlignment="Top">
<StackPanel Orientation="Horizontal" VerticalAlignment="Top">
<Label Name="CurrencyLabel" Margin="3,3,3,3" Height="25"
VerticalAlignment="Top">Currency:
</Label>
<TextBox Name="CurrencyTextBox" Height="30" Width="200"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
LostFocus="CurrencyTextBox_LostFocus"
Text="{Binding Path=MoneyRaised, Mode=TwoWay,
Converter={StaticResource numberConverter},
ConverterParameter=C0}"
Style="{StaticResource TextBoxHighlighter}">
</TextBox>
</StackPanel>
<Label Margin="3,3,3,3" Height="25" VerticalAlignment="Top" >Move your mouse over one of the tabs.
</Label>
</StackPanel>
</TabItem>
<TabItem Header="Tab2" Style="{StaticResource TabItem}">
<Label Name="Tab2Label1">This is tab 2
</Label>
</TabItem>
</TabControl>
</Window>
// Styles
// TextBox.xaml
<ResourceDictionary 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/interactivedesigner/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:clr="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d" x:Class="WPFTutorial.TextBoxEvents" >
<Style x:Key="TextBoxHighlighter" TargetType="{x:Type TextBox}">
<EventSetter Event="GotMouseCapture" Handler="TextBox_GotFocus"/>
<EventSetter Event="GotFocus" Handler="TextBox_GotFocus"/>
</Style>
</ResourceDictionary>
// Styles
//TextBoxEvents.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using System.Windows.Input;
namespace WPFTutorial
{
public partial
class TextBoxEvents : ResourceDictionary
{
public void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
if (sender.GetType() == typeof(TextBox))
{
(sender
as TextBox).SelectAll();
return;
}
}
}
}
// Styles
// TabControl.xaml
<ResourceDictionary 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/interactivedesigner/2006"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:clr="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
x:Class="WPFTutorial.TabControlEvents">
<Style x:Key="TabItem" d:IsControlPart="True" TargetType="{x:Type TabItem}">
<Setter Property="Margin" Value="0,3,0,0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border Margin="0,0,0,-1" x:Name="Border" HorizontalAlignment="Center" VerticalAlignment="Center" BorderThickness="1,0,1,0" >
<Border.BorderBrush>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#DEDFE7" Offset="0.339"/>
<GradientStop Color="#C6DED6" Offset="0.34"/>
</LinearGradientBrush>
</Border.BorderBrush>
<StackPanel>
<Border Height="1" x:Name="TopBorder" BorderThickness="0,0,0,0" />
<Label Foreground="Black" Margin="17,-3,17,5" FontWeight="Bold" x:Name="Label" FontSize="11" FontFamily="Tahoma" Focusable="True">
<Label.Style>
<Style TargetType="{x:Type Label}">
<EventSetter Event="MouseEnter" Handler="TabItem_MouseEnter"/>
</Style>
</Label.Style>
<ContentPresenter HorizontalAlignment="Center" Margin="0,0,0,0" x:Name="ContentSite" VerticalAlignment="Center" RecognizesAccessKey="True" ContentSource="Header"/>
</Label>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
// Styles
// TabControlEvents.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using System.Windows.Input;
namespace WPFTutorial
{
public partial
class TabControlEvents : ResourceDictionary
{
public void TabItem_MouseEnter(object sender, MouseEventArgs e)
{
(sender
as Label).Focus();
}
}
}
//
app.xaml
<Application x:Class="WPFTutorial.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/TabControl.xaml"/>
<ResourceDictionary Source="Styles/TextBox.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>