【问题标题】:Event before user changes TabItem用户更改 TabItem 之前的事件
【发布时间】:2017-04-24 22:34:28
【问题描述】:

我有一个 WPF 应用程序,其中包含一个 TabControl 和几个 TabItem,每个 TabItem 包含一个 UserControl。用户可以更改 TabItem 中包含的用户控件中的条目,例如配置应用程序。

<TabControl>
  <TabItem Header="Configuration">
    <views:ConfigurationView x:Name="ConfigurationView_Object" />
  </TabItem>
  <TabItem Header="Artist">
    ...
  </TabItem>
</TabControl>

我有一个功能可以检查用户控件中是否有未保存的更改。 用户更改标签或关闭应用程序之前,我想让他选择保存、丢弃或留在标签上。

这可能吗?如果可以,怎么做?如果它需要除 TabControl 之外的其他控件/结构,那也可以,因为我目前正处于计划阶段...

提前致谢,
弗兰克

【问题讨论】:

    标签: wpf events


    【解决方案1】:

    TabControl 没有 TabChanging 事件。但是,您可以使用.Items.CurrentChanging 事件。这仅在您在 TabControl 上设置 IsSynchronizedWithCurrentItem="True" 时才有效

    XAML**

      <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height='1*' />
          <RowDefinition Height='Auto' />
        </Grid.RowDefinitions>
        <TabControl x:Name='MainTab'
                    IsSynchronizedWithCurrentItem='True'
                    Grid.Row='0'>
          <TabItem x:Name='TabTickets'
                   Header='Tickets'>
            
              <StackPanel Orientation='Horizontal' >
                <TextBlock Text='Provide some text:'
                           Margin='10,0' />
                <TextBox x:Name='ExampleTextBox'
                         VerticalAlignment='Top' MinWidth='90' />
              </StackPanel>
           
          </TabItem>
          <TabItem x:Name='TabCalendar'
                   Header='Calendar' />
          <TabItem x:Name='TabAbout'
                   Header='About' />
        </TabControl>
    
        <TextBlock x:Name='MessageTextBox'
                   Grid.Row='1' />
    
      </Grid>
    

    代码

       public TabChangingWindow() {
          InitializeComponent();
    
          MainTab.Items.CurrentChanging += Items_CurrentChanging;
        }
    
        void Items_CurrentChanging(object sender,
                                   System.ComponentModel.CurrentChangingEventArgs e) {
          if (e.IsCancelable)
          {
            var fromElement = ((ICollectionView)sender).CurrentItem as FrameworkElement;
            var toElement = MainTab.SelectedItem as FrameworkElement;
            if (fromElement!= null && toElement!= null)
            {
              if (ExampleTextBox.Text.Length == 0)
              {
                e.Cancel = true;
                MessageTextBox.Text = "Example Text cannot be blank.";
                MainTab.SelectedItem = fromElement;
              }
              else
              {
                MessageTextBox.Text = 
                 String.Format("Changing from {0} to {1}", fromElement.Name, toElement.Name);
              }
             
            }
            
          }
    
        }
    

    截图

    当数据不完整时,防止移动到另一个选项卡。


    数据完成后允许移动到另一个选项卡。

    【讨论】:

    • 感谢您的回复!问题是,只要在更改期间没有用户交互,此解决方案就可以工作。 (见stackoverflow.com/questions/30706758/…)但我想问用户,他想做什么(保存/丢弃/留下)
    • 你试过代码了吗?用户无法与 UI 的任何其他部分进行交互,它们会被发送回原始选项卡。如果这还不够,您可以使用消息框来提示用户,并且您可以在对话框上设置按钮以从用户那里获取信息。它是模态的,在关闭消息框之前,用户将无法与应用交互。
    • 如何向用户显示对话框并获得结果与您提出的问题不同。见stackoverflow.com/questions/3830228/…
    【解决方案2】:

    您可以对任何事件使用交互性。这是 MVVM 解决方案。

    <TabControl>
       <TabItem Header="Configuration">
       <views:ConfigurationView x:Name="ConfigurationView_Object" />
        <intr:Interaction.Triggers>
             <intr:EventTrigger EventName="MouseUp">
                   <intr:InvokeCommandAction Command="{Binding Yourcommand}" CommandParameter="YourCommandParameter"/>
                   </intr:EventTrigger>
             </intr:Interaction.Triggers>
    

    【讨论】:

    • xmlns:intr="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    • 有趣的可能性。我不确定 mouseup 事件是否足以涵盖更改选项卡的所有可能方式,然后我仍然需要防止选项卡更改...
    【解决方案3】:

    如果你想通过事件来实现,你可以对 TabControl 使用 SelectionChanged 事件,对 Window 使用 Closing 事件。像这样的:

    XAML:

    <Window x:Class="Namespace.View"
            .....
            Closing="Window_Closing">
    ......
    

    C#:

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        if (UnsavedChanges())
        {
            //save changes            
        }        
    }
    

    XAML:

    <TabControl SelectionChanged="TabControl_SelectionChanged">
        <TabItem Header="Configuration">
        <views:ConfigurationView x:Name="ConfigurationView_Object" />
     </TabItem>
     <TabItem Header="Artist">
      ...
     </TabItem>
    </TabControl>
    

    C#:

    private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (UnsavedChanges())
        {
            //save changes
        }
    }
    

    【讨论】:

    • 感谢您的回复!关键是,我还想让用户取消更改,保留在选定的选项卡上。从我在 Stackoverflow 上读到的内容来看(stackoverflow.com/questions/30706758/…),这似乎相当复杂......
    【解决方案4】:

    我终于自己实现了一个简单的 tabcontrol,因为这样我就可以控制一切了。

    <WrapPanel x:Name="WrapPanel_Main"> <!-- This is the TabControl -->
      <Border x:Name="Border_Configuration" Margin="5,5,0,0" BorderThickness="4,4,4,0"> <!-- This is the first tab -->
        <TextBlock x:Name="TextBlock_Configuration" Text="Configuration" Padding="5" MouseLeftButtonUp="TextBlock_Step_MouseLeftButtonUp"/>
      </Border>
      <Border Margin="5,5,0,0" BorderThickness="4,4,4,0"> <!-- This is the second tab -->
        <TextBlock x:Name="TextBlock_Artists" Text="Artists" Padding="5" MouseLeftButtonUp="TextBlock_Step_MouseLeftButtonUp" />
      </Border>
      <Border Margin="5,5,0,0" BorderThickness="4,4,4,0"> <!-- This is the third tab -->
        <TextBlock x:Name="TextBlock_ReleaseGroups" Text="Release Groups" Padding="5" MouseLeftButtonUp="TextBlock_Step_MouseLeftButtonUp"/>
      </Border>
    </WrapPanel>
    
    <Border x:Name="Border_Placeholder" Grid.Row="1" Margin="5,0,5,5"> <!-- placeholder for the content of each tab -->
      <ContentControl x:Name="ContentControl_Placeholder" Grid.Row="1" Padding="5" />
    </Border>
    

    这里是处理“选项卡”的 Mouse-Up-Event 的处理程序。我创建了一个界面,每个用作选项卡内容的用户控件都必须实现。这允许用户控件通知“选项卡控件”有关未保存的更改并采取适当的(用户选择)操作。 之后,它会加载新内容并更改“Tab-Headers”的外观。在我看来,这导致的更多代码量对于选项卡更改的完全控制是可以接受的。

    private void TextBlock_Step_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
      ICancelUnloading currentElement = ContentControl_Placeholder.Content as ICancelUnloading;
      if (currentElement != null)
      {
        if (currentElement.UnsavedChanges)
        {
          MessageBoxResult result = MessageBox.Show("Yes: Save, No: Discard, Cancel: Stay", "Unsaved Changes", MessageBoxButton.YesNoCancel, MessageBoxImage.Warning, MessageBoxResult.Cancel);
          if (result == MessageBoxResult.Cancel)
            return;
          if (result == MessageBoxResult.Yes)
            currentElement.Save();
        }
      }
    
      TextBlock textBlock = sender as TextBlock;
      if (textBlock != null)
      {
        switch (textBlock.Name)
        {
          case "TextBlock_Configuration":
            ContentControl_Placeholder.Content = new ConfigurationView();
            break;
          case "TextBlock_Artists":
            ContentControl_Placeholder.Content = new ArtistsView();
            break;
          case "TextBlock_ReleaseGroups":
            ContentControl_Placeholder.Content = new ReleaseGroupsView();
            break;
        }
    
        ActivateTab(textBlock);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多