内容呈现器中的内容数据触发器无法正常工作。

5

我正在尝试根据数据触发器切换内容呈现器的内容。如果我有一个已设置的值,我想在contentpresenter.content中显示一个用户控件,否则我需要显示一个错误消息。但是我的数据触发器绑定失败,指出找不到属性。我无法让数据上下文继承进行数据触发器检查。使用注释代码可以使其正常工作。但我很困惑为什么它不能按照正常方式工作。

  <ContentPresenter.Style>
            <Style TargetType="{x:Type ContentPresenter}">
                  <Setter Property="Content" Value="{Binding UC}"/>
                <Style.Triggers>
                    <!--<DataTrigger Binding="{Binding DataContext.HasValue,RelativeSource={RelativeSource AncestorType={x:Type ContentPresenter}}}" Value="false">
                        <Setter Property="Content" Value="No preview"/>
                    </DataTrigger>-->
                    <DataTrigger Binding="{Binding HasValue}" Value="false">
                        <Setter Property="Content" Value="No value"/>
                    </DataTrigger> 

                </Style.Triggers> 

            </Style>
        </ContentPresenter.Style>
    </ContentPresenter>
2个回答

8

如果您想要使用触发器来显示UserControl,应该使用ContentControl而不是ContentPresenter。

我更喜欢在自定义控件中使用ContentPresenter,在我的系统中使用UserControl作为自定义数据类型的视图,并允许提供动态行为。

示例:要切换ContentPresenter模板,您需要像这样设置ContentTemplateSelector

<ContentPresenter Content="{Binding MyContent}"
                          ContentTemplate="{Binding MyContentTemplate}"
                          ContentTemplateSelector="{Binding MyContentTemplateSelector}"/>

MyContent、MyContentTemplate和MyContentTemplateSelector是依赖属性,可以在使用其实例的任何地方进行绑定。

阅读:

ContentPresenter的用法

ContentControl和ContentPresenter之间的区别

问题中提到的绑定不起作用,因为

ContentPresenter的DataContext自动设置为其Content属性的值,而ContentControl的DataContext则没有。

绑定是相对于DataContext属性的值解析的。如果在ContentPresenter上声明了一个绑定,那么一旦其内容被设置,绑定就会被重新评估。

ContentControl.Content属性可以根据您的要求在任何触发器上更改。如果您想在ViewModel的属性的PropertyChanged事件上使用它进行更改,则可以通过将其与包含UserControl实例的DataTemplate绑定或使用该UserControl的静态资源来使用DataTrigger。

<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Value="{StaticResource UnSelectedDataTemplate}" Property="ContentTemplate" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
                    <Setter Value="{StaticResource SelectedDataTemplate}" Property="ContentTemplate" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentContro.Style>
</ContentControl>

阅读 如何使用触发器来控制内容模板,更多细节请看这里

DataTemplate和StaticResource作用域的区别在于每次应用DataTemplate时都会创建一个新的模板实例。而StaticResource则是再次使用UserControl的同一实例(静态实例)。您还可以使用EventTriggers根据控件事件(如MouseOver等)更改内容。

替代方法
与上述方法非常相似,只有轻微的差异。将其定义为资源中的数据模板。触发内容更改的方法基本相同。

...在<x.Resources />标签中:

<DataTemplate x:Key="DesignerTemplate" DataType="{x:Type vm:SolutionViewModel}">
    <vw:SolutionDesignerView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SolutionViewModel}">
    <ContentControl Content="{Binding }">
        <ContentControl.Style>
            <Style TargetType="{x:Type ContentControl}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsLoaded}" Value="True">
                        <Setter Property="ContentTemplate" Value="{StaticResource DesignerTemplate}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
    </ContentControl>
</DataTemplate>

...然后:

<ContentControl Content="{Binding Solution}" />  

我已经想出了编码的解决方案。你在这里提供了一些很好的信息。我可以将示例粘贴到你的答案中,然后你可以进行编辑和修改。这将授予你奖励,并可能得到一些赞同。 - IAbstract
当然,你可以添加它。我也在添加一些信息。感谢赏金。 - Akanksha Gaur
赏金奖励窗口将在24小时后开启。 - IAbstract
@IAbstract 没问题 :) - Akanksha Gaur
1
直接回答OP的问题 - {Binding HasValue} 触发器不起作用,因为 ContentPresenter.DataContext 被设置为 ContentPresenter.Content 的值。所以一旦内容被设置为 ViewModel.UC 值,它会尝试查找 UC.HasValue 而不是 ViewModel.HasValue,并且可以理解地失败。出于同样的原因,相对源方法有效。这些信息可以在您提供的第一个链接下找到,但我认为在您的答案中明确指出这一点会很有用。 - Grx70

-2

我通常使用触发器,就像这样...

                        <UserControl>
                            <UserControl.Resources>
                                <DataTemplate x:Key="normalTemplate" >
                                     <!-Fav UserControl->
                                </DataTemplate >
                                <DataTemplate x:Key="overWriteTempalte">
                                    <!-Fav UserControl->                                    </DataTemplate>
                            </UserControl.Resources>
                            <ContentPresenter x:Name="ContentField"
                                              Content="{Binding}"
                                              ContentTemplate="{StaticResource ResourceKey=normalTemplate}" />
                            <UserControl.Triggers>
                                <DataTrigger Binding="{Binding Path=MyProperty}" Value="True">
                                    <Setter TargetName="ContentField" Property="ContentTemplate" Value="{StaticResource ResourceKey=overWriteTempalte}" />
                                </DataTrigger>
                            </UserControl.Triggers>
                        </UserControl>

如果绑定是一个问题,可以使用Snoop来检测绑定错误。

这是错误的。UserControl上的触发器必须是EventTrigger类型。 - Joe
@IAbstract 绑定用户控件不能通过提供类型来完成... 需要为用户控件实例声明静态资源,然后将其绑定作为内容即可。由于此用户控件作为内容放在基于视图模型绑定的内容呈现器中显示,因此将使用 Data Trigger... 当您希望基于用户控件属性显示时,将使用事件触发器。 - Akanksha Gaur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接