Delay's Blog is the blog of David Anson, a Microsoft developer who works with the Silverlight, WPF, Windows Phone, and web platforms.
http://dlaa.me/
@DavidAns
In the introductory post for LayoutTransformControl and the feature-enhancing follow-up, I gave a variety of examples to convey the need for LayoutTransform. I also noted that Silverlight 2 supports only RenderTransform, but went on to demonstrate how it was possible to use RenderTransform to get LayoutTransform behavior. I'm biased, but I happen think LayoutTransform is a pretty fundamental part of application design - and the feedback I've gotten about LayoutTransformControl suggests that at least some of you agree with me. :)
Soon after the Silverlight 2 release candidate became available, a customer contacted me to ask about updating LayoutTransformControl. I've spent some time doing just that and am happy to share a version that works well on the latest Silverlight 2 bits. The complete implementation - along with demo and test applications - can be found in LayoutTransformControl.cs in the ZIP file attached to this post.
LayoutTransformControl.cs
The sample application works the same as it did before (with the updated look-and-feel of the new bits):
It remains my belief that LayoutTransformControl for Silverlight 2 is just as powerful as WPF's LayoutTransform. Your application can take advantage of complete LayoutTransform functionality today - even though the Silverlight platform doesn't support LayoutTransform!
Notes:
LayoutTransformControl.TransformUpdated()
UIElement.UseLayoutRounding
True
.Child
My thanks to everyone who has expressed interest in LayoutTransformControl or is actively using it in their projects! I hope this update continues to serve you well as Silverlight 2 approaches final release.
Last night the Silverlight team released Silverlight 2 Release Candidate 0! The new bits include a minor change with a big impact on SilverlightDefaultStyleBrowser (background reading available here, here, here, and here): the path for default styles has changed from generic.xaml to themes/generic.xaml. So SilverlightDefaultStyleBrowser ends up looking in the wrong place for the RC assemblies and doesn't find any styles. That's not very helpful, so I've just updated SilverlightDefaultStyleBrowser to extract styles from both of these paths (which will continue to work with Beta bits and will also work with the new RC bits).
generic.xaml
themes/generic.xaml
The version number of SilverlightDefaultStyleBrowser appears in the window's title and the latest release number is 1.0.3191.14705. (Note: I haven't updated the screen shot below which shows the introductory version number.) If installed via ClickOnce, the application should automatically prompt you to upgrade when it detects the update (which typically happens after running the app once or twice). If you're using the standalone EXE, you'll need to update manually.
Click here or on the image above to install SilverlightDefaultStyleBrowser via ClickOnce with automatic updating.
Click here to download the standalone SilverlightDefaultStyleBrowser executable and source code in a ZIP file.
The RC bits of Silverlight introduce some neat new controls: ComboBox, PasswordBox, and ProgressBar. Go play with them now to see how they work - then use the new SilverlightDefaultStyleBrowser to see how they're styled!
I first came across the notion of automatic mouse button clicking some time ago. The basic idea can be summarized as follows:
The nearly universal pattern of mouse use is: move/click/wait... move/click/wait... move/click/wait.... In the overwhelming majority of cases, the only reason the mouse gets moved is to position the pointer over the next user interface element that needs to be clicked. Because every move is immediately followed by a click, it should be possible to simplify the process by performing the click automatically when the mouse stops moving (i.e., moves to a new location and stays still for a few moments). This automatic click saves the user only a tiny bit of effort each time it happens, but it eliminates a conceptually unnecessary, repetitive motion that's carried out many, many times over the course of every day. As an additional ergonomic benefit, automatic clicking enables the user to hold the mouse in a variety of new ways now that it's no longer necessary to keep a finger on the mouse button.
My first reaction was that the automatic clicking behavior would be nearly impossible to live with - and for the first couple of minutes I tried using it, it was. :) But, once I got out of the habit of wiggling the mouse needlessly and got into the habit of moving it where I needed when I needed, I found I quite liked the behavior after all. As I became more comfortable, I got used to the timing and found I could manage double clicks with just a single click by timing that click to happen right after the automatic click! The only challenge was to learn what parts of the user interface do nothing when clicked on - because these are the "safe areas" where the mouse can be "parked" if you start moving it and then decide you don't really want to click anything after all.
I found I liked automatic clicking so much, I wrote my own tool to implement it - and I've used that tool happily for nearly a decade. But my tool has a specific hardware dependency as a consequence of some other functionality it implements and that became a problem a few days ago when I changed hardware. So I decided this was a great opportunity to extract the clicking functionality into its own, dedicated, hardware-agnostic tool - and then blog about it!
The new tool I wrote is called MouseButtonClicker and does exactly what I describe above. It's written completely in native code - partly because I saw no need for the overhead of managed code for a simple "always-on" scenario like this one - and partly because it was a good excuse to brush up on my (rusty) native coding skills. MouseButtonClicker is a UI-less application - which means it doesn't have a window, or a notification icon, or anything to look at - it just runs invisibly and does its job. Of course, it wouldn't be hard to add a simple notification icon, but that's unnecessary for my purposes: MouseButtonClicker auto-starts with my computer and never gets closed. If you find you need to exit MouseButtonClicker, simply kill it with Task Manager or via TaskKill /IM MouseButtonClicker.exe - MouseButtonClicker maintains no persistent state and terminating it is completely safe to do at any time. And while the standard 32-bit executable works just fine on 64-bit operating systems, I've also provided a 64-bit version for those who prefer not to mix their bits. :)
TaskKill /IM MouseButtonClicker.exe
Click here to download the MouseButtonClicker executables (32- and 64-bit) and the complete Visual Studio 2008 source code.
Implementation notes:
COMPILE_TIME_ASSERT
So if you're in the mood to try something new and you don't mind a bit of a learning curve, give MouseButtonClicker a try for an hour or two. If you're lucky, you may never need to click again... :)
PS - For the benefit of casual readers and search engines, here is MouseButtonClicker.cpp:
// MouseButtonClicker - http://blogs.msdn.com/Delay // // "MouseButtonClicker clicks the mouse so you don't have to!" // // Simple Windows utility to click the primary mouse button whenever the mouse // stops moving as a usability convenience/enhancement. #include <windows.h> #include <tchar.h> #include <malloc.h> #include <strsafe.h> // Macro for compile-time assert #define COMPILE_TIME_ASSERT(e) typedef int CTA[(e) ? 1 : -1] // Constants for code simplification #define RI_ALL_MOUSE_BUTTONS_DOWN (RI_MOUSE_BUTTON_1_DOWN | RI_MOUSE_BUTTON_2_DOWN | RI_MOUSE_BUTTON_3_DOWN | RI_MOUSE_BUTTON_4_DOWN | RI_MOUSE_BUTTON_5_DOWN) #define RI_ALL_MOUSE_BUTTONS_UP (RI_MOUSE_BUTTON_1_UP | RI_MOUSE_BUTTON_2_UP | RI_MOUSE_BUTTON_3_UP | RI_MOUSE_BUTTON_4_UP | RI_MOUSE_BUTTON_5_UP) // Check that bit-level assumptions are correct COMPILE_TIME_ASSERT(RI_MOUSE_BUTTON_1_DOWN == (RI_MOUSE_BUTTON_1_UP >> 1)); COMPILE_TIME_ASSERT(RI_MOUSE_BUTTON_2_DOWN == (RI_MOUSE_BUTTON_2_UP >> 1)); COMPILE_TIME_ASSERT(RI_MOUSE_BUTTON_3_DOWN == (RI_MOUSE_BUTTON_3_UP >> 1)); COMPILE_TIME_ASSERT(RI_MOUSE_BUTTON_4_DOWN == (RI_MOUSE_BUTTON_4_UP >> 1)); COMPILE_TIME_ASSERT(RI_MOUSE_BUTTON_5_DOWN == (RI_MOUSE_BUTTON_5_UP >> 1)); COMPILE_TIME_ASSERT(RI_ALL_MOUSE_BUTTONS_DOWN == (RI_ALL_MOUSE_BUTTONS_UP >> 1)); // Macro for absolute value #define ABS(v) ((0 <= v) ? v : -v) // Application-level constants #define APPLICATION_NAME (TEXT("MouseButtonClicker")) #define TIMER_EVENT_ID (1) #define MOUSE_MOVE_THRESHOLD (2) // Window procedure LRESULT CALLBACK WndProc(const HWND hWnd, const UINT message, const WPARAM wParam, const LPARAM lParam) { // Tracks the mouse move delta threshold across calls to WndProc static LONG lLastClickDeltaX = 0; static LONG lLastClickDeltaY = 0; static bool fOkToClick = false; switch (message) { // Raw input message case WM_INPUT: { // Query for required buffer size UINT cbSize = 0; if(0 == GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, NULL, &cbSize, sizeof(RAWINPUTHEADER))) { // Allocate buffer on stack (falls back to heap) const LPVOID pData = _malloca(cbSize); if(NULL != pData) { // Get raw input data if(cbSize == GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, pData, &cbSize, sizeof(RAWINPUTHEADER))) { // Only interested in mouse input const RAWINPUT* const pRawInput = static_cast<LPRAWINPUT>(pData); if (RIM_TYPEMOUSE == pRawInput->header.dwType) { // Only interested in devices that use relative coordinates // Specifically, input from pens/tablets is ignored if(0 == (pRawInput->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)) { // Tracks the state of the mouse buttons across calls to WndProc static UINT usMouseButtonsDown = 0; // Update mouse delta variables lLastClickDeltaX += pRawInput->data.mouse.lLastX; lLastClickDeltaY += pRawInput->data.mouse.lLastY; // Enable clicking once the mouse has exceeded the threshold in any direction fOkToClick |= ((MOUSE_MOVE_THRESHOLD < ABS(lLastClickDeltaX)) || (MOUSE_MOVE_THRESHOLD < ABS(lLastClickDeltaY))); // Determine the input type const UINT usButtonFlags = pRawInput->data.mouse.usButtonFlags; if(0 == (usButtonFlags & (RI_ALL_MOUSE_BUTTONS_DOWN | RI_ALL_MOUSE_BUTTONS_UP | RI_MOUSE_WHEEL))) { // Mouse move: (Re)set click timer if no buttons down and mouse moved enough to avoid jitter if((0 == usMouseButtonsDown) && fOkToClick) { // Use double-click time as an indication of the user's responsiveness preference (void)SetTimer(hWnd, TIMER_EVENT_ID, GetDoubleClickTime(), NULL); } } else { // Mouse button down/up or wheel rotation: Cancel click timer (void)KillTimer(hWnd, TIMER_EVENT_ID); // Update mouse button state variable (asserts above ensure the bit manipulations are correct) usMouseButtonsDown |= (usButtonFlags & RI_ALL_MOUSE_BUTTONS_DOWN); usMouseButtonsDown &= ~((usButtonFlags & RI_ALL_MOUSE_BUTTONS_UP) >> 1); } } } } // Free buffer (void)_freea(pData); } } } break; // Timer message case WM_TIMER: { // Timeout, stop timer and click primary button (void)KillTimer(hWnd, TIMER_EVENT_ID); INPUT pInputs[2] = {0}; pInputs[0].type = INPUT_MOUSE; pInputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN; pInputs[1].type = INPUT_MOUSE; pInputs[1].mi.dwFlags = MOUSEEVENTF_LEFTUP; (void)SendInput(2, pInputs, sizeof(INPUT)); // Reset mouse delta and threshold variables lLastClickDeltaX = 0; lLastClickDeltaY = 0; fOkToClick = false; } break; // Close message case WM_DESTROY: (void)PostQuitMessage(0); break; // Unhandled message default: return DefWindowProc(hWnd, message, wParam, lParam); } // Return value 0 indicates message was processed return 0; } // WinMain entry point int APIENTRY _tWinMain(const HINSTANCE hInstance, const HINSTANCE hPrevInstance, const LPTSTR lpCmdLine, const int nCmdShow) { // Avoid compiler warnings for unreferenced parameters UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // Create a mutex to prevent running multiple simultaneous instances const HANDLE mutex = CreateMutex(NULL, FALSE, APPLICATION_NAME); if((NULL != mutex) && (ERROR_ALREADY_EXISTS != GetLastError())) { // Register the window class WNDCLASS wc = {0}; wc.lpfnWndProc = WndProc; wc.hInstance = hInstance; wc.lpszClassName = APPLICATION_NAME; if(0 != RegisterClass(&wc)) { // Create a message-only window to receive WM_INPUT and WM_TIMER const HWND hWnd = CreateWindow(APPLICATION_NAME, APPLICATION_NAME, 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, hInstance, NULL); if (NULL != hWnd) { // Register for the mouse's raw input data RAWINPUTDEVICE rid = {0}; rid.usUsagePage = 1; // HID_DEVICE_SYSTEM_MOUSE rid.usUsage = 2; // HID_DEVICE_SYSTEM_MOUSE rid.dwFlags = RIDEV_INPUTSINK; rid.hwndTarget = hWnd; if(RegisterRawInputDevices(&rid, 1, sizeof(rid))) { // Pump Windows messages MSG msg = {0}; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Return success return static_cast<int>(msg.wParam); } } } // Failed to initialize, output a diagnostic message (which is not more // friendly because it represents a scenario that should never occur) TCHAR szMessage[64]; if(SUCCEEDED(StringCchPrintf(szMessage, sizeof(szMessage)/sizeof(szMessage[0]), TEXT("Initialization failure. GetLastError=%d\r\n"), GetLastError()))) { (void)MessageBox(NULL, szMessage, APPLICATION_NAME, MB_OK | MB_ICONERROR); } } // Return failure return 0; // By contract, Windows frees all resources as part of process exit }
When I was writing my video frame grabbing sample, I came up with a potentially useful technique for maintaining the developer/designer separation that's so sought after in the WPF (and Silverlight) world. I'm sure something similar has been discussed before, but I hadn't come across it (and couldn't find anything like it with a quick search), so I thought I'd describe it here for posterity.
Background
The high-level goal is to maintain a separation between how an application works (developer) and how it looks (designer). The conventional approach to this is to follow some kind of model/view pattern where the application exposes various properties that represent its state and the interface binds to those properties to convey that state to the user. In WPF, this is typically done in XAML via Binding or TemplateBinding. These two constructs are very powerful and work quite well. However, it seemed to me that there might be a slightly better way...
The Conventional Way
In my scenario, the application exposes a Boolean DependencyProperty "Processing" which is normally False, but flips to True whenever a video file is being processed. The UI exposes an "Open File" Button which is normally enabled, but should flip to disabled whenever a video file is being processed.
The first instinct is to use a Trigger to apply the style changes to the Button. However, there's a catch - the Trigger needs to be defined on Button itself because the Button properties are being changed, yet the actual Trigger property (the Processing property) is defined on the Window object. I didn't see an obvious way to point the Trigger at the Window from the Button, so I moved on to the next idea which was to use a Binding to attach the Button's IsEnabled property to the Window's Processing property. Depending on whether you prefer ElementName or RelativeSource, the XAML looks like this:
<Button IsEnabled="{Binding Processing, ElementName=Window}" Content="..."/>
<Button IsEnabled="{Binding Processing, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:Window1}}}" Content="..."/>
Both approaches work, but not perfectly: the IsEnabled state of the Button is backwards because we've got IsEnabled == Processing when what we really want is IsEnabled == !Processing. Fortunately, Bindings support Converters and I've already written about the power of IValueConverter. So the designer goes to the developer and asks for a Converter that will invert the state of a Boolean variable and the developer writes the following code:
IsEnabled == Processing
IsEnabled == !Processing
/// <summary> /// Simplest implementation of a Converter to invert the state of a bool. /// </summary> /// <remarks> /// Needs parameter validation and error handling. /// </remarks> public class BooleanInversionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return !((bool)value); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Now the designer adds an instance of this Converter to the resources of the application:
<Application.Resources> <l:BooleanInversionConverter x:Key="BooleanInversionConverter"/> </Application.Resources>
And uses it to achieve the desired effect:
<Button IsEnabled="{Binding Processing, ElementName=Window, Converter={StaticResource BooleanInversionConverter}}" Content="..."/>
<Button IsEnabled="{Binding Processing, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type l:Window1}}, Converter={StaticResource BooleanInversionConverter}}" Content="..."/>
And the scenario works. But then the designer decides that the text of the Button should also be italic when it's disabled... And that requires the designer to go back to the developer who then needs to write a BooleanToFontStyleItalicConverter. Or maybe the designer attaches the Binding to the Button's Style property and gets the developer to write some kind of Style toggling Converter. Sheesh, what started out as a simple scenario has gotten annoyingly complicated and involves significant developer/designer coordination to accomplish something the designer should have been able to do independently in the first place.
A Different Way
So that's when I came up with a different idea that would let me use a Trigger after all: an attached DependencyProperty that inherits! Simply by changing the registration of the Processing property from Register to RegisterAttached and adding the FrameworkPropertyMetadataOptions.Inherits flag, we end up with a property on Window that looks to the Button just like a property defined on the Button itself. And suddenly the original idea to use a Trigger becomes possible - and even preferable! - because there's no longer a need for Bindings, Converters, or any of the complication above. Now the XAML looks just like it should and the designer can change things with complete autonomy:
<Button Content="..."> <Button.Style> <Style TargetType="{x:Type Button}"> <Style.Triggers> <Trigger Property="l:Window1.Processing" Value="True"> <Setter Property="IsEnabled" Value="False"/> <Setter Property="FontStyle" Value="Italic"/> </Trigger> </Style.Triggers> </Style> </Button.Style> </Button>
Much better.
Another Different Way
I discussed this scenario with my teammate Beatriz Costa briefly (she's one of the WPF data binding experts) and she independently suggested a very similar solution to what I came up with: DataTrigger. Using a DataTrigger instead of a Trigger has the nice benefit that the DependencyProperty no longer needs to be made attached/inheritable; any standard DependencyProperty will do. However, DataTrigger uses a Binding instead of a Property, so we end up back in the world of long-ish ElementName/RelativeSource markup syntax. Here's the XAML for the ElementName form:
<Button Content="..."> <Button.Style> <Style TargetType="{x:Type Button}"> <Style.Triggers> <DataTrigger Binding="{Binding Processing, ElementName=Window}" Value="True"> <Setter Property="IsEnabled" Value="False"/> <Setter Property="FontStyle" Value="Italic"/> </DataTrigger> </Style.Triggers> </Style> </Button.Style> </Button>
Just as better - pick whichever feels more comfortable. :)
Summary
Achieving a good developer/designer separation is good for a lot of reasons: flexibility, productivity, encapsulation, etc.. Unfortunately, it's not always as easy as we'd like... So if the approaches outlined here can help move us all just a little bit closer to that goal, then this post has served its purpose!
When I transfer clips from my digital camera to my computer, I briefly check that the video transferred successfully. For images, this is usually a matter of opening a few of them - for video files I usually load them in Media Player and seek to a couple of different points. Mine is hardly a fail proof system, but it's fairly quick and it makes me feel a little safer about deleting the originals from the camera. :) One day I got to thinking that it would be pretty easy to automatically generate a set of video thumbnails instead of seeking around manually...
So I did some research into video frame capture with WPF and found that it boiled down to a few main ideas:
Because of the asynchronous nature of the video frame updates, attempting to capture multiple frames in rapid succession (ex: seek, capture, seek, capture, ...) resulted in duplicate frames every time the capture occurred too soon for the frame's content to update (which was most of the time!). This was clearly unsuitable for my scenario because I wanted my application to generate its thumbnails as quickly as possible. After a bit of experimentation and playing around, I arrived at a technique that seems to work quite reliably in practice.
I wrote a simple WPF application to show how it works - here's an image of VideoThumbnailer displaying a summary of a video of someone tubing in the snow:
(The complete source code to VideoThumbnailer is available as an attachment to this post.)
The technique itself is fairly simple, if not entirely as straightforward as it would be if changes to the Position property were synchronous:
There's nothing particularly clever going on here, but there are a few complicating circumstances worth mentioning:
Having implemented the technique above, we should have a fast, reliable solution for video file thumbnail generation that works with a wide variety of file types by virtue of the underlying operating system's support. And, indeed, VideoThumbnailer works fine with all the WMV, DVR-MS, MPEG, and AVI files I've tried (and probably supports some other types I didn't try). There are plenty of changes one could make to VideoThumbnailer to make it a more useful application, but I hope it serves pretty well as a concrete example of the concepts discussed here.
Happy frame grabbing!