dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.06k stars 1.17k forks source link

We can change the dispatcher in dependency property recursively. #174

Open lindexi opened 5 years ago

lindexi commented 5 years ago

Sometime, we may write the code cross thread to improve performance. But we are hard to share the dependency property to other thread.

As we know the DependencyObject is inherited from DispatcherObject in WPF. And the WPF use DispatcherObject.CheckAccess to check the calling thread and the creating thread. Some of the dependency property will call the DispatcherObject.DetachFromDispatcher to set their dispatcher to null to share the dependency property to other threads.

But I need to change all of the DependencyObject in a control that I want to share it with other threads. I hope we can use some method like Freeze to freeze some control to share it with other threads. Or we can set the dispatcher in dependency property recursively.

walterlv commented 5 years ago

I prefer to add some APIs to provide the ability to change the _dispatcher field to null recursively. This helps us to create visuals or controls in a background thread and use it in the main UI thread. This is a great performance improvement.

weltkante commented 5 years ago

This would be especially useful for loading images. It's really annoying that you have to load them on the UI thread.

Unfortunately, as far as I understand the implementation, images are using native COM components which are bound to a thread (apartment) and not a dispatcher, so the implementation may not be as easy as it sounds.

walterlv commented 5 years ago

Like this:

weltkante commented 5 years ago

@walterlv No, thats just generating an image from XAML, also useful but not what I meant. What I mean is loading an image from data I provide.

In WPF I can calculate the image content off-thread but it is still very costly to load a byte array of content into a WPF image instance, and you are forced to do it on the UI thread because otherwise the image will have the wrong dispatcher. WPF has no auto-throttling so if you happen to construct multiple images in the same layout pass (e.g. a list of thumbnails getting constructed through data binding) you get a huge lag spike when WPF tries to transfer image content into its internal representations.

It would be immensely useful if I could construct bitmap objects on a dedicated Dispatcher thread and later transfer them to the UI thread when they are loaded and ready to render. But that will cause problems because the underlying technology (WIC) is COM and thus bound to a threading apartment.

stevenbrix commented 5 years ago

This is a great idea @lindexi, thanks for opening it! There is a lot to dissect here, and I think we need to make sure that we have a solid understanding of the design and implications of it. Can you clarify a little more on what the exact ask is? You mentioned some API that would null out the dispatcher, do you have an idea of what this API would look like? It almost sounds like you want the ISealable interface to be exposed publically? Or do you want to be able to null any DependencyObjects dispatcher?

lindexi commented 5 years ago

At the common development, we do not need to share or new a DependencyObjects. We only need it when we new a DependencyObjects in other thread.

But I think the Dispatcher can be inherited like DataContext that we can create the object in UI thread that works as previous and we can share the DependencyObject to other thread.

The other way, we can open a static method to set the dispatcher in dependency property recursively. And the static method is in the namespace that known to few.