Learning WPF with BabySmash - Making a Transparent Window with Substance
NOTE: If you haven't read the first post in this series, I would encourage you do to that first, or check out the BabySmash category. Also check out http://windowsclient.net/ for more developer info on WPF.
BACKGROUND: This is one of a series of posts on learning WPF. I wrote an application for my 2 year old using WPF, but as I'm a Win32-minded programmer, my working app is full of Win32-isms. It's not a good example of a WPF application even though it uses the technology. I'm calling on community (that's you, Dear Reader) to blog about your solutions to different (horrible) selections of my code. You can get the code http://www.codeplex.com/babysmash. Post your solutions on your blog, in the comments, or in the Feedback and we'll all learn WPF together. Pick a single line, a section, or subsystem or the whole app!
Someone wanted the BabySmash Window to have the option to be transparent. I figured, hey, WPF must be good at that, transparency at all. I bet it'll be like one line of code.
I went into my MainWindow.xaml and added these attributes:
Background="Transparent" AllowsTransparency="True" WindowStyle="None"
Bam! Right? Well, kind of. This made the Window Transparent, but "hollow" in that I couldn't click on it. It was totally invisble, except for my little bit of text at the top. I could "click through" the application to the desktop below.
Well, here's why, and if I'd read 4 chapters into Chris Sell's book, I'd have figured this out. ;)
Per Dwayne Need on the WPF Team:
"Windows treats fully transparent layered-windows windows as hollow."
This was interesting because everywhere else in WPF if you have a Brush that is null, that's "hollow" and Transparent is solid. Again, Dwayne:
"This is because a layered window is represented to the OS as a bitmap, so all it can do is look at the pixel values. It [Windows] cannot differentiate between null and Transparent."
Ah, makes sense. This is where WPF meets the rest of Windows. Ok, but I have complete control over my Brushes and their colors. I can make a Brush that is not just #000000 (Black) but also #01000000 (really really transparent black. Like 1/255th transparent). You've got not just RGB, you've got an Alpha Channel PLUS RGB.
Now if I set my Window's background to this Brush:
I get the look of transparency, except the Window is still there and I can click on it.
"Your almost-transparent brush fails the Windows transparency test, so windows delivers events to it. If something is 100% transparent then hit testing is bypassed. If you want transparency and hit testing, define a color with minimal alpha just as you have done.
This was an interesting edge class where WPF's definition of Transparent didn't quite line up with Window's definition.
Once you've created a Window that is transparent like my funky full-screen one,,,
MainWindow m = new MainWindow(this)
WindowStartupLocation = WindowStartupLocation.Manual,
Left = s.WorkingArea.Left,
Top = s.WorkingArea.Top,
Width = s.WorkingArea.Width,
Height = s.WorkingArea.Height,
WindowStyle = WindowStyle.None,
Topmost = true,
AllowsTransparency = Settings.Default.TransparentBackground,
Background = (Settings.Default.TransparentBackground ? new SolidColorBrush(Color.FromArgb(1,0,0,0)) : Brushes.WhiteSmoke)
...you can't change the Transparency after you've shown it...or else...
Chris Sells suggested I set my Window to AllowsTransparency = True all the time, then make a Canvas over the top of it that had an opaque Brush with color and transparency I could change as I liked as it'd be "inside" WPF world and not have to deal with any underlying limitations in Win32.
For now, my not-quite-transparent brush got me the feature I wanted - BabySmash over an existing desktop.