WPF - XAML TabControl MouseEnter TabItem TextBox SelectAll

Quick tip on MouseEnter / MouseOver a specific TabItem in a TabControl. Includes sample of TextBox .SelectAll globally in your application.

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>
By Robbe Morris   Popularity  (6518 Views)
Picture
Biography - Robbe Morris
Robbe has been a Microsoft MVP in C# since 2004. He is also the co-founder of NullSkull.com which provides .NET articles, book reviews, software reviews, and software download and purchase advice.  Robbe also loves to scuba dive and go deep sea fishing in the Florida Keys or off the coast of Daytona Beach. Microsoft MVP