31

我有带有 DataTemplateSelector 的 ContentPresenter:

    ...

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        var model = item as ItemControlViewModel;

        if (model.CurrentStatus == PrerequisitesStatus.Required)
        {
            return RequiredTemplate;
        }

        if (model.CurrentStatus == PrerequisitesStatus.Completed)
        {
            return FinishedTemplate;
        }

        ...

        return InProgressTemplate;
    }

当 CurrentStatus 更改时,将调用 OnPropertyChanged。

当属性更改并更改 ContentPresenter DataTemplate 时,我需要以某种方式触发此 DataTemplateSelector。有什么建议么?

Threre 有类似的问题: 1 2,但我不想使用任何 DataTriggers,因为状态太多。

尝试使用 DataTriggers

    <ContentPresenter
        Grid.Column="1"
        Height="16"
        Width="16"
        Margin="3">
        <ContentPresenter.Triggers>
            <DataTrigger Binding="{Binding Path=CurrentStatus}" Value="0">
                <Setter Property="ContentPresenter.ContentTemplate" Value="{StaticResource ResourceKey=_requiredStatusTemplate}" />
            </DataTrigger>
        </ContentPresenter.Triggers>
    </ContentPresenter>

但出现错误:触发器集合成员必须是 EventTrigger 类型 :(

4

4 回答 4

37

当您在评论中请求一个带有数据触发器的示例时,您在这里:

FrameworkElement 只能有 EventTrigger,因此您会收到错误消息 触发器集合成员必须是 EventTrigger 类型

而且也不要直接使用 ContentPresenter,它应该在 ControlTemplate 中使用。当您想要拥有动态内容时,最好使用 ContentControl。请参阅ContentControl 和 ContentPresenter 有什么区别?

最后,这是对您的 DataTrigger 问题的建议。我已将其放入可重用的样式中....

XAML:

<Window x:Class="WpfApplication88.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>

    <DataTemplate x:Key="requiredTemplate">
      <TextBlock Text="requiredTemplate"></TextBlock>
      <!--your stuff here-->
    </DataTemplate>

    <DataTemplate x:Key="completedTemplate">
      <TextBlock Text="CompletedTemplate"></TextBlock>
      <!--your stuff here-->
    </DataTemplate>

    <Style x:Key="selectableContentStyle" TargetType="{x:Type ContentControl}">
      <Style.Triggers>
        <DataTrigger Binding="{Binding Path=CurrentStatus}" Value="Required">
          <Setter Property="ContentTemplate" Value="{StaticResource requiredTemplate}" />
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=CurrentStatus}" Value="Completed">
          <Setter Property="ContentTemplate" Value="{StaticResource completedTemplate}" />
        </DataTrigger>
        <!--  your other Status' here -->
      </Style.Triggers>
    </Style>

  </Window.Resources>

  <Grid>
    <ContentControl Width="100" Height="100" Style="{StaticResource selectableContentStyle}"/>
  </Grid>

</Window>
于 2012-01-04T16:00:02.287 回答
6

我可能是错的,但我相信只有在为添加到集合中的项目创建容器DataTemplateSelector时才使用。ItemContainerGenerator因为当属性值更改时不会生成新容器,所以DataTemplate永远不会通过选择器应用新容器。

正如评论中所建议的那样,我建议您查看VisualStateManageror 数据触发器,否则当一个或多个属性更改值时,您将不得不为每个项目重新创建容器。

于 2012-01-03T18:50:22.167 回答
2

作为一个额外的选择——如果你想坚持你的模板,只需使用 s 与转换器的绑定。

于 2012-01-03T20:15:29.183 回答
2

我想出了一个理论上可以做到这一点的行为。

C#:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;

public class UpdateTemplateBehavior : Behavior<ContentPresenter>
{
    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(nameof(Content), typeof(object), typeof(UpdateTemplateBehavior), new FrameworkPropertyMetadata(null, OnContentChanged));
    public object Content
    {
        get => GetValue(ContentProperty);
        set => SetValue(ContentProperty, value);
    }
    static void OnContentChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is UpdateTemplateBehavior behavior)
            behavior.Update();
    }

    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(nameof(Value), typeof(object), typeof(UpdateTemplateBehavior), new FrameworkPropertyMetadata(null, OnValueChanged));
    public object Value
    {
        get => GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }
    static void OnValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender is UpdateTemplateBehavior behavior)
            behavior.Update();
    }

    public UpdateTemplateBehavior() : base() { }

    protected override void OnAttached()
    {
        base.OnAttached(); 
        Update();
    }

    void Update()
    {
        if (Content != null)
        {
            BindingOperations.ClearBinding(AssociatedObject, ContentPresenter.ContentProperty);
            AssociatedObject.Content = null;

            BindingOperations.SetBinding(AssociatedObject, ContentPresenter.ContentProperty, new Binding() { Path = nameof(Content), Source = this });
        }
    }
}

XAML:

<ContentPresenter ContentTemplateSelector="{StaticResource MySelector}">
    <i:Interaction.Behaviors>
        <Behavior:UpdateTemplateBehavior Content="{Binding SomeContent}"
            Value="{Binding SomeValue}"/>
    </i:Interaction.Behaviors>
</ContentPresenter>

当内容(在本例中为“SomeContent”)和任意值(在本例中为“SomeValue”)以及行为发生变化时,内容被“更新”(通过清除然后重置绑定)先附上。

除非内容不为空(我的项目特定要求),否则不会进行更新。附加时不更新可能会避免无意中一次更新两次,但如果值最初是null,则在值至少更改一次之前不会发生更新。

注意:在上面的示例中,我不确定行为是否与ContentPresenter. 为简洁起见,我使用了一个未在此处包含的帮助程序类。测试时请记住这一点...

于 2022-01-14T02:36:20.100 回答