WPF标签控件如何防止标签切换

11

我正在尝试为我的应用程序开发一个系统维护界面,在该界面中,有几个选项卡分别表示不同的维护选项,例如维护系统用户等。一旦用户单击编辑/新建以更改现有记录,我希望防止从当前选项卡导航到其他选项卡,直到用户单击保存或取消。

经过一些谷歌搜索,我找到了一个链接http://joshsmithonwpf.wordpress.com/2009/09/04/how-to-prevent-a-tabitem-from-being-selected/,它似乎解决了我的问题,但事实并非如此。

我尝试实现这个解决方案,但我的事件似乎从未触发。以下是我的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>

这是我的代码

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;
    }
}

若我有所愚见,还请谅解。但是我真的不能理解为什么当用户在选项卡之间点击时,我的事件没有触发。

感谢您的所有帮助。

Emlyn


Josh的方法对我也不起作用。 - Bolu
8个回答

16

我想出了一个适合我的需求的解决方案。看起来有点反常,但与我发现的其他选项相比,它似乎更加美观整洁。

基本上,我保留了当前tabIndex的私有变量,并在TabControlSelectionChanged事件上进行了一些检查。如果用户不处于浏览模式,则将TabControl.SelectedIndex设置回此值。

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);
        }
    }
}

9
我也曾遇到这种问题。只需添加以下内容即可解决:
IsSynchronizedWithCurrentItem="True"

将设置应用于TabControl。之后就像魔法一样运作。


1
太好了...我们这里也遇到了同样的问题,现在解决了。谢谢! - MetalMikester

2
根据这篇文章。

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

这对我有用:
<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;
            }
        }
    }
}

0

我开始掌握XAML,所以希望我没有错过你的重点。我遇到了同样的问题,对我来说这是最好的解决方法。

在XAML中,我定义了一个样式来切换只读状态。

<!-- prevent the tab from being changed while editing or adding a physician -->
<Style BasedOn="{StaticResource {x:Type TabControl}}" 
        TargetType="{x:Type TabControl}" x:Key="InactivateTabControl">
    <!-- <Setter Property="IsEnabled" Value="True" /> -->
    <Style.Triggers>
        <DataTrigger Binding="{Binding PhysicianTypeTabAreLocked}" Value="False">
            <Setter Property="IsEnabled" Value="True" />
        </DataTrigger>
        <DataTrigger Binding="{Binding PhysicianTypeTabAreLocked}" Value="True">
            <Setter Property="IsEnabled" Value="False" />
        </DataTrigger>
    </Style.Triggers>
</Style>

<TabControl Grid.Row="1" Grid.Column="0" Margin="0, 10, 0, 0"
     x:Name="PhysicianTypesTabControl" 
     SelectedIndex="{Binding PhysicianTypeSelectedTabIndex, Mode=TwoWay}" 
     Style="{StaticResource InactivateTabControl}">

    <!-- rest here ....-->

 <TabControl/>

在视图模型中,我有一个属性。
    private EditingState _PhysicianEditingState;

    public EditingState PhysicianEditingState
    {
        get { return _PhysicianEditingState; }
        set
        {
            _PhysicianEditingState = value;

            PhysicianTypeTabAreLocked = (PhysicianEditingState != EditingState.NotEditing);
            NotifyPropertyChanged("PhysicianTypeTabAreLocked");
        }
    }

希望这能有所帮助。

0
我最终发现对我最有效的方法是在我不想让用户选择选项卡时禁用“TabItems”,而当我想要它们可用时再启用它们。
<TabControl IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Pages}" SelectedItem="{Binding SelectedPage}" TabStripPlacement="Left">
        <TabControl.Resources>
            <Style BasedOn="{StaticResource {x:Type TabItem}}" TargetType="TabItem">
                <Setter Property="IsEnabled" Value="{Binding IsEnabled}" />
            </Style>
        </TabControl.Resources>
        <TabControl.ItemTemplate>
            <DataTemplate>
                <Label Content="{Binding StepName}" Foreground="{DynamicResource TabActiveSelectedFont}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
</TabControl>

这样可以让我的视图模型(在本例中是我的页面)能够确定何时允许用户移动到下一页。因此,当他们完成当前页面时,我会“启用”下一页,以便他们可以单击它继续。


0

或者自己实现...

public delegate void PreviewSelectionChangedEventHandler(object p_oSender, PreviewSelectionChangedEventArgs p_eEventArgs);

public class PreviewSelectionChangedEventArgs
{
    internal PreviewSelectionChangedEventArgs(IList p_lAddedItems, IList p_lRemovedItems)
    {
        this.AddedItems = p_lAddedItems;
        this.RemovedItems = p_lRemovedItems;
    }
    public bool Cancel { get; set; }
    public IList AddedItems { get; private set; }
    public IList RemovedItems { get; private set; }
}

public class TabControl2: TabControl
{
    public event PreviewSelectionChangedEventHandler PreviewSelectionChanged;

    private int? m_lLastSelectedIndex;

    protected override void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        base.OnSelectionChanged(e);

        // déterminer si on doit annuler la sélection
        PreviewSelectionChangedEventArgs eEventArgs = new PreviewSelectionChangedEventArgs(e.AddedItems, e.RemovedItems);
        if (m_lLastSelectedIndex.HasValue)
            if (PreviewSelectionChanged != null)
                PreviewSelectionChanged(this, eEventArgs);

        // annuler (ou pas) la sélection
        if (eEventArgs.Cancel)
            this.SelectedIndex = m_lLastSelectedIndex.Value;
        else
            m_lLastSelectedIndex = this.SelectedIndex;
    }
}

我正在使用这种技术,它似乎运行良好。由于我想告诉用户为什么他们不能更改选项卡,所以我将以下内容添加到PreviewSelectionChanged处理程序中:if (dontAllowTabChange) { eventargs.Cancel = true; Dispatcher.BeginInvoke(new Action(() => { MessageBox.Show("请在离开此选项卡之前保存更改。", "警告", MessageBoxButton.OK, MessageBoxImage.Exclamation); })); } - Number8

0

Josh正在使用tab.ItemsSource。而您正在使用tab.Items.SourceCollection。这可能是问题所在。


谢谢您的建议,但是由于我的选项卡在XAML中是“硬编码”的,而不是通过集合创建的,所以这对我似乎不起作用。 - Emlyn

0
您可以简单地将TabItems的IsEnabled属性设置为false,这将阻止导航/激活但不会禁用选项卡的内容,直到满足您的条件(保存/取消)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接