ContentTemplateSelector只被调用一次,始终显示相同的数据模板。

9
我制作了一个样例演示项目,使用的是VS 2010 RC,因为在我的生产项目中,我使用MVVM时遇到了相同的错误。
在我的样例演示项目中,我只使用了Code-behind,没有第三方依赖,所以你可以在这里下载演示项目并运行它:http://www.sendspace.com/file/mwx7wv 现在来说问题:当我点击女孩/男孩按钮时,它应该切换数据模板,对吧?
我做错了什么?
好的,我也提供一个代码片段: Code-Behind MainWindow.cs:
namespace ContentTemplateSelectorDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Person person;

        public MainWindow()
        {
            InitializeComponent();

            person = new Person(){ Gender = "xxx"};
            person.IsBoy = true;    

            ContentGrid.DataContext = person;
        }

        private void btnBoys_Click(object sender, RoutedEventArgs e)
        {
            person.IsBoy = true;
            person.IsGirl = false;
            this.ContentGrid.DataContext = person;
        }

        private void btnGirls_Click(object sender, RoutedEventArgs e)
        {
            person.IsGirl = true;
            person.IsBoy = false;
            this.ContentGrid.DataContext = person;    
        }        
    }
}

XAML MainWindow.xaml:

<Window x:Class="ContentTemplateSelectorDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentTemplateSelectorDemo"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>            
        <DataTemplate x:Key="girlsViewTemplate">
            <local:UserControl1 />
        </DataTemplate>

        <DataTemplate x:Key="boysViewTemplate" >
            <local:UserControl2 />
        </DataTemplate>

        <local:PersonDataTemplateSelector x:Key="PersonSelector" />            
    </Window.Resources>

    <Grid x:Name="ContentGrid" >
        <StackPanel>
            <Button Name="btnGirls" Click="btnGirls_Click">Switch Girls</Button>
            <Button Name="btnBoys" Click="btnBoys_Click">Switch Boys</Button>
        <ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ResourceKey=PersonSelector}" />
        </StackPanel>
    </Grid>
</Window>

DataTemplateSelector类:

public class PersonDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item,DependencyObject container)
    {
        if (item is Person)
        {
            Person person = item as Person;

            Window window = Application.Current.MainWindow;

            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode( window))
                return null;

            if (person.IsBoy)               
                return window.FindResource("boysViewTemplate") as DataTemplate;
            if (person.IsGirl)            
                return window.FindResource("girlsViewTemplate") as DataTemplate;

        }
        return null;
    }
}

:)


1
最好发布代码片段而不是提供下载,因为人们很可能不会下载。 - Gerrie Schenck
1
经过一些研究:正如Josh在他的代码注释中所说:“...这是必要的,因为WPF绑定系统将忽略属性返回与之前相同的对象引用的PropertyChanged通知...”我不能返回相同的对象,而是必须在按钮事件处理程序中重新创建Person对象,例如:... = new Person(){ IsBoy = true, IsGirl = false} 等等... 然后它就可以工作了。谢谢你,Josh! - msfanboy
请告诉我你的真实类中没有IsBoy和IsGirl属性 :-) - Simon_Weaver
没有啊 :P 这段代码只是一个示例...但是在我的模型中加入性别属性也没有问题。 - msfanboy
3个回答

5

我喜欢Neil的解决方案(在Josh的帖子上找到,通过你提供的链接):

<DataTemplate DataType="{x:Type local:MyType}">
    <ContentPresenter Content="{Binding}" Name="cp" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding Path=IsRunning}" Value="True">
            <Setter TargetName="cp" Property="ContentTemplate" Value="{StaticResource StopTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=IsRunning}" Value="False">
            <Setter TargetName="cp" Property="ContentTemplate" Value="{StaticResource StartTemplate}" />
        </DataTrigger>
    </DataTemplate.Triggers>
</DataTemplate>

编辑:实际上我无法让上述代码工作,但是使用样式可以使其正常运行:

<ContentControl DockPanel.Dock="Bottom" >
    <ContentControl.Style>
        <Style>      
            <Style.Triggers> 
                <DataTrigger Binding="{Binding Path=SourceSystem.SourceSystemName}" Value="mysite.com">
                    <Setter Property="ContentControl.ContentTemplate" Value="{StaticResource mysiteToolbar}" />
                </DataTrigger>

                <DataTrigger Binding="{Binding Path=SourceSystem.SourceSystemName}" Value="mysite2.com">
                    <Setter Property="ContentControl.ContentTemplate" Value="{StaticResource mysiteToolbar2}" />
                </DataTrigger>
            </Style.Triggers>            
        </Style>
    </ContentControl.Style>  
</ContentControl>

https://dev59.com/62025IYBdhLWcg3w1JiG - invalidusername

3
注意: 我认为这种方法相当笨拙,但在一些情况下可能有效。我更喜欢使用触发器的方法(来自Neil),我将其发布为一个单独的答案。


另一种可能的方法是将ContentTemplateSelectorContent绑定到确定应选择哪个模板的属性上。例如,在这里,我根据SourceSystem的值选择了两个不同的工具栏。我将Content设置为源系统属性本身。

<ContentControl ContentTemplateSelector="{StaticResource toolbarTemplateSelector}" 
                DataContext="{Binding}" Content="{Binding SourceSystem}" />

模板选择器只需查看源系统并返回所需模板。
如果模板需要访问控件的数据上下文,只需使用元素绑定设置即可。
 <UserControl.Resources>
    <DataTemplate x:Key="toolbar1">
        <views:OrdersToolbar1View Margin="0,5,0,0" 
               DataContext="{Binding ElementName=control,Path=DataContext}"/>
    </DataTemplate>
    <DataTemplate x:Key="toolbar2">
        <views:OrdersToolbar2View Margin="0,5,0,0" 
               DataContext="{Binding ElementName=control,Path=DataContext}"/>
    </DataTemplate>
 </UserControl.Resources>

-2

使用此方法进行自定义内容选择器:

private void ReloadContent()
{
    MainContentControl.ContentTemplate = MainContentControl.ContentTemplateSelector.SelectTemplate(null, MainContentControl);
}

在 XAML 中:

<ContentControl Content="{Binding}" x:Name="MainContentControl">
    <ContentControl.ContentTemplateSelector >
            <templateSelectors:MainViewContentControlTemplateSelector>
                <templateSelectors:MainViewContentControlTemplateSelector.BoysTemplate>
                    <DataTemplate>
                       <local:UserControl1 />
                    </DataTemplate>
                    </templateSelectors:MainViewContentControlTemplateSelector.BoysTemplate>
                <templateSelectors:MainViewContentControlTemplateSelector.GirlsTemplate>
                    <DataTemplate>
                        <local:UserControl2 />
                     </DataTemplate>
                     </templateSelectors:MainViewContentControlTemplateSelector.GirlsTemplate>
    </ContentControl>

选择器:

public class MainViewContentControlTemplateSelector : DataTemplateSelector
{
    public DataTemplate BoysTemplate{ get; set; }
    public DataTemplate GirlsTemplate{ get; set; }


    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var contentControl = container.GetVisualParent<ContentControl>();
        if (contentControl == null)
        {
            return BoysTemplate;
        }

        if (//Condition)
        {
            return GirlsTemplate;

        }

        return BoysTemplate;
    }

1
虽然有些正确,但它不允许您像<ContentControl Content="{Binding}" ..>那样对整个对象执行内容绑定,同时允许_模板选择器观察与内容不同的东西_,例如.IsBoy vs .IsGirl,就像问题中所述。 - quetzalcoatl

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