WinUI中如何根据Type枚举属性切换ContentPresenter的DataTemplate?
WinUI中根据枚举属性切换Shimmer控件的DataTemplate
问题背景
定义了ShimmerType枚举对应7种Shimmer样式,给Shimmer控件添加了Type依赖属性,在XAML中编写了对应枚举值的DataTemplate,但尝试用代码切换模板未生效,需要实现根据Type值自动切换ContentPresenter的ContentTemplate。
现有核心代码片段
枚举定义
public enum ShimmerType { CirclePersona, SquarePersona, Profile, Article, Video, Feed, Shopping, }
控件依赖属性
public ShimmerType Type { get { return (ShimmerType)GetValue(TypeProperty); } set { SetValue(TypeProperty, value); } } public static readonly DependencyProperty TypeProperty = DependencyProperty.Register("Type", typeof(ShimmerType), typeof(SfShimmer), new PropertyMetadata(ShimmerType.CirclePersona, OnTypePropertyChangedMethod));
XAML中的DataTemplate
<DataTemplate x:Key="ShimmerFeedTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerCirclePersonaTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerArticleStyle"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerProfileTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerShoppingTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerVideoTemplate"> <!-- 模板内容 --> </DataTemplate>
控件样式
<Style x:Key="ShimmerStyle" TargetType="local:SfShimmer"> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Fill" Value="{ThemeResource ShimmerFillColor}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:SfShimmer"> <StackPanel> <ContentControl x:Name="Grid1"> <ContentPresenter x:Name="presenters" ContentTemplate="{StaticResource ShimmerCirclePersonaTemplate}" /> </ContentControl> <Grid x:Name="Grid2"> <ContentControl> <ContentPresenter Content="{TemplateBinding Content}"/> </ContentControl> </Grid> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
现有代码的问题
- 模板Key不匹配:代码中查找的
ShimmerFeedControlTemplate和XAML定义的ShimmerFeedTemplate名字不一致; - 资源查找范围错误:仅从控件自身
Resources查找,可能找不到定义在控件样式资源字典里的模板; - 时机问题:
OnTypePropertyChanged执行时,控件模板可能还未应用,导致Presenter为null; - 枚举值覆盖不全:缺少
SquarePersona和Article的模板切换逻辑。
正确实现方案
1. 统一DataTemplate命名
把XAML中的ShimmerArticleStyle改为ShimmerArticleTemplate,新增ShimmerSquarePersonaTemplate,确保枚举值和模板Key遵循Shimmer{枚举值}Template的规则:
<DataTemplate x:Key="ShimmerFeedTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerCirclePersonaTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerArticleTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerProfileTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerShoppingTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerVideoTemplate"> <!-- 模板内容 --> </DataTemplate> <DataTemplate x:Key="ShimmerSquarePersonaTemplate"> <!-- 模板内容 --> </DataTemplate>
2. 修正控件C#代码
封装模板更新方法,确保在模板应用和属性变更时都能正确切换模板:
public class SfShimmer : Control { public SfShimmer() { DefaultStyleKey = typeof(SfShimmer); } private ContentPresenter _presenter; public ShimmerType Type { get { return (ShimmerType)GetValue(TypeProperty); } set { SetValue(TypeProperty, value); } } public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(nameof(Type), typeof(ShimmerType), typeof(SfShimmer), new PropertyMetadata(ShimmerType.CirclePersona, OnTypePropertyChanged)); protected override void OnApplyTemplate() { base.OnApplyTemplate(); // 获取模板中的ContentPresenter _presenter = GetTemplateChild("presenters") as ContentPresenter; // 模板应用后立即更新对应模板 UpdateShimmerTemplate(); } private static void OnTypePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is SfShimmer shimmer) { shimmer.UpdateShimmerTemplate(); } } private void UpdateShimmerTemplate() { if (_presenter == null) return; DataTemplate template = null; // 根据枚举值自动拼接模板Key string templateKey = $"Shimmer{Type}Template"; // 优先从控件资源查找,找不到再从应用全局资源查找 if (Resources.ContainsKey(templateKey)) { template = Resources[templateKey] as DataTemplate; } else if (Application.Current.Resources.ContainsKey(templateKey)) { template = Application.Current.Resources[templateKey] as DataTemplate; } // 兜底逻辑:找不到对应模板时用默认的CirclePersona模板 if (template == null) { template = Resources["ShimmerCirclePersonaTemplate"] as DataTemplate ?? Application.Current.Resources["ShimmerCirclePersonaTemplate"] as DataTemplate; } _presenter.ContentTemplate = template; } }
3. 调整控件样式
去掉ContentPresenter的硬编码默认模板,让代码逻辑控制模板切换:
<Style x:Key="ShimmerStyle" TargetType="local:SfShimmer"> <Setter Property="HorizontalAlignment" Value="Center"/> <Setter Property="VerticalAlignment" Value="Center"/> <Setter Property="Fill" Value="{ThemeResource ShimmerFillColor}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="local:SfShimmer"> <StackPanel> <ContentControl x:Name="Grid1"> <ContentPresenter x:Name="presenters" /> </ContentControl> <Grid x:Name="Grid2"> <ContentControl> <ContentPresenter Content="{TemplateBinding Content}"/> </ContentControl> </Grid> </StackPanel> </ControlTemplate> </Setter.Value> </Setter> </Style>
4. MainWindow用法保持不变
<shim:Shimmer x:Name="shimmer" Type="Profile" />
内容的提问来源于stack exchange,提问作者Budding bro
相关产品推荐
相关产品推荐

