Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TabControl- preventing user from changing the selected tab: MessageBox causing bug

I've been pounding away at this issue for a little while, and have only found part of the solution.

I'm trying to set up a TabControl so that I can in some cases prevent the user from changing the currently selected tab. When the user is prevented from changing the currently selected tab, then they are shown a dialog box.

I have already read the following documents:

  • WPF - reset ListBox scroll position when ItemsSource changes
  • http://wizardsofsmart.net/uncategorized/itemssourcechanged-event-using-attached-dependency-properties/
  • http://joshsmithonwpf.wordpress.com/2009/09/04/how-to-prevent-a-tabitem-from-being-selected/
  • http://social.expression.microsoft.com/Forums/en-US/wpf/thread/f7b46018-1e97-4bbe-ada8-49b75dbc1da2/

I have implemented the solution indicated in the 3rd link (though all of the above create the same error seen below). And it works, but...

Things mess up thoroughly if the user does the following:

  • attempts to change the tab when such an action is disallowed. The MessageBox pops up with the error.
  • the user clicks "OK" and is returned to the original window.
  • the user tries again to change the tab. No MessageBox appears.
  • if the user minimizes the window, and then maximizes it again, then the MessageBox that was supposed to appear earlier appears.
  • the user clicks "OK" and is returned to the original window... but the tab has been changed to the one they selected before, even though they should not be able to change tabs.

This is obviously not ideal behavior. Why isn't the MessageBox appearing the second time, and why is the tab changing when it should be disallowed from doing so?

If I remove the MessageBox part, it works fine.

Here is the code for the TabControl.SelectionChanged event handler:

bool _isChanging = false;

    private void tabControlForNavigation_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (!_isChanging && canChangeTabs.IsChecked.HasValue)
        {
            _isChanging = true;


            bool canLeave = canChangeTabs.IsChecked.Value;  //normally this would be replaced by a check in the ViewModel

            if (!canLeave)
            {
                int prevIndex = tabControlForNavigation.Items.IndexOf(tabControlForNavigation.SelectedContent);
                tabControlForNavigation.SelectedIndex = prevIndex;
                MessageBox.Show("Can't change tabs!"); //if I comment out this line, everything works fine.
            }

            _isChanging = false;
        }
    }

I am using MVVM to implement this. The Window looks like this:

<Window x:Class="TestTabControlSwitching.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
    Height="350"
    Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <CheckBox x:Name="canChangeTabs"
              Content="Can Change Tabs"
              IsChecked="True" />
    <TabControl x:Name="tabControlForNavigation"
                Grid.Row="1"
                IsSynchronizedWithCurrentItem="True"
                ItemsSource="{Binding Collection}"
                SelectedItem="{Binding SelectedItem}"
                SelectionChanged="tabControlForNavigation_SelectionChanged"
                Margin="4"
                HorizontalAlignment="Stretch">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <ContentPresenter Content="{Binding Path=Name}" />
            </DataTemplate>
        </TabControl.ItemTemplate>

    </TabControl>
</Grid>

I'm omitting the rest of the code for sake of brevity- there is a pretty straight-forward ViewModel structure backing the window.

like image 423
skybluecodeflier Avatar asked Sep 06 '11 22:09

skybluecodeflier


1 Answers

As you noticed, the problem is the MessageBox inside the event handler. The focus will change to the MessageBox and you can get all kind of undesired effects. I've had my own problems with this.

Here is a couple of SO question on the same subject
WPF: Does MessageBox Break PreviewMouseDown?
Wpf stop routing event when MessageBox appear?

If you must display a message to the user then an alternate approach might be to create a new Window which you style like a MessageBox and then call Show (not ShowDialog) on it inside the event handler.

like image 139
Fredrik Hedblad Avatar answered Sep 28 '22 13:09

Fredrik Hedblad