在 TabControl 中禁用 UserControl

Disabling UserControls in a TabControl

我有一个包含 TabControlTabItem 的应用程序,它们托管自定义 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 的 属性,也不是 TabItemUserControl。所以我决定改用 IsEnabled-属性。

但是如果我禁用整个 TabControl,用户将无法再在 TabItems 之间切换来读取数据,因为如果它们被禁用则无法点击(这似乎合法,鉴于 IsEnabled-属性).

的含义

因此,为了实现我的“已禁用但仍可点击”-TabItem,我尝试在 [= 的资源中设置 StyleDataTrigger 15=],因此禁用 TabItems 内的 UserControls 而不是整个 TabControlTabItems:

<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 的情况并非如此。


现在这里的主要问题是:

为什么 StyleTargetType = 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

简短的回答是:

Styles 只能应用于具体对象

UserControl只是一个class,不是具体的对象。具体对象将是 <local:UC_1/>.

另一方面,StackPanels 当然 个具体对象,即使它们托管在可视化树的下方,在 UserControl 内,因此可以应用 Style

此外,如果 UserControl.[=48= 中的对象尚未设置 Style,则似乎只能从上到下设置 Styles ] 我尝试自上而下设置的任何 Style 都会被嵌套在可视化树中更深处的 Style 再次覆盖(最后 Style 获胜)。
这也适用于 ResourceDirectoryUserControl 中的对象设置的任何 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 必须与元素的类型完全匹配。