更改datacontext属性时更改contentcontrol的数据模板

本文关键字:数据 contentcontrol datacontext 属性 更改 | 更新日期: 2023-09-27 18:28:57

我有一个绑定到对象的contentcontrol(示例中为DataContext的data属性)。每当data引用的对象发生更改时,我都希望重新选择datatemplate。我该怎么做?

<ContentControl Name="rootData" Content="{Binding data}" 
            ContentTemplateSelector="{StaticResource myTemplateSelector}"/>

更改datacontext属性时更改contentcontrol的数据模板

如果您正在更改整个数据类型,那么您的视图应该如下所示,唯一的区别是删除键并使用DataType作为DataTemplate,这被称为隐式数据模板

<Window x:Class="TextBindingFormatting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBindingFormatting"
    xmlns:viewModels="clr-namespace:TextBindingFormatting.ViewModels"
    Title="MainWindow" Height="350" Width="555">
<Window.Resources>
    <local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
    <DataTemplate DataType="{x:Type viewModels:Student}">
        <StackPanel Orientation="Horizontal">
            <Label>Id</Label>
            <TextBlock Text="{Binding Id}"></TextBlock>
            <Label>Name</Label>
            <TextBlock Text="{Binding Name}"></TextBlock>
        </StackPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:Parent}">
        <StackPanel Orientation="Vertical">
            <Label>Name</Label>
            <TextBlock Text="{Binding Name}"></TextBlock>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <StackPanel>
    <ContentControl Content="{Binding Data}" >
    </ContentControl>
        <Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
    </StackPanel>
</Grid>

背后的代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel() {Data = new Student{Id = 1, Name = "Student"}};
    }
    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = this.DataContext as MainWindowViewModel;
        vm.Data = new Parent() {Name = "This is parent"};
    }
}

我有两个等级如下

public class Student : INotifyPropertyChanged
{
    private string _name;
    private int _id;
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    public string Name {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
    public int Id
    {
        get { return _id; }
        set
        {
            _id = value;
            OnPropertyChanged("Id");
        }
    }
}

还有另一个

public class Parent : INotifyPropertyChanged 
{
    private string _name;
    public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}

如果您有相同的数据类型和不同的data值,那么您可以使用DataTemplate Selector,如下所示。否则,只需使用DataTemplate的DataType属性,您甚至不需要数据模板选择器。以下是每次更改数据时选择模板的示例代码。

主窗口.xaml

<Window x:Class="TextBindingFormatting.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TextBindingFormatting"
    Title="MainWindow" Height="350" Width="555">
<Window.Resources>
    <local:MyTemplateSelector x:Key="MyTemplateSelector"></local:MyTemplateSelector>
    <DataTemplate x:Key="Template1">
        <StackPanel Orientation="Horizontal">
            <Label>Label 1</Label>
            <Label>Label 2</Label>
        </StackPanel>
    </DataTemplate>
    <DataTemplate x:Key="Template2">
        <StackPanel Orientation="Vertical">
            <Label>Label 1</Label>
            <Label>Label 2</Label>
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<Grid>
    <StackPanel>
    <ContentControl Content="{Binding Data}" ContentTemplateSelector="{StaticResource MyTemplateSelector}"></ContentControl>
        <Button Content="Change DataTemplate" Click="ButtonBase_OnClick"></Button>
    </StackPanel>
</Grid>

下面是代码隐藏,理想情况下应该使用命令来处理按钮单击,但为了快速示例,我在代码隐藏中实现了只是为了触发数据的更改。

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel() {Data = "1"};
    }
    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        var vm = this.DataContext as MainWindowViewModel;
        vm.Data = "2";
    }
}

以下是主窗口的ViewModel

namespace TextBindingFormatting.ViewModels
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        private string _data;
        public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    public string Data
    {
        get { return _data; }
        set
        {
            _data = value;
            OnPropertyChanged("Data");
        }
    }
}

}

数据模板选择器

namespace TextBindingFormatting
{
    public class MyTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            var element = container as FrameworkElement;
            if (element == null || item == null)
                return base.SelectTemplate(item, container);
        if (item.ToString() == "1")
            return element.FindResource("Template1") as DataTemplate;
        if (item.ToString() == "2")
            return element.FindResource("Template2") as DataTemplate;
        return base.SelectTemplate(item, container);
    }
    }
}

从Rachel那里找到了一个可能的解决方案:动态更改数据模板

DataTemplateSelector不会在PropertyChange上触发,但设置触发器对我来说很有用!