There are several ways to approach it - our standard is to warn the user and give them a chance to remain on the dirty page. Our pages are broken up into a Search tab, an Add tab, an Edit tab, etc. If the user has modified data on an Add or Edit tab and clicks on another tab we want to display a dialog box and give them an opportunity to stay on the current tab.
In WPF the only event available that is consistently raised when a user tries to change tabs on a tab control is the SelectionChanged event. This is raised AFTER the selection has changed so if you want to keep the user on the current tab you have to set the tab control's SelectedIndex back to what it was BEFORE the event was changed. This requires you to track the current index. No big deal. The real problem is that setting the SelectedIndex back only works a few times, then stops. Try this XAML and code behind...
<Window x:Class="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>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<TabControl Name="t1" Grid.Row="0" SelectionChanged="t1_SelectionChanged_1" >
<TabItem Name="Search" Header="Search">
<CheckBox Name="CanEditCheckbox" Content="Can Edit?" ></CheckBox>
</TabItem>
<TabItem Name="Edit" Header="Edit">
<Label Name="EditLabel" Content="Edit"></Label>
</TabItem>
</TabControl>
<Label Name="MessageLabel" Grid.Row="1"/>
</Grid>
</Window>
Class MainWindow
Private Sub t1_SelectionChanged_1(sender As Object, e As SelectionChangedEventArgs)
If t1.SelectedIndex = 1 Then
If Not CanEditCheckbox.IsChecked Then
t1.SelectedIndex = 0
MessageLabel.Content = "No Edit You! " & Now()
Else
MessageLabel.Content = "OK You Edit Now! " & Now()
End If
End If
End Sub
End Class
Each time you click on the [Edit] tab you will see a message saying "No Edit You!" with the time after it. However after you have clicked it two or three times the time stops updating. In fact, the SelectionChanged event handler is no longer being called. That's because we're changing the SelectedIndex back to zero on the same thread. It immediately calls the SelectionChanged event handler again and stuff gets very messy.
To fix the problem we have to set the index back in a background thread. Fortunately WPF makes this fairly easy. Try changing the code behind like this (note the new Import)...
Imports System.Windows.Threading
Class MainWindow
Private Sub t1_SelectionChanged_1(sender As Object, e As SelectionChangedEventArgs)
If t1.SelectedIndex = 1 Then
If Not CanEditCheckbox.IsChecked Then
Application.Current.Dispatcher.BeginInvoke(DirectCast(Sub() t1.SelectedIndex = 0, Action), DispatcherPriority.Render, Nothing)
MessageLabel.Content = "No Edit You! " & Now()
Else
MessageLabel.Content = "OK You Edit Now! " & Now()
End If
End If
End Sub
End Class
All we did was create a lambda function to change the SelectedIndex and run it in a lower priority background thread. It doesn't get executed until our event handler is completed and nothing gets messed up.
Awesome solution, thank you so much! BeginInvoke was exactly what I needed! (C#)
ReplyDelete