在 ViewModel 中绑定到 属性 的 DataTrigger 未在 DataGrid 的按钮中触发

DataTrigger binded to property in ViewModel is not fired in the Button of DataGrid

我创建了 ControlTemplates:

<Window.Resources>
    <ControlTemplate x:Key="imgNo" TargetType="{x:Type Control}">
        <Image Source="pack://application:,,,/Images/up.png"/>
    </ControlTemplate>

    <ControlTemplate x:Key="imgUp" TargetType="{x:Type Control}">
        <!--<TextBlock  Text="Up"/>-->
        <Image Source="pack://application:,,,/Images/up.png"/>
    </ControlTemplate>

    <ControlTemplate x:Key="imgDown" TargetType="{x:Type Control}">
        <Image Source="pack://application:,,,/Images/downArrow.png"/>
    </ControlTemplate>

    <DataTemplate x:Key="ButtonOneDataTemplate">
        <Control x:Name="theControl" Template="{DynamicResource imgNo}" />
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding IsImageChanged}" Value="true">
                <Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgUp}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding IsImageChanged}" Value="false">
                <Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgDown}" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>
</WindowResources>

DataGrid 中的 Button 上面使用了 ControlTemplates:

<DataGrid ItemsSource="{Binding Persons}" Grid.Row="1" AutoGenerateColumns="False">
   <DataGrid.Columns>
     <DataGridTextColumn Binding="{Binding IdPerson}">
       <DataGridTextColumn.HeaderTemplate>                        
          <DataTemplate>
            <Border Background="Violet">
             <StackPanel>
                 <Button ContentTemplate="{StaticResource ButtonOneDataTemplate}" 
                 Command="{Binding DataContext.HelloCommand, RelativeSource=
                  {RelativeSource AncestorType=Window}}"
                  CommandParameter="{Binding DataContext.Hello,   
                RelativeSource={RelativeSource AncestorType=DataGrid}}"/> 
             </StackPanel>
            </Border>
         </DataTemplate>
       </DataGridTextColumn.HeaderTemplate>                   
     </DataGridTextColumn>
   </DataGrid.Columns>
 </DataGrid>                    

我的ViewModel:

public class MainWindowViewModel:ViewModelBase
{
    public RelayCommand HelloCommand { get; set; }
    public MainWindowViewModel()
    {
       LoadPersons();
       HelloCommand = new RelayCommand(SayHello);
    }

    int helloCounter = 0;
    private void SayHello(object obj)
    {
       if (helloCounter % 2 == 0)
          IsImageChanged = true;
       else
          IsImageChanged = false;           
       helloCounter++;
    }


    private bool isImageChanged=true;
    public bool IsImageChanged
    {
       get { return isImageChanged; }
       set { isImageChanged = value;
             OnPropertyChanged("IsImageChanged");
           }
    }
}

我想要的是当我点击按钮<Button ContentTemplate="{StaticResource ButtonOneDataTemplate}"/>时,然后Template应该被替换为{DynamicResource imgDown}{DynamicResource imgUp}DataTrigger 取决于 IsImageChanged 值。

但是,如果我点击 Button,那么 DataTrigger 不会触发(Controltemplates 例如 imgUpimgDown 没有改变)。我怎样才能从我的 ViewModel 实现这个?

问题在于 DataGrid 列不是可视化树的一部分,因此它不继承 DataContext。为了能够在您的 ButtonOneDataTemplate 中使用 DataTriggers,您需要应用此模板的那个按钮具有正确的 DataContext。有一个技巧,如何为不在 VisualTree 中的元素提供 DataContext,如 here

所述

将该解决方案应用于您的代码,我们将得到以下结果:

代理

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

Window.Resources

    <local:BindingProxy x:Key="proxy" Data="{Binding}" />

    <DataTemplate x:Key="ButtonOneDataTemplate">
        <Control x:Name="theControl" Template="{DynamicResource imgNo}" Foreground="Orange"/>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.IsImageChanged}" Value="True">
                <Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgUp}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.IsImageChanged}" Value="False">
                <Setter TargetName="theControl" Property="Template" Value="{DynamicResource imgDown}" />
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

页眉模板

<DataGridTextColumn.HeaderTemplate>
    <DataTemplate>
        <Border Background="Violet">
            <StackPanel>
                <Button ContentTemplate="{StaticResource ButtonOneDataTemplate}" 
                        DataContext="{Binding Path=Data, Source={StaticResource proxy}}"
                        Command="{Binding DataContext.ButtonClick, RelativeSource={RelativeSource AncestorType=Window}}"
                        CommandParameter="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
            </StackPanel>
        </Border>
    </DataTemplate>
</DataGridTextColumn.HeaderTemplate>