C# 与转换器一起应用并绑定到其他对象的属性的样式在运行时不会被覆盖 问题:

C# 与转换器一起应用并绑定到其他对象的属性的样式在运行时不会被覆盖 问题:,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,样式在第一次加载窗口时设置,然后我无法替换它 XAML: 虚拟机: 财产: public bool BoolProperty => SomeOtherObject.YetAnotherObject.OtherBoolProperty; 在构造函数中: SomeOtherObject.YetAnotherObject.PropertyChanged += (s, a) => PropertyChanged.Notify(this, nameof

样式在第一次加载窗口时设置,然后我无法替换它

XAML: 虚拟机: 财产:

public bool BoolProperty => SomeOtherObject.YetAnotherObject.OtherBoolProperty;
在构造函数中:

SomeOtherObject.YetAnotherObject.PropertyChanged += (s, a)
                    => PropertyChanged.Notify(this, nameof(BoolProperty));
转换器: 转换器基于BoolProperty返回样式对象 不包括代码,因为它按预期工作,请参阅解决方案。 风格: 事实证明,这些样式是正确的,不会导致问题。在这种情况下,请参阅解决方案 笔记: Notify只是一种扩展方法,可以简化IPropertyChanged的使用 在根事件和所有通知、转换器调用等之后调用相同的通知时,样式将正确更新 我核实了以下情况:

发生导致更改的事件时,将正确调用PropertyChanged.Notify 然后按预期调用BoolProperty的getter 在调用转换器之后,我验证了它返回的样式是否正确 在Live Property Explorer中检查样式时,很明显,第一个样式仍处于设置状态 我所尝试的: 将.Notifythis、nameofBoolProperty更改为.NotifyAllthis 首先应用第二种样式,看看它是否与样式本身无关 添加UpdateSourceTrigger=PropertyChanged} 将Path=BoolProperty替换为just BoolProperty 添加属性名为的ConverterParameter属性 解决方案/解决方案: 多亏了@EldHasp,我才能够验证这实际上不是XAMl/Converter/Style问题,而是与通知调用的方式有关。 我不知道为什么当所有调用/线程完成时UI不更新,但我通过替换以下内容来修复它:

SomeOtherObject.YetAnotherObject.PropertyChanged += (s, a)
                    => PropertyChanged.Notify(this, nameof(BoolProperty));
与:

虽然是黑客,但在我的情况下,这种变通方法是可以接受的,因为我没有时间进一步调查根本原因。 完成时,命令如下所示:

public ICommand SomeCommand_That_Also_Relies_On_YetAnotherObject => new RelayCommand(
            () =>  /* some code */ ,
            () => SomeOtherObject.YetAnotherObject.OtherBoolProperty);
该命令还需要以下内容才能刷新:

CommandManager.InvalidateRequerySuggested();
看起来问题在于没有从主线程调用Notify

实际解决方案:
问题显然不在您显示的代码中。 这里有一个最简单的例子,效果很好

using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace StyleBindingConverter
{
    public class BooleanToStyleConverter : IValueConverter
    {
        public Style TrueStyle { get; set; }
        public Style FalseStyle { get; set; }

        private static readonly BooleanConverter boolConverter = new BooleanConverter();
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is bool boolean))
            {
                string str = value?.ToString();
                if (string.IsNullOrWhiteSpace(str) ||
                    !boolConverter.IsValid(str))
                    return DependencyProperty.UnsetValue;

                boolean = (bool)boolConverter.ConvertFromString(value.ToString());
            }


            return boolean
                ? TrueStyle
                : FalseStyle;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

