WPF动态嵌套控件所选项目的问题

问题描述:

当我动态创建数据绑定的用户控件时,WPF存在一个真正的问题,即WPF无法维护当前的Selected Item.解释它的最简单方法是向您展示一个简化的示例.

在这个简单的示例中,我有一个WPF窗口,该窗口在XAML标记中包含一个名为OuterTabs的TabControl. TabControl具有用于本地DummyClass的DataTemplate,因此只要将DummyClass作为其内容,它就会显示一个名为InnerTabs的用户控件.

这是我的MainWindow.xaml:

I''m having a real problem with WPF not maintaining the current Selected Item when I create a data-bound user control dynamically. The easiest way to explain it is to show you a stripped-down example.

In this simple example I have a WPF window that contains a TabControl named OuterTabs in the XAML markup. The TabControl has a DataTemplate for a local DummyClass, so that it will display a User Control named InnerTabs whenever it has DummyClass as its content.

Here is my MainWindow.xaml:

<Window x:Class="SubTabTest.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:local="clr-namespace:SubTabTest"

        Loaded="Window_Loaded"

        Title="WPF Selection Problem" Height="250" Width="350">
    <TabControl x:Name="OuterTabs">
        <TabControl.Resources>
            <DataTemplate DataType="{x:Type local:DummyClass}">
                <local:InnerTabs Margin="10" DataContext="{Binding}" />
            </DataTemplate>
        </TabControl.Resources>
    </TabControl>
</Window>


这是我的InnerTabs.xaml用户控件:


Here is my InnerTabs.xaml, the user control:

<UserControl x:Class="SubTabTest.InnerTabs"

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <TabControl>
        <TabItem Header="Inner Tab 1">
            <TabItem.Content>
                <StackPanel Orientation="Horizontal" Margin="20">
                    <TextBlock Text="SubTab of "></TextBlock>
                    <TextBlock Text="{Binding DummyMember}"></TextBlock>
                </StackPanel>
            </TabItem.Content>
        </TabItem>
        <TabItem Header="Inner Tab 2">
            <TabItem.Content>
                <StackPanel Orientation="Horizontal" Margin="20">
                    <TextBlock Text="SubTab of "></TextBlock>
                    <TextBlock Text="{Binding DummyMember}"></TextBlock>
                </StackPanel>
            </TabItem.Content>
        </TabItem>
    </TabControl>
</UserControl>


InnerTabs.xaml后面没有任何重要的代码(只是构造函数中的InitializeComponent())

最后,这里是我的MainWindow.xaml.cs代码.加载窗口后,它会动态创建2个选项卡.对于这个简单的示例,DummyClass只是在MainWindow.xaml.cs
中定义的


InnerTabs.xaml has no significant code behind (just InitializeComponent() in the constructor)

Finally here is my MainWindow.xaml.cs code behind. When the window is loaded, it dynamically creates 2 tabs. For this simple example, the DummyClass is just defined in the MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;

namespace SubTabTest
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            TabItem tiNew = new TabItem()
            {
                Header = "Outer Tab 1",
                Content = new DummyClass()
                {
                    DummyMember = 1
                }
            };

            OuterTabs.Items.Add(tiNew);

            tiNew = new TabItem()
            {
                Header = "Outer Tab 2",
                Content = new DummyClass()
                {
                    DummyMember = 2
                }
            };

            OuterTabs.Items.Add(tiNew);
        }
    }

    public class DummyClass
    {
        public int DummyMember
        {
            get;
            set;
        }
    }
}


要演示该错误,请按照下列步骤操作:

1)运行应用程序
2)从一个外部标签切换到另一个外部标签
3)注意,他们都选择了SubTab 1
4)现在将一个内部标签"的当前选择切换到第二个标签
5)现在更改外部选项卡选择
6)注意,它的当前选项卡现在也是第二个选项卡,但是当您最后离开该选项卡时,它选择了第一个选项卡.因此,当您更改第一个外部选项卡上的当前选项卡时,还具有更改第二个外部选项卡上的当前选项卡的效果!

当每个外部"选项卡都包含一个日历"控件时,也会观察到这种情况.如果更改选项卡1上的日期,它也会自动更改选项卡2上的日期!我希望两个标签都能够独立保持其UI状态,包括所有选择等.

谢谢大家对这个问题的帮助.


To demonstrate the bug, follow these steps:

1) Run the app
2) Switch from 1 outer tab to the other outer tab
3) Notice they both have SubTab 1 selected
4) Now switch the current selection of one Inner Tab to the 2nd tab
5) Now change the outer tab selection
6) Notice that its current tab is also now the 2nd tab, but when you last left this tab, it had the 1st tab selected. So when you changed the current tab on the 1st outer tab, it had the effect of ALSO changing the current tab on the 2nd outer tab!

This has also been observed when each Outer Tab contains a Calendar control. If I change the date on Tab 1, it automatically also changes the date on Tab 2! I want both tabs to be able to independently keep their UI state including all selections, etc.

Thank you everyone for your help with this issue.

我认为的问题是您的tiNew变量正在被重新分配,因此该程序两次具有相同的选项卡.您应该编写一个方法来创建一个新的TabItem并设置所有适当的属性,然后返回TabItem要添加到TabControl.Items集合中.
The problem I think is that your tiNew variable is being reallocated, so the program has the same tab twice. You sholud write a method that creates a new TabItem and sets all the appropriate properties, and returns the TabItem to be added to the TabControl.Items collection.


我已经开发了一个解决此问题的方法.

使用DataTemplate并让Binding自动实例化用户控件似乎是问题所在.

我可以通过以下方式使它起作用:
A)不要使用DataTemplate
B)将TabItem的内容设置为新的InnerTabs用户控件,并将该用户控件的DataContext直接设置为基础类.

这是修改后的MainWindow.xaml:

I have developed a workaround for this issue.

Using a DataTemplate and letting Binding automatically instantiate the User Control seems to be the problem.

I was able to get it to work by:
A) Do not use a DataTemplate
B) Set the Content of the TabItem to be a new InnerTabs usercontrol, and directly set the DataContext of that usercontrol to the underlying class.

Here is the modified MainWindow.xaml:

<Window x:Class="SubTabTest.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:local="clr-namespace:SubTabTest"

        Loaded="Window_Loaded"

        Title="WPF Selection Problem" Height="250" Width="350">
    <TabControl x:Name="OuterTabs" />
</Window>



这是动态加载选项卡的新功能:



And here is the new function that dynamically loads the tabs:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    TabItem tiNew = new TabItem()
    {
        Header = "Outer Tab 1",
        Content = new InnerTabs()
        {
            DataContext = new DummyClass()
            {
                DummyMember = 1
            }
        }
    };
    OuterTabs.Items.Add(tiNew);

    tiNew = new TabItem()
    {
        Header = "Outer Tab 2",
        Content = new InnerTabs()
        {
            DataContext = new DummyClass()
            {
                DummyMember = 2
            }
        }
    };
    OuterTabs.Items.Add(tiNew);
}