内容控件(ContentControl)的内容模板选择器(ContentTemplateSelector)动态选择模板

8
我在窗口右侧设置了一个ContentControl,并将Content绑定到Items(类型为ObservableCollection)。现在我想实现这样的效果:如果没有项目,ContentControl会选择第一个DataTemplate,并添加一个项目到items,ContentControl将选择第二个DataTemplate来显示一些信息。
就像这样:
图片
问题是当我向items中添加一个项目时,ContentControl没有更新并更改DataTemplate。我尝试设置模式、UpdateSourceTrigger等,但失败了。在ViewModel中,删除一个项目后,我使用这些语句,它能很好地工作<1>:
private void ExecuteDeleteClientCommand()
{
    ...
    if (DeleteClient(item))
    {
        ObservableCollection<MyViewModel> tmp = TabItems;
        TabItems = null;
        TabItems = tmp;
    }
}

.

<ContentControl 
    ContentTemplateSelector="{StaticResource MyDataTemplateSelector}" 
    Content="{Binding Items}"/>

.

public class SingleClientDataTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, 
        DependencyObject container)
    {
        ObservableCollection<MyViewModel> obj = 
            item as ObservableCollection<MyViewModel>;
        if (null == obj || 0 == obj.Count)
        {
            return App.Current.FindResource("NullItemDataTemplate") as DataTemplate;
        }
        return App.Current.FindResource("DefaultDataTemplate") as DataTemplate;
    }
}

编辑:在删除一个项目后,使用这种方式也失败了:


RaisePropertyChanging(ItemsPropertyName);
RaisePropertyChanged(ItemsPropertyName);

但我想知道为什么它在<1>中表现良好。

编辑2 这是声明:

public const string ItemsPropertyName = "Items";
private ObservableCollection<MyViewModel> items = new ObservableCollection<MyViewModel>();
public ObservableCollection<SingleClientDetailViewModel> TabItems
{
    get { return items; }
    set 
    { 
        if (items == value) { return;}
        RaisePropertyChanging(ItemsPropertyName);
    items = value;
    RaisePropertyChanged(ItemsPropertyName);
    }
}

你在你的视图模型中实现了“INotifyPropertyChanged”接口并为“Items”属性实现了吗? - Sheridan
1
我使用mvvmlight。类MyViewModel继承自ViewModelBase。ViewModelBase:ObservableObject:INotifyPropertyChanged。 - SubmarineX
1个回答

15

ContentControl只会监听PropertyChanged事件,而不是CollectionChanged事件。如果你需要这种行为,你需要使用ItemsControl或它的其他版本之一,例如ListView

作为解决方法,你可以为ContentControl创建一个Style,并在DataTrigger上定义Items.Count来设置ContentTemplate,而不是使用TemplateSelector。类似于以下内容:

        <ContentControl Content="{Binding Items}">
        <ContentControl.Style>
            <Style TargetType="ContentControl">
                <Setter Property="ContentTemplate" Value="{StaticResource DefaultDataTemplate}" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=Items}" Value="{x:Null}">
                        <Setter Property="ContentTemplate" Value="{StaticResource NullItemDataTemplate}" />
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=Items.Count}" Value="0">
                        <Setter Property="ContentTemplate" Value="{StaticResource NullItemDataTemplate}" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ContentControl.Style>
    </ContentControl>

谢谢,这种方式很好用。但是我仍然不知道为什么在手动调用RaisePropertyChanged(ItemsPropertyName)的情况下它不起作用:现在Items.Count为1,我删除了唯一的一个项目并调用了RaisePropertyChanged(ItemsPropertyName)。 - SubmarineX
你确定 ItemsPropertyName 的值是字符串值 Items 而不是 null 吗?由于你没有提供该属性的声明,因此无法进一步评论。 - Mat J
@SubmarineX 请检查首字母的大小写。 - Mat J

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