需要与 TabItem 内容相同的用户控件的两个独立实例(部分 "WPF is disrespecting my code")

WANTED Two independent instances of same user control as TabItem content (section "WPF is disrespecting my code")

取消勾选 select 选项卡“Header B”,您会看到:取消勾选已反映出来。

如果我放置两个用户控件并以不同的方式命名它们,WPF 怎么会认为我需要一个实例?

随着时间的推移,WPF 让我抓狂。

MainWindow.xaml:

<!-- The silver price is currently high? I don't care. -->
<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:UserControls="clr-namespace:WpfApplication2"
        mc:Ignorable="d"
        Background="Silver" Title="MainWindow" FontSize="14" WindowStartupLocation="CenterScreen" 
        Height="300" Width="700" FocusManager.FocusedElement="{Binding ElementName=_btCancel}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TabControl x:Name="_tabControl" Grid.Row="0" >
            <TabControl.ItemContainerStyle>
                <Style TargetType="TabItem">
                    <Setter Property="Header" Value="{Binding Header}" />
                    <Setter Property="Content" Value="{Binding Content}" />
                </Style>
            </TabControl.ItemContainerStyle>
            <TabItem x:Name="_tabItem_A" Header="Header A">
                <TabItem.Content>
                    <Grid>
                        <UserControls:UserControl_1 x:Name="_userControl_A" />
                    </Grid>
                </TabItem.Content>
            </TabItem>
            <TabItem x:Name="_tabItem_B" Header="Header B">
                <TabItem.Content>
                    <Grid>
                        <UserControls:UserControl_1 x:Name="_userControl_B" />
                    </Grid>
                </TabItem.Content>
            </TabItem>
        </TabControl>
        <Button Name="_btCancel" Grid.Row="1" Content="   Cancel   " Margin="0,10,0,10" 
                VerticalAlignment="Bottom" HorizontalAlignment="Center" IsCancel="True" Click="_btCancel_Click" />
    </Grid>
</Window>

MainWindow.xaml.cs:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;

namespace WpfApplication2
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            List<_ListViewItem> listViewItems = new List<_ListViewItem>();
            listViewItems.Add(new _ListViewItem() { _IsActive = false, _Text = "Text 1" });
            listViewItems.Add(new _ListViewItem() { _IsActive = true, _Text = "Text 2" });
            listViewItems.Add(new _ListViewItem() { _IsActive = false, _Text = "Text 3" });

            Application.Current.Resources.Add("_xamlReference_listViewContent", listViewItems);

            InitializeComponent();
        }

        private void _btCancel_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }
    }

    public class _ListViewItem
    {
        public bool _IsActive { get; set; }
        public string _Text { get; set; }
    }
}

UserControl_1.xaml:

<UserControl x:Class="WpfApplication2.UserControl_1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <UserControl.Resources>
        <CollectionViewSource x:Key="_collectionViewSource" Source="{Binding Source={StaticResource _xamlReference_listViewContent}}" />
    </UserControl.Resources>
    <Grid x:Name="_grid">
        <ListView x:Name="_lv" Margin="10" ItemsSource="{Binding Source={StaticResource _collectionViewSource}, Mode=OneWay}">
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="Margin" Value="0,5,0,0" />
                    <Setter Property="HorizontalContentAlignment" Value="Center" />
                </Style>
            </ListView.ItemContainerStyle>
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="70">
                        <GridViewColumn.Header>
                            <Label Content="Active" />
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <CheckBox IsChecked="{Binding Path=_IsActive}" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Width="130">
                        <GridViewColumn.Header>
                            <Label Content="Interval" />
                        </GridViewColumn.Header>
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <TextBox Text="{Binding Path=_Text}" HorizontalContentAlignment="Center" Width="80" />
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

UserControl_1.xaml.cs:

using System.Windows.Controls;

namespace WpfApplication2
{
    public partial class UserControl_1 : UserControl
    {
        public UserControl_1()
        {
            InitializeComponent();
        }
    }
}

App.xaml:

<Application x:Class="WpfApplication2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApplication2"
             xmlns:UserControls="clr-namespace:WpfApplication2"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <!-- These brute force statements do not help: -->
        <Style TargetType="{x:Type UserControls:UserControl_1}" x:Shared="False" />
        <Style TargetType="{x:Type ListView}" x:Shared="False" />
    </Application.Resources>
</Application>

使用 MVVM 模式。有两个 view-models(每个选项卡一个)共享与检查复选框相关的状态并将 TabItem DataContext 绑定到这些 view-models.

一般来说,您应该尽可能避免使用 WPF 魔法,并使用 bindings/view-models 来解决您的问题。

这里的问题是 UserControls 都使用相同的 List<_ListViewItem> 作为他们在这段代码中的资源:

<UserControl.Resources>
    <CollectionViewSource x:Key="_collectionViewSource" Source="{Binding Source={StaticResource _xamlReference_listViewContent}}" />
</UserControl.Resources>

当您执行此操作时,两个控件上的 Checkbox 将绑定到同一对象的相同 _IsActive 属性,因此更改一个将更改另一个。解决此问题的一种方法(请参阅评论)是将复选框上的绑定设置为 OneWay,如下所示:

<CheckBox IsChecked="{Binding Path=_IsActive, Mode=OneWay}" />