根据您的描述,问题很可能在于设置绑定的XAML中。转换器只在一个地方使用吗?@EldHasp我在两个不同的地方使用它。它第一次应用了基于转换器的样式,但是当它应该更改转换器返回不同的样式时,更改没有反映在UI中。仅供参考,您是对的-我无法使用您的代码重现问题,并且经过进一步调查,似乎PropertyChanged触发得太早,似乎没有。如果我在代码中的其他地方调用它,它会正确地更改样式。我会自己继续调查,谢谢。典型的调用是PropertyChanged?.Invokethis,new PropertyChangedEventArgspropertyName;。但我不认为这是问题所在,因为您编写的转换器是执行的。我希望您这样检查:转换上的断点。。。方法,每次BoolProperty更改时,都会发生一个停止,然后一步一步地执行F10,直到该方法退出。我想我发现了问题-看起来是因为启动通知链的事件位于不同的线程上。请参阅更新的问题。当订阅命令的CanExecuteChanged时,我能够修复该问题,并确认它从主线程而不是从其他线程运行。对于PropertyChanged事件,在哪个线程上触发它并不重要。但是CollectionChanged和CanExecuteChanged事件是线程敏感的。如果它们不是在UI线程中创建的,它们可能会消失,甚至引发异常并使应用程序崩溃。其他一些详细信息:
CommandManager.InvalidateRequerySuggested();
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace StyleBindingConverter
{
    public class BooleanToStyleConverter : IValueConverter
    {
        public Style TrueStyle { get; set; }
        public Style FalseStyle { get; set; }

        private static readonly BooleanConverter boolConverter = new BooleanConverter();
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is bool boolean))
            {
                string str = value?.ToString();
                if (string.IsNullOrWhiteSpace(str) ||
                    !boolConverter.IsValid(str))
                    return DependencyProperty.UnsetValue;

                boolean = (bool)boolConverter.ConvertFromString(value.ToString());
            }


            return boolean
                ? TrueStyle
                : FalseStyle;

        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
namespace StyleBindingConverter
{
    public class BooleanViewModel
    {
        public bool BoolProperty { get; set; }
    }
}
<Window x:Class="StyleBindingConverter.TestStyleConverterWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StyleBindingConverter"
        mc:Ignorable="d"
        Title="TestStyleConverterWindow" Height="450" Width="400">
    <Window.Resources>
        <Style x:Key="Button.Style.True" TargetType="Button">
            <Setter Property="Background" Value="Green"/>
        </Style>
        <Style x:Key="Button.Style.False" TargetType="Button">
            <Setter Property="Background" Value="LightCoral"/>
        </Style>
        <local:BooleanToStyleConverter x:Key="BooleanToStyleConverter"
                                       FalseStyle="{StaticResource Button.Style.False}"
                                       TrueStyle="{StaticResource Button.Style.True}"/>
    </Window.Resources>
    <Window.DataContext>
        <local:BooleanViewModel/>
    </Window.DataContext>
    <UniformGrid Columns="1">
        <Button Content="Test Button" HorizontalAlignment="Center" VerticalAlignment="Center"
                Padding="15 5"
                Style="{Binding BoolProperty, Converter={StaticResource BooleanToStyleConverter}, Mode=OneWay}"/>
        <CheckBox Content="Style Change" HorizontalAlignment="Center" VerticalAlignment="Center"
                IsChecked="{Binding BoolProperty, Mode=TwoWay}"/>
    </UniformGrid>
</Window>
using Simplified;
using System.Timers;

namespace StyleBindingConverter
{
    public class BooleanViewModelAsync : BaseInpc
    {
        private bool _boolProperty;

        public bool BoolProperty { get => _boolProperty; private set => Set(ref _boolProperty, value); }

        private readonly Timer timer = new Timer() { Interval = 1000};

        public BooleanViewModelAsync()
        {
            timer.Elapsed += (s, e) => BoolProperty = !BoolProperty;
            timer.Start();
        }
    }

}
<Window x:Class="StyleBindingConverter.TestStyleConverterAsyncWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:StyleBindingConverter"
        mc:Ignorable="d"
        Title="TestStyleConverterAsyncWindow"
        Height="450" Width="400">
    <Window.Resources>
        <Style x:Key="Button.Style.True" TargetType="Button">
            <Setter Property="Background" Value="Green"/>
        </Style>
        <Style x:Key="Button.Style.False" TargetType="Button">
            <Setter Property="Background" Value="LightCoral"/>
        </Style>
        <local:BooleanToStyleConverter x:Key="BooleanToStyleConverter"
                                       FalseStyle="{StaticResource Button.Style.False}"
                                       TrueStyle="{StaticResource Button.Style.True}"/>
    </Window.Resources>
    <Window.DataContext>
        <local:BooleanViewModelAsync/>
    </Window.DataContext>
    <Grid>
        <Button Content="Test Button" HorizontalAlignment="Center" VerticalAlignment="Center"
                Padding="15 5"
                Style="{Binding BoolProperty, Converter={StaticResource BooleanToStyleConverter}, Mode=OneWay}"/>
    </Grid>
</Window>