且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

在异步WPF TabControl的问题结合的SelectedItem

更新时间:2023-12-06 17:02:34

我会专注于自己原来的问题,没有异步的部分。

I'll focus on your original problem, without the async part.

为什么添加一个新的标签时,标签不正确选择的原因是因为你设置中值 Col​​lectionChanged 事件处理程序。引发事件导致它们被加入以便处理程序的顺序调用。既然你在构造函数中添加处理器,它总是会被调用的第一位的,什么是重要的,它永远是更新的TabControl 一前调用。所以,当你在处理程序设置属性,的TabControl 还没有的知道这有集合在这样一个标签。更precisely,尚未产生用于标签的报头的容器,并作为选择它不能标记(这将导致您缺少视觉效果),而且,也不会当它最终生成可以。 TabControl.SelectedItem 还是更新,让你看到标签的内容,但它也会引起头容器$ P $的选择为无标记pviously标记,并且你最终结束与没有选项卡中选择明显

The reason why the tabs are not properly selected when adding a new tab is because you set the Selected value in the CollectionChanged event handler. Raising an event causes sequential invocation of handlers in order in which they were added. Since you add your handler in the constructor, it will always be the first one to be invoked, and what's important, it will always be invoked before the one that updates the TabControl. So when you set the Selected property in your handler, TabControl doesn't yet "know" that there's such a tab in the collection. More precisely, the header container for the tab is not yet generated, and it cannot be marked as selected (which causes the visual effect you're missing), moreover, it won't be when it's finally generated. TabControl.SelectedItem is still updated, so you see the content of the tab, but it also causes header container previously marked as selected to be unmarked, and you eventually end up with no tab visibly selected.

根据您的需要,有几种方法来解决这个问题。如果添加新的选项卡的唯一途径就是通过 AddNewTabCommand ,你可以只修改 AddNewTab 方法:

Depending on your needs, there are several ways to solve this problem. If the only way of adding new tabs is through the AddNewTabCommand, you could just modify the AddNewTab method:

private void AddNewTab()
{
    var tab = new Tab(GetNextName(), GetRandomContent());
    Tabs.Add(tab);
    Selected = tab;
}

在这种情况下,你不应该设置在 Col​​lectionChanged 处理程序价值,因为它会prevent 的PropertyChanged 从在合适的时间被提出。

In this case you should not set the Selected value in the CollectionChanged handler, because it will prevent PropertyChanged from being raised at the right time.

如果 AddNewTabCommand 不加入标签,我通常做的唯一方法是创建一个专门的回收这将做必要的逻辑(这个类是嵌套在 TabGroup

If AddNewTabCommand is not the only way of adding tabs, what I usually do is to create a dedicated collection which would do the required logic (this class is nested in TabGroup):

private class TabsCollection : ObservableCollection<Tab>
{
    public TabsCollection(TabGroup owner)
    {
        this.owner = owner;
    }

    private TabGroup owner;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e); //this will update the TabControl
        var newItems = e.NewItems?.Cast<Tab>()?.ToList();
        if (newItems?.Any() == true)
            owner.Selected = newItems.Last();
    }
}

然后简单地初始化集合中的 TabGroup 构造器:

Tabs = new TabsCollection(this);

如果这种情况出现在不同的地方,你不喜欢重复你的code,你可以创建一个可重用的集合类:

If this scenario appears in various places and you don't like repeating your code, you could create a reusable collection class:

public class MyObservableCollection<T> : ObservableCollection<T>
{
    public event NotifyCollectionChangedEventHandler AfterCollectionChanged;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnCollectionChanged(e);
        AfterCollectionChanged?.Invoke(this, e);
    }
}

,然后订阅 AfterCollectionChanged 每当你需要确保所有的 Col​​lectionChanged 用户已收到通知。

and then subscribe to AfterCollectionChanged whenever you need to be sure that all CollectionChanged subscribers have been notified.