如何解决使用转换器应用并绑定到其他对象的属性的样式在运行时不会被覆盖 问题:XAML:虚拟机:转换器:样式:注意事项:我尝试过的:
问题:
第一次加载窗口时设置样式,然后我无法替换它。
XAML:
Style="{Binding Path=BoolProperty,Converter={StaticResource ButtonStyleConverter}}"
虚拟机:
属性:
public bool BoolProperty => SomeOtherObject.YetAnotherObject.OtherBoolProperty;
在构造函数中:
SomeOtherObject.YetAnotherObject.PropertyChanged += (s,a)
=> PropertyChanged.Notify(this,nameof(BoolProperty));
转换器:
样式:
- 经证明,样式是正确的,不会导致这种情况下的问题(请参阅解决方案)
注意事项:
我验证了以下内容:
- 当引起改变的事件发生时,PropertyChanged.Notify 被正确调用
- 然后按预期调用
BoolProperty
的 getter - 在调用转换器之后,我确认它返回了正确的样式
- 在 Live Property Explorer 中检查样式时,很明显第一个样式仍然设置
我尝试过的:
- 将
.Notify(this,nameof(BoolProperty))
更改为.NotifyAll(this)
- 先应用第二种样式(看看是不是样式本身的问题)
- 添加
UpdateSourceTrigger=PropertyChanged}
- 仅用
Path=BoolProperty
替换BoolProperty
- 使用属性名称添加
ConverterParameter
属性
解决方案/变通方法:
感谢@EldHasp,我能够验证它实际上不是 XAMl/Converter/Style 问题,而是与 Notify() 调用的方式有关。 我不知道为什么在所有调用/线程完成后 UI 没有更新,但我通过替换来修复它:
SomeOtherObject.YetAnotherObject.PropertyChanged += (s,nameof(BoolProperty));
与:
this.Command_That_Also_Relies_On_OtherBoolProperty.CanExecuteChanged += (s,a)
=> PropertyChanged.Notify(this,nameof(BoolProperty));
虽然是 hack,但在我的情况下,这种解决方法是可以接受的,因为我没有时间进一步调查根本原因。 为了完成,命令如下所示:
public ICommand SomeCommand_That_Also_Relies_On_YetAnotherObject => new RelayCommand(
() => /* some code */,() => SomeOtherObject.YetAnotherObject.OtherBoolProperty);
该命令还需要以下内容才能刷新:
CommandManager.InvalidateRequerySuggested();
看起来问题是 Notify() 不是从主线程调用的。
实际解决方案:
Raising OnPropertyChanged when an object property is modified from another thread
解决方法
问题显然不在您展示的代码中。 这是最简单的例子,效果很好。
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,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>
异步对属性、通知和转换器的行为绝对没有影响。
这是异步更改属性的示例。
使用了主题中 INotifyPropertyChanged
的基本实现:BaseInpc。
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,Mode=OneWay}"/>
</Grid>
</Window>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。