需要与 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}" />
取消勾选 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}" />