Problem:
Style gets set when the window is first loaded, then I am unable to replace it.
XAML:
Style="{Binding Path=BoolProperty, Converter={StaticResource ButtonStyleConverter}}"
VM:
Property:
public bool BoolProperty => SomeOtherObject.YetAnotherObject.OtherBoolProperty;
In the constructor:
SomeOtherObject.YetAnotherObject.PropertyChanged += (s, a)
=> PropertyChanged.Notify(this, nameof(BoolProperty));
Converter:
- Converter returns a Style object based on the BoolProperty
- Not including code because it works as expected, see Solution.
Styles:
- It has been proven that the styles are correct and are not causing the issue in this case (see Solution)
Notes:
- Notify() is just an extension method to ease use of the IPropertyChanged
- When calling the same Notify() after the root event and all the notifies, converter calls etc., the style updates correctly
I verified the following:
- When the event causing the change happens, PropertyChanged.Notify is called correctly
- Then the getter of the
BoolProperty
is called as expected - Right after that the converter is called and I verified that it returns the correct style
- When inspecting the style in Live Property Explorer it clearly looks like the 1st style is still set
What I've tried:
- Changing
.Notify(this, nameof(BoolProperty))
to.NotifyAll(this)
- Applying the second style first (to see if it's not an issue with the style itself)
- Adding
UpdateSourceTrigger=PropertyChanged}
- Replacing
Path=BoolProperty
with justBoolProperty
- Adding
ConverterParameter
attribute with the property name
Solution/Workaround:
Thanks to @EldHasp I was able to verify that it's in fact not XAMl/Converter/Style issue but it's related to how Notify() calls are made. I don't know why UI doesn't update when all the calls/threads finish, but I fixed it by replacing:
SomeOtherObject.YetAnotherObject.PropertyChanged += (s, a)
=> PropertyChanged.Notify(this, nameof(BoolProperty));
With:
this.Command_That_Also_Relies_On_OtherBoolProperty.CanExecuteChanged += (s, a)
=> PropertyChanged.Notify(this, nameof(BoolProperty));
While a hack, this workaround is acceptable in my case as I have no time to further investigate the root cause. For completion, the command looks as follows:
public ICommand SomeCommand_That_Also_Relies_On_YetAnotherObject => new RelayCommand(
() => /* some code */ ,
() => SomeOtherObject.YetAnotherObject.OtherBoolProperty);
The command also requires the following to refresh:
CommandManager.InvalidateRequerySuggested();
It looks like the issue was that the Notify() was not called from the Main Thread.
Actual solution:
Raising OnPropertyChanged when an object property is modified from another thread