在 TabControl 中禁用 UserControl
Disabling UserControls in a TabControl
我有一个包含 TabControl
和 TabItem
的应用程序,它们托管自定义 UserControl
,如下所示:
<TabControl>
<TabItem Header="UC_1 and UC_2">
<StackPanel>
<local:UC_1/>
<Separator/>
<local:UC_2/>
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3/>
</TabItem>
</TabControl>
UserControl
有很多不同的控件,像这样:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBox Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
现在我想将整个 TabControl
设置为 ReadOnly
,具体取决于模型中的一个 属性,这样用户仍然可以阅读但不能编辑控件的内容.
不幸的是,IsReadOnly
不是 TabControl
的 属性,也不是 TabItem
或 UserControl
。所以我决定改用 IsEnabled
-属性。
但是如果我禁用整个 TabControl
,用户将无法再在 TabItems
之间切换来读取数据,因为如果它们被禁用则无法点击(这似乎合法,鉴于 IsEnabled
-属性).
的含义
因此,为了实现我的“已禁用但仍可点击”-TabItem
,我尝试在 [= 的资源中设置 Style
和 DataTrigger
15=],因此禁用 TabItem
s 内的 UserControl
s 而不是整个 TabControl
或 TabItem
s:
<TabControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Model.TCEnabledProperty}" Value="9">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
但这不起作用。
所以我改变了
<Style TargetType="{x:Type UserControl}">
到
<Style TargetType="{x:Type StackPanel}">
突然我得到了预期的行为但是(当然)只有如果给定的UserControl
有一个StackPanel
托管所有其他控件,所有这些 UserControl
的情况并非如此。
现在这里的主要问题是:
为什么 Style
对 TargetType = StackPanel
起作用但对 TargetType = UserControl
不起作用?我不明白我可以访问控件 inside UserControl
但不能访问 UserControl
本身。
如果没有其他选择,我将在 UserControl
中添加一个 StackPanel
包装,但出于好奇,我想了解整个我在这里遇到的行为...
添加 IsReadOnly
依赖项 属性 到 UserControl
:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBlock x:Name="textBlock" Text="{Binding Model.Firstname}" />
<TextBox x:Name="textBox" Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
和代码(可以使用绑定和转换器来完成,x:Name
-way 只是更快捷的演示方式):
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false, (d, e) =>
{
var userControl = (MyUserControl)s;
var value = (bool)e.NewValue;
userControl.textBlock.Visible = value ? Visibility.Visible : Visibility.Hidden;
userControl.textBox.Visible = !value ? Visibility.Visible : Visibility.Hidden;
}));
那你应该可以在标签页中设置:
<TabItem Header="MyUserControl">
<local:MyUserControl IsReadOnly="{Binding SomeBinding}"/>
</TabItem>
Edit:
Liero provided an even better solution to the problem. I'm still leaving my answer up, as I was confusing base classes with concrete objects by the time I was wondering why I was unable to apply a Style
to TargetType=UserControl
in my View and the text below might help others to understand the difference as well.
Sinatrs answer lead me to another post here on Whosebug, where I found the solution to the question评论中的讨论。
为什么 setter 不适用于 TargetType=UserControl
?
简短的回答是:
Style
s 只能应用于具体对象。
UserControl
只是一个class,不是具体的对象。具体对象将是 <local:UC_1/>
.
另一方面,StackPanels
当然 是 个具体对象,即使它们托管在可视化树的下方,在 UserControl
内,因此可以应用 Style
。
此外,如果 UserControl
.[=48= 中的对象尚未设置 Style
,则似乎只能从上到下设置 Style
s ]
我尝试自上而下设置的任何 Style
都会被嵌套在可视化树中更深处的 Style
再次覆盖(最后 Style
获胜)。
这也适用于 ResourceDirectory
为 UserControl
中的对象设置的任何 Style
。
为什么不单独绑定每个 TabItem 的内容 IsEnabled 属性?
<TabItem Header="UC_1 and UC_2">
<StackPanel IsEnabled="{Binding ...}"/> <!-- use converter if you can't create property called 'IsTCEnabled' in viewmodel -->
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
您还可以创建样式以避免重复绑定:
<Style x:Key="TabContent" TargetType="FrameworkElement">
<Setter Property="IsEnabled" Value="{Binding }" />
</Style>
<TabItem Header="UC_1 and UC_2">
<StackPanel Style="{StaticResource TabContent}" />
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
编辑: 回答你的问题
Why is the Style working for the TargetType = StackPanel but not for
the TargetType = UserControl?
很简单。 StackPanel 样式有效,因为您已将 StackPanel 添加到第一个选项卡。 UserControl 不起作用,因为您已将 UC_1 和 UC_2 添加到第二个选项卡。隐式样式不适用于继承的控件。 TargetType 必须与元素的类型完全匹配。
我有一个包含 TabControl
和 TabItem
的应用程序,它们托管自定义 UserControl
,如下所示:
<TabControl>
<TabItem Header="UC_1 and UC_2">
<StackPanel>
<local:UC_1/>
<Separator/>
<local:UC_2/>
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3/>
</TabItem>
</TabControl>
UserControl
有很多不同的控件,像这样:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBox Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
现在我想将整个 TabControl
设置为 ReadOnly
,具体取决于模型中的一个 属性,这样用户仍然可以阅读但不能编辑控件的内容.
不幸的是,IsReadOnly
不是 TabControl
的 属性,也不是 TabItem
或 UserControl
。所以我决定改用 IsEnabled
-属性。
但是如果我禁用整个 TabControl
,用户将无法再在 TabItems
之间切换来读取数据,因为如果它们被禁用则无法点击(这似乎合法,鉴于 IsEnabled
-属性).
因此,为了实现我的“已禁用但仍可点击”-TabItem
,我尝试在 [= 的资源中设置 Style
和 DataTrigger
15=],因此禁用 TabItem
s 内的 UserControl
s 而不是整个 TabControl
或 TabItem
s:
<TabControl.Resources>
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Model.TCEnabledProperty}" Value="9">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TabControl.Resources>
但这不起作用。
所以我改变了
<Style TargetType="{x:Type UserControl}">
到
<Style TargetType="{x:Type StackPanel}">
突然我得到了预期的行为但是(当然)只有如果给定的UserControl
有一个StackPanel
托管所有其他控件,所有这些 UserControl
的情况并非如此。
现在这里的主要问题是:
为什么 Style
对 TargetType = StackPanel
起作用但对 TargetType = UserControl
不起作用?我不明白我可以访问控件 inside UserControl
但不能访问 UserControl
本身。
如果没有其他选择,我将在 UserControl
中添加一个 StackPanel
包装,但出于好奇,我想了解整个我在这里遇到的行为...
添加 IsReadOnly
依赖项 属性 到 UserControl
:
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="Surname"/>
<Border>
<TextBox Text="{Binding Model.Surname}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Firstname" />
<Border>
<TextBlock x:Name="textBlock" Text="{Binding Model.Firstname}" />
<TextBox x:Name="textBox" Text="{Binding Model.Firstname}" />
</Border>
</DockPanel>
</StackPanel>
</ScrollViewer>
和代码(可以使用绑定和转换器来完成,x:Name
-way 只是更快捷的演示方式):
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(MyUserControl), new PropertyMetadata(false, (d, e) =>
{
var userControl = (MyUserControl)s;
var value = (bool)e.NewValue;
userControl.textBlock.Visible = value ? Visibility.Visible : Visibility.Hidden;
userControl.textBox.Visible = !value ? Visibility.Visible : Visibility.Hidden;
}));
那你应该可以在标签页中设置:
<TabItem Header="MyUserControl">
<local:MyUserControl IsReadOnly="{Binding SomeBinding}"/>
</TabItem>
Edit:
Liero provided an even better solution to the problem. I'm still leaving my answer up, as I was confusing base classes with concrete objects by the time I was wondering why I was unable to apply aStyle
toTargetType=UserControl
in my View and the text below might help others to understand the difference as well.
Sinatrs answer lead me to another post here on Whosebug, where I found the solution to the question评论中的讨论。
为什么 setter 不适用于 TargetType=UserControl
?
简短的回答是:
Style
s 只能应用于具体对象。
UserControl
只是一个class,不是具体的对象。具体对象将是 <local:UC_1/>
.
另一方面,StackPanels
当然 是 个具体对象,即使它们托管在可视化树的下方,在 UserControl
内,因此可以应用 Style
。
此外,如果 UserControl
.[=48= 中的对象尚未设置 Style
,则似乎只能从上到下设置 Style
s ]
我尝试自上而下设置的任何 Style
都会被嵌套在可视化树中更深处的 Style
再次覆盖(最后 Style
获胜)。
这也适用于 ResourceDirectory
为 UserControl
中的对象设置的任何 Style
。
为什么不单独绑定每个 TabItem 的内容 IsEnabled 属性?
<TabItem Header="UC_1 and UC_2">
<StackPanel IsEnabled="{Binding ...}"/> <!-- use converter if you can't create property called 'IsTCEnabled' in viewmodel -->
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 IsEnabled="{Binding ...}"/>
</TabItem>
您还可以创建样式以避免重复绑定:
<Style x:Key="TabContent" TargetType="FrameworkElement">
<Setter Property="IsEnabled" Value="{Binding }" />
</Style>
<TabItem Header="UC_1 and UC_2">
<StackPanel Style="{StaticResource TabContent}" />
<local:UC_1 />
<Separator />
<local:UC_2 />
</StackPanel>
</TabItem>
<TabItem Header="UC_3">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
<TabItem Header="UC_4">
<local:UC_3 Style="{StaticResource TabContent}" />
</TabItem>
编辑: 回答你的问题
Why is the Style working for the TargetType = StackPanel but not for the TargetType = UserControl?
很简单。 StackPanel 样式有效,因为您已将 StackPanel 添加到第一个选项卡。 UserControl 不起作用,因为您已将 UC_1 和 UC_2 添加到第二个选项卡。隐式样式不适用于继承的控件。 TargetType 必须与元素的类型完全匹配。