Kyle Williamson
Kyle Williamson

Reputation: 2184

Change a TabItem's name by double clicking the name

I have a TabControl and I'm trying to allow the user to change the tab name... but only when the name is double-clicked. That way, users can click the different tab names to simply change the active tab, but also change the tab name if they desire.

What I've tried so far is to capture the MouseDoubleClick and LostFocus events, then set the "Focusable" property to be true only when the tab name is double clicked. The problem with this method is the LostFocus event is firing immediately after the double click, presumably because the focus is being set to the content of the TabItem.

My tab control XAML:

    <Mah:MetroAnimatedTabControl x:Name="ViewTabs" DataContext="{Binding MyTabsViewModel}" ItemsSource="{Binding}">
        <Mah:MetroAnimatedTabControl.ItemTemplate>
            <DataTemplate DataType="{x:Type viewModels:MyTabViewModel}">
                <TextBox x:Name="TabNameTextBox" 
                         Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                         MouseDoubleClick="TabNameTextBox_MouseDoubleClick"
                         LostFocus="TabNameTextBox_LostFocus"
                         Cursor ="Arrow"/>        
            </DataTemplate>
        </Mah:MetroAnimatedTabControl.ItemTemplate>
    </Mah:MetroAnimatedTabControl>

Code behind events MouseDoubleClick and Lost Focus:

    private void TabNameTextBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
    {
        var textBox = (TextBox)sender;
        textBox.Focusable = true;
        textBox.Focus();
        textBox.SelectAll();
    }

    private void TabNameTextBox_LostFocus(object sender, RoutedEventArgs e)
    {
        var textBox = (TextBox)sender;
        textBox.Focusable = false;
    }

I found a similar question where the asker couldn't get the Lost Focus event to fire. In my situation, it is firing before I expect it to.

Upvotes: 2

Views: 466

Answers (1)

Tam Bui
Tam Bui

Reputation: 3048

I got the following to work with a standard WPF TabControl. My guess is that it should work for a Mah:MetroAnimatedTabControl as well.

MainWindow.xaml:

<TabControl ItemsSource="{Binding Items}">
    <TabControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:Item}">
            <Grid>
                <TextBox x:Name="textBox" Text="{Binding Name,UpdateSourceTrigger=PropertyChanged}" LostFocus="textBox_LostFocus" Visibility="Collapsed"/>
                <TextBlock x:Name="textBlock" Text="{Binding Name}" MouseDown="TextBlock_MouseDown"/>
            </Grid>
        </DataTemplate>
    </TabControl.ItemTemplate>
    <TabControl.ContentTemplate>
        <DataTemplate DataType="{x:Type local:Item}">
            <TextBlock Text="{Binding Contents}"/>
        </DataTemplate>
    </TabControl.ContentTemplate>
</TabControl>

MainWindow.xaml.cs:

public partial class MainWindow : Window
{
    private ObservableCollection<Item> items;

    public MainWindow()
    {
        InitializeComponent();
        Items.Add(new Item() { Name = "Item 1" });
        Items.Add(new Item() { Name = "Item 2" });
        Items.Add(new Item() { Name = "Item 3" });
        this.DataContext = this;
    }

    public ObservableCollection<Item> Items
    {
        get
        {
            if (items == null)
                items = new ObservableCollection<Item>();
            return items;
        }
    }

    private void TextBlock_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount == 2 && e.ButtonState == MouseButtonState.Pressed)
        {
            var grid = (Grid)VisualTreeHelper.GetParent(sender as UIElement);
            (sender as UIElement).Visibility = Visibility.Collapsed;
            grid.Children[0].Visibility = Visibility.Visible;
            grid.Children[0].Focus();
            ((TextBox)grid.Children[0]).SelectAll();
            e.Handled = true;
        }
    }

    private void textBox_LostFocus(object sender, RoutedEventArgs e)
    {
        var grid = (Grid)VisualTreeHelper.GetParent(sender as UIElement);
        (sender as UIElement).Visibility = Visibility.Collapsed;
        grid.Children[1].Visibility = Visibility.Visible;
    }
}

Item.cs:

public class Item : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public Item()
    {
        this.PropertyChanged += OnItemPropertyChanged;
    }

    private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Name))
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Contents)));
        }
    }

    private string name;

    public string Name { get => name; set { name = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name))); } }

    public string Contents { get => $"Contents of {Name}"; }


}

Screenshot after I double-clicked the "Item 3" header name:

enter image description here

Upvotes: 2

Related Questions