Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Tab Control Prevent Tab Change

Tags:

c#

wpf

I'm trying to develop a system maintenance screen for my application in which I have several tabs each representing a different maintenance option i.e. maintain system users et cetera. Once a user clicks on edit/new to change a existing record I want to prevent navigating away from the current tab until the user either clicks save or cancel.

After some googling I've found a link http://joshsmithonwpf.wordpress.com/2009/09/04/how-to-prevent-a-tabitem-from-being-selected/ which seemed to solve my problem, or so I thought.

I've tried implementing this, but my event never seems to fire. Below is my XAML.

<TabControl Name="tabControl">
    <TabItem Header="Users">
        <DockPanel>
            <GroupBox Header="Existing Users" Name="groupBox1" DockPanel.Dock="Top" Height="50">
                <StackPanel Orientation="Horizontal">
                    <Label Margin="3,3,0,0">User:</Label>
                    <ComboBox Width="100"  Height="21" Margin="3,3,0,0"></ComboBox>
                    <Button Width="50" Height="21" Margin="3,3,0,0" Name="btnUsersEdit" Click="btnUsersEdit_Click">Edit</Button>
                    <Button Width="50" Height="21" Margin="3,3,0,0" Name="btnUsersNew" Click="btnUsersNew_Click">New</Button>
                </StackPanel>
            </GroupBox>
            <GroupBox Header="User Information" Name="groupBox2">
                <Button Content="Cancel" Height="21" Name="btnCancel" Width="50" Click="btnCancel_Click" />
            </GroupBox>
        </DockPanel>
    </TabItem>
    <TabItem Header="User Groups">

    </TabItem>        
</TabControl>

And this is my code

public partial class SystemMaintenanceWindow : Window
{

    private enum TEditMode { emEdit, emNew, emBrowse }

    private TEditMode _EditMode = TEditMode.emBrowse;        

    private TEditMode EditMode
    {
        get { return _EditMode; }
        set 
        { 
            _EditMode = value; 
        }
    }        

    public SystemMaintenanceWindow()
    {
        InitializeComponent();

        var view = CollectionViewSource.GetDefaultView(tabControl.Items.SourceCollection);
        view.CurrentChanging += this.Items_CurrentChanging;
    }        

    void Items_CurrentChanging(object sender, CurrentChangingEventArgs e)
    {
        if ((e.IsCancelable) && (EditMode != TEditMode.emBrowse))
        {
            var item = ((ICollectionView)sender).CurrentItem;
            e.Cancel = true;
            tabControl.SelectedItem = item;

            MessageBox.Show("Please Save or Cancel your work first.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }        

    private void btnUsersNew_Click(object sender, RoutedEventArgs e)
    {
        EditMode = TEditMode.emNew;
    }

    private void btnUsersEdit_Click(object sender, RoutedEventArgs e)
    {
        EditMode = TEditMode.emEdit;
    }

    private void btnCancel_Click(object sender, RoutedEventArgs e)
    {
        EditMode = TEditMode.emBrowse;
    }
}

Apologies now if I'm being stupid, but for the life of me I cannot workout see why my event does not fire when the user clicks between tabs.

Thanks for all your help.

Emlyn

like image 903
Emlyn Avatar asked Feb 23 '11 11:02

Emlyn


People also ask

How to Prevent Tab change in wpf?

If you want to enable or disable specific tab item(s) you have to use the Boolean property IsEnabled of the class RadTabItem. You can set it from your code-behind, XAML or in Blend.


3 Answers

According to this post

https://social.msdn.microsoft.com/Forums/vstudio/en-US/d8ac2677-b760-4388-a797-b39db84a7e0f/how-to-cancel-tabcontrolselectionchanged?forum=wpf

this worked for me:

<TabControl>
   <TabControl.Resources>
     <Style TargetType="TabItem">
        <EventSetter Event="PreviewMouseLeftButtonDown" 
            Handler="OnPreviewMouseLeftButtonDown"/>
     </Style>  
   </TabControl.Resources>
   <TabItem Header="Tab1"/>
   <TabItem Header="Tab2"/>
</TabControl>
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (e.Source is TabItem) //do not handle clicks on TabItem content but on TabItem itself
    {
        var vm = this.DataContext as MyViewModel;
        if (vm != null)
        {
            if (!vm.CanLeave())
            {
                e.Handled = true;
            }
        }
    }
}
like image 184
clemensoe Avatar answered Nov 05 '22 09:11

clemensoe


I was struggling with this too. Just got it working by simply adding the

IsSynchronizedWithCurrentItem="True"

setting to the TabControl. Worked like a charm after that.

like image 24
robertmiles3 Avatar answered Nov 05 '22 08:11

robertmiles3


I've come up with a solution which suits my needs. Seems slightly backwards but compared to the other options I found, it seems nice and neat.

Basically I keep a private variable of the current tabIndex and on SelectionChanged event of the TabControl, I'm doing some checks and set the TabControl.SelectedIndex back to this value if the user is not in browse mode.

private void tabControl_SelectionChanged(object sender, 
    System.Windows.Controls.SelectionChangedEventArgs e)
{
    if (e.OriginalSource == tabControl)
    {
        if (EditMode == TEditMode.emBrowse)
        {
            _TabItemIndex = tabControl.SelectedIndex;
        }
        else if (tabControl.SelectedIndex != _TabItemIndex) 
        {
            e.Handled = true;
            tabControl.SelectedIndex = _TabItemIndex;
            MessageBox.Show("Please Save or Cancel your work first.", "Error", 
                MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}
like image 20
Emlyn Avatar answered Nov 05 '22 07:11

Emlyn