Giter Site home page Giter Site logo

Custom converter specified on binding is called only once at startup and not with the every OnPropertyChanged event about avalonia HOT 7 CLOSED

viktorspacil avatar viktorspacil commented on August 26, 2024 2
Custom converter specified on binding is called only once at startup and not with the every OnPropertyChanged event

from avalonia.

Comments (7)

grokys avatar grokys commented on August 26, 2024 4

I've been thinking about this, and as much as I dislike some of the patterns this is used for, Avalonia 11.0 supports it and WPF supports it so Avalonia 11.1 should also support it.

from avalonia.

grokys avatar grokys commented on August 26, 2024 1

As @stevemonaco said, this is a bit of an edge case as you're raising PropertyChanged on a property which hasn't changed and then relying on a stateful IValueConverter to mutate the state differently each time. The issue is that we now check for a changed bound value earlier than before and so the converter isn't being called.

I cross-checked with WPF and it also does call the converter even if the value hasn't changed so I'm in two minds about whether we should support this use-case. On one hand we try to match WPF's behavior where it makes sense, but on the other hand it feels like this is an anti-pattern in that it's generally bad practise to rely on side-effects in bindings. It also feels like a misuse of PropertyChanged in that if that event is raised on a property that hasn't changed, a listener should be free to ignore the event.

from avalonia.

robloo avatar robloo commented on August 26, 2024 1

@stevemonaco

ObservableCollection.Count can be binded to as it raises PropertyChanged for Count. Granted, that's a lot of notifications and converter calls since ObservableCollection doesn't well-support range operations.

lol, I've done it using the collection itself for so many years I never thought to check that. I never knew INotifyPropertyChanged fired for Count. Always learning, thanks!

I think stateful converters are bad practice, but I also think the ViewModel or some component in-between should be responsible for restricting notifications, if necessary.

I would say an IsNullOrEmptyConverter isn't stateful. It's a transform of the raw information and adds nothing to it or maintains any state itself. Transforming information is exactly what converters are for. Simply speaking we want to minimize duplicate information (properties) in the view model. I'm sure there is some information theory rationale here.


Anyway, I still think we shouldn't try to outsmart application developers. Just as in WPF, if the view model wants an update it should get an update. I'm sure I can find other cases where it's needed if I looked a while.

from avalonia.

stevemonaco avatar stevemonaco commented on August 26, 2024

You should consider writing an IMultiValueConverter and move the _toggle state into the ViewModel. IMO, converters are best left as pure functions without local state.

I can confirm the regression from 11.0.11 with the repro though (rc1 and nightly), so it should be fixed.

Also, I see a use of BindingNotification which might not work until RC2 or stable.

from avalonia.

robloo avatar robloo commented on August 26, 2024

I've run across a related issue to this that I never filed an issue for. The situation is this:

  1. Have a custom IsNullOrEmptyConverter bound to an ObservableCollection property. That converter is used to show "list empty, please add an item" type messages. So ListBlock.IsVisible="{Binding ListProperty, Converter={StaticResource IsNullOrEmptyConverter}}" in pseudocode.
  2. In the view model, manually force the converter to update visibility by calling OnPropertyChanged("ListProperty") which should force re-calculation of the converter and hide the message. In Avalonia it does not.

This is a pattern I've used since WPF as it saves one additional property on the view model that adds no new information. For now I've had to work-around this with a "IsListEmpty" property.

I really wish the binding systems were smart enough to raise property change notification on the ListProperty when it's a collection that has INotifyCollectionChanged but we aren't there yet. So I've been using this method.

Overall though, I think there always needs to be a way for apps to force re-evaluation of bindings. That's needed in edge cases we've discussed separately anyway.

from avalonia.

viktorspacil avatar viktorspacil commented on August 26, 2024

As @stevemonaco said, this is a bit of an edge case as you're raising PropertyChanged on a property which hasn't changed and then relying on a stateful IValueConverter to mutate the state differently each time. The issue is that we now check for a changed bound value earlier than before and so the converter isn't being called.

I cross-checked with WPF and it also does call the converter even if the value hasn't changed so I'm in two minds about whether we should support this use-case. On one hand we try to match WPF's behavior where it makes sense, but on the other hand it feels like this is an anti-pattern in that it's generally bad practise to rely on side-effects in bindings. It also feels like a misuse of PropertyChanged in that if that event is raised on a property that hasn't changed, a listener should be free to ignore the event.

I provided the minimal sample to simulate this behavior. In real use I need to raise the property change for the property (flags) even if its value does not change because I need to translate the flags value into another language in runtime. I do not figure out another way how to do that.

from avalonia.

stevemonaco avatar stevemonaco commented on August 26, 2024

@robloo

ObservableCollection<T>.Count can be binded to as it raises PropertyChanged for Count. Granted, that's a lot of notifications and converter calls since ObservableCollection<T> doesn't well-support range operations.

I think stateful converters are bad practice, but I also think the ViewModel or some component in-between should be responsible for restricting notifications, if necessary.

@viktorspacil

To clarify my earlier answer: Use a IMultiValueConverter and move the _toggle value to the ViewModel. You bind both TestEnum and Toggle to the converter. Then when you mutate either one from the VM, the converter will be reevaluated and the binding target updated.

from avalonia.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.