2015/07/28 - WPF annoyances, Part 1
Created: / Modified:
So, UIconEdit uses the Windows Presentation Foundation. WPF, like many other things created by Microsoft, is absolutely beautiful and wonderful in a great many respects, except when you run into one of a zillion or so edge-cases, at which point it comes to a screeching brain-numbingly annoying halt.
The extract-window, at design time.
Here's what I want to do: I want UIconEdit to have the ability to extract icons from DLL and EXE files. Storing icons in DLLs and EXEs is an ancient, hallowed tradition, and there are numerous ways to access them. The above window will be used to select which icon you want to extract, and shows all of them in a neat little grid with the index-number and the number of images in that icon (I already have that part complete).
Now, the process of loading every icon is not going to be instantaneous — shell32.dll, which has been in every modern version of Windows, contains over 325 icons as of Windows 10 — especially if I decide to use UIconEdit's own icon-loading system instead of being stuck with dinky 32x32 icons loaded using the ExtractIcon function. So, I want to use a progress-bar, and make it possible to cancel the loading of the icons partway through.
This is going to have a lot of technical programming stuff.
(For the solution, skip to the end.)
Attempt #1: The Straightforward Method
Simple enough! In the WindowLoaded event, I'll just have it load each icon, and increment the value of the progress bar for each one.
Result: The window is blank and empty until all the icons are done loading. This seems to be because doing everything in the Window.Loaded event stops it from loading or displaying anything.
Attempt #2: Threading
Equally simple: I put the job of loading the image into a separate thread. This won't block up everything in the Window.Loaded event, because the Window.Loaded event isn't doing anything, the loading is happening elsewhere!
Result: A crash in the second thread the instant it tries to increment the progress bar's value. See, in WPF, pretty much everything is what's known as a dependency object, which gets its name from the fact that its values are called dependency properties, which have a neat feature where you can bind them to other values (this will become relevant in the next attempt). You can say, "Hey, progress-bar, I want your Value dependency-property to always have the same value as this other object's Value property!" And within reason, this will work just fine.
The problem is that dependency objects are really possessive of their dependency properties, and they do not like it when you try to access their values in another thread. You can't even read the value of a dependency property in the wrong thread! I assume that part of this is so that they don't have to try to get bindings to behave sensibly given all the potential problems with threading,1
and partly because the underlying graphical systems aren't built to use multiple threads; you can't display an image (or anything else, really) in one thread if it was loaded in a different thread (EDIT: Whoops, this is wrong! See next post). The bottom line is that when you try to set the value of the progress-bar, it does not work.
Attempt #3: INotifyPropertyChanged
But there is a workaround! The reason you can bind dependency properties to each other is that dependency objects are highly communicative; when you change the value of a dependency property, it will announce the change to any other dependancy properties which are bound to it, and provide a lot of detailed information about it. This doesn't work for most other objects, because they don't have any way of announcing the change in their values ... unless the object implements the INotifyPropertyChanged interface, which announces the name of the property to everything which is prepared to listen. The best part of it for my purposes is, if you bind a dependency property to an INotifyPropertyChanged property, the dependency property will change along with the other value even if the other value was changed in a different thread.
So inside the window-object, I create a second object which implements INotifyPropertyChanged with a "ProgressBarValue" property and bind the progress-bar's value to that. It loads all the icons in its own separate thread, sets the progress bar value in each thread, and sends over all the fully-loaded icons.
Result: It crashes when I try to send over all the fully-loaded icons. Because they were all loaded in, once again, a different thread. D'oh.
Attempt #4: Getting A Handle On Things
I mentioned the ExtractIcon function before. What this function actually does is, it returns a handle, which WPF can then load as a WPF-image. So how about I just load the handle in the extra thread, and load the WPF-image for each icon after I've loaded all the handles?
Result: The progress-bar zooms along lickety split as it loads the handles, and then the window freezes for longer than it took to load the handles as they are converted into WPF images.
To be continued tomorrow, when I'm more awake.
1Have a problem? Use threading! o different problems! Now you have tw[sic]
Previous: 2015/07/09 - Getting this off my chest
Next: 2015/07/30 - WPF annoyances, Part 2: Solution Here
Back to Blog-Like Typing Detected
Back to Main Page
No comments on this article.