Datatrigger on contentpresenter.content not workin

2019-04-09 18:39发布

问题:

I am trying to switch the content of a contentpresenter based on a datatrigger.I want to display a usercontrol in the contentpresenter.content, if i have a value set or else i need to display an error message.But the binding on my datatrigger fails stating that the property is not found.I cant get the datacontext to inherit for the datatrigger checking.I can make it work by using the commented out code.But i am confused why it doesn't work the normal way.

  <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>

回答1:

If you want to use triggers to display UserControl, you should use ContentControl not ContentPresenter. I prefer to use ContentPresenter for CustomControls and When I am using the UserControl for views of Custom Data Types in my system and Allow to give dynamic behavior.

Example: To switch templates for ContentPresenter you need to set ContentTemplateSelector like this

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

MyContent, MyContentTemplate & MyContentTemplateSelector are Dependency Properties and can be binded wherever you are using its instance.

READ :

Usage of ContentPresenter

What is the difference between ContentControl and ContentPresenter

The binding mentioned in the question won't work as

ContentPresenter’s DataContext is automatically set to the value of its Content property, while ContentControl’s DataContext is not.

Bindings are resolved relatively to the value of the DataContext property. If you declare a binding on the ContentPresenter, the moment its content is set, the binding would be re-evaluated.

ContentControl.Content Property can be changed on any trigger based on your requirement. If you want to use it to change on PropertyChanged Event of a property of ViewModel, DataTrigger can be used by binding it with a DataTemplate with UserControl instance in it or using static resource of that UserControl.

<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>

READ How to use triggers for content template, more details here

Difference in DataTemplate and StaticResource scope is DataTemplate creates a new instance of the template every time its applied. Whereas, StaticResource is using the same instance of UserControl again (Static Instance). You can also use EventTriggers to change content base don Control Events like MouseOver etc.

Alternate approach
Very similar to the above with slight difference. Defining as a data template in resources. Triggering for the content change is essentially identical.

...in <x.Resources /> tag:

<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>

...then:

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


回答2:

I usually use trigger Like this...

                        <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>

If Bindings are a problem Use Snoop to Detect binding errors