Friday, April 30, 2010

Random properties in WPF UI Elements

So, in WPF, I have some tool windows showing. I want to only show one at a time. I want when I show one, to hide the others. And, when I show one, I want it to go to the last position it had. Seems simple enough. My windows are all borderless, so I have a button for dragging and I use the DragMove() method to make that all work. So, if you click the button within a certain timeframe, it toggles hide/show, otherwise it calls DragMove, which does the rest for you.

The issue is that DragMove allows you to move the window, but when you're done, it has the old Left/Top values. In other words, the values in the control are not tied to what they represent, they are free agents that sometimes get updated when the control moves, but not always. This reminds me of what I realised the 'Width' and 'Height' properties are provided for some sort of joke, and the ActualWidth and ActualHeight ones are needed ( because the Width is not the ActualWidth, right ? ). I suspect this has something to do with the half cocked move to respecting DPI settings, but I'm not sure, I just know I've never needed or found the Width or Height property to be worth anything.

So, I try to track the mouse to keep the values stored myself. Well, it turns out the the methods in WPF to get the mouse position do not work during a drag operation. So, the whole thing is useless. I needed to p/invoke GetCursorPos, then track for myself my left and top positions on the basis that WPF is incapable of doing so. It works nicely now. I should add, the Left and Top are settable, which is obvious ( so you can move a window ), but useful to me now, because I can tell it where it is, as it plainly does not know. And the fact my windows do not move at ALL when I lift my mouse is a good double check that the values I calculated are correct, unlike the ones WPF tracks.


  1. Did you try ActualTop and ActualLeft? :)

  2. Oh, duh. I did not try that, although I DID find MS documentation telling me that says the position is not properly tracked during a drag operation, so I assumed there was no way to do it, based on the docs. I even mentioned in my post that these 'Actual' properties exist, but I've never used ActualLeft and Top, only the Width and Height. I still don't know why the Width and the ActualWidth are two different things, however.

  3. As far as I can tell, the Width property may report a bogus number (like 0 or infinity) when the Width is set to "Auto" or SizeToContent includes Width; thus the need for ActualWidth.
    You may also find Window.RestoreBounds useful for checking position or size.

  4. Width = how wide you want the thing to be.
    ActualWidth = how wide the thing has actually been rendered.

    For ex., a TextBox with horizontalAlignment=stretch and Width=NaN will take all the available space. ActualWidth will tell you how much space has been assigned to it.

    Documentation is your friend.