WPF删除选项卡后不会处理内容?

3

我正在编写程序,在一个TabControl中以编程方式添加选项卡。每个选项卡的内容都是使用UserControl填充的:

private void process_addTab(string head, string type)
{
    TabItem tab = new TabItem();
    tab.Header = head;

    switch (type)
    {
        case "trophy2": tab.Content = new Scoreboard2(); break;
        case "trophy4": tab.Content = new Scoreboard4(); break;
        case "text": tab.Content = new TextFields(); break;
        case "talk": tab.Content = new LowerThirds(); break;
        case "image": tab.Content = new ImageSelect(); break;
        case "timer": tab.Content = new Timer(); break;
        case "hitbox": tab.Content = new Hitbox(); break;
        case "twitch": tab.Content = new Twitch(); break;
        case "twitter": tab.Content = new Twitter(); break;
        case "ustream": tab.Content = new Ustream(); break;
    }

    tabControl.Items.Add(tab);
}

这很好用。但是,当我从TabControl中删除选项卡时,问题就出现了。每个选项卡都有一个按钮,可以将该特定选项卡从控件中删除:

public static void RemoveClick(TabControl tabControl)
{
    if (MessageBox.Show("Are you sure you wish to remove this tab?", "Remove Tab",
        MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
    {
        TabItem item = (TabItem)tabControl.SelectedItem;
        tabControl.Items.Remove(tabControl.SelectedItem);
    }
}

这种方法看起来可以很好地去掉选项卡。但是它有点具有误导性。在一些控件中,我有定时器函数运行DispatcherTimer。例如,Twitch控件内部有一个定时器,每30秒轮询Twitch API以获取频道信息。如果我删除选项卡,定时器仍然会继续运行;即使它不应该存在了。
有任何解决办法吗?假设我对C#不太熟悉;因为我确实不太熟悉。

1
为什么计时器不应该再存在?调度程序拥有对计时器的引用,因此它不会被垃圾回收,而框架也不知道您创建它的原因,因此不会随意停用它。您需要自己禁用它。 - Mike Strobel
计时器位于用户控件内部,该用户控件位于选项卡项内...而该选项卡项已被移除。移除选项卡项不应该同时移除包含计时器的用户控件吗? - Jason Axelrod
为什么会这样呢?你所做的只是从可视树中移除“UserControl”。这并不会本质上导致它“消失”,只是将其从显示中移除。只有在没有对它的引用,并且垃圾回收器已经将其删除时,它才真正地从内存中“消失”。由于计时器仍然具有活动引用(在“Dispatcher”中),因此不会发生这种情况。 - Mike Strobel
好的,那么我该如何处理用户控件以及其中的DispatcherTimer? - Jason Axelrod
1
当您移除选项卡时,可以设置 timer.IsEnabled = false。或者,您可以通过处理选项卡内容的 LoadedUnloaded 事件,并分别将 IsEnabled 设置为 truefalse 来管理其状态。 - Mike Strobel
1个回答

1
一般情况下,当WPF中的用户控件从视口中移除时,它们不会被处理。这会在某些应用程序中引起问题(有关更广泛的讨论,请参见Disposing WPF User Controls)。
要解决您更具体的问题(例如停止计时器),请在TabItem上处理Unloading事件,并在那里禁用计时器(请参见https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.tabitem)。
以下是一个高级示例(子类化TabItem仅作为封装计时器的示例):
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        TimerTabItem item = new TimerTabItem();
        item.Unloaded += ItemOnUnloaded;
        tabControl.Items.Add(item);
    }

    private void ItemOnUnloaded(object sender, RoutedEventArgs routedEventArgs)
    {
        (sender as TimerTabItem).Dispose();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        tabControl.Items.Remove(tabControl.Items[0]);
    }
}

class TimerTabItem : TabItem, IDisposable
{
    private DispatcherTimer MyTimer { get; set; }

    public TimerTabItem() : base()
    {
        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = new TimeSpan(0, 0, 0, 3);
        timer.Tick += TimerOnTick;
        timer.Start();
        MyTimer = timer;
    }

    private void TimerOnTick(object sender, EventArgs eventArgs)
    {
        MessageBox.Show("Hello!");
    }

    #region Implementation of IDisposable

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    /// <filterpriority>2</filterpriority>
    public void Dispose()
    {
        MyTimer.Stop();
        MyTimer.Tick -= TimerOnTick;
        MyTimer = null;
    }

    #endregion
}

嗯...我正在使用我的DispatcherTimer作为示例。但是,用户控件中还有其他元素需要清除。有没有一种快速简便的方法来处理整个用户控件的内容?此外,DispatcherTimer没有Dispose函数。 - Jason Axelrod
在DispatcherTimer上,您可以调用Stop()方法(http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer(v=vs.110).aspx)。对于其他内容,您可以实现IDisposable接口,编写拆卸逻辑,然后从事件处理程序中调用Dispose方法。 - Michael
你介意给我一个如何完成所有这些的例子吗?另外,我在某个地方读到WPF不支持IDisposable。 - Jason Axelrod
刚刚更新了示例。WPF不支持IDisposable,你仍然需要处理Unloaded事件并自行调用Dispose方法。 - Michael

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