A walk through of Avalon inside Win32 (HwndSource)
To put Avalon inside Win32 applications, one uses HwndSource, which provides an hwnd that contains your Avalon content. HwndSource is pretty straightforward to use – first you create the HwndSource, giving it similar parameters to CreateWindow. Then you tell the HwndSource about the Avalon content you want inside it. Finally, you get the hwnd out of the HwndSource.
Let’s demonstrate this by taking the Windows Date and Time Properties dialog (which you get to buy double clicking on the time in the lower right corner of the screen), and bring this dialog into the 21st century by replacing the ugly Win32-style clock with an Avalon clock.
We can recreate this by creating a plain old C++ Win32 project in Visual Studio, and using the dialog editor to create:
(You don’t need to use Visual Studio to use HwndSource, and you don’t need to uses C++ to write Win32 programs, but this is a fairly typical way to do it)
We need to do five things to put an Avalon clock into that dialog:
The first step is to turn this unmanaged Win32 project into one that can call managed code. We need to tell the compiler to use /clr, link to the necessary DLLs we want to use, and decorate our Main method for use with Avalon.
First, to enable the use of managed code inside our C++ project: Right-click on w32clock project and select "Properties". On the "General" property page (the default), change Common Language Runtime support to "/clr".
Next, add references to DLLs necessary for Avalon: PresentationCore.dll, PresentationFramework.dll, System.dll, and WindowsBase.dll. Right-click on w32clock project and select "References...", and inside that dialog:
Finally, we add the STAThreadAttribute to our _tWinMain method for use with Avalon:
This attribute tells the CLR that when it initializes COM, it should use a single threaded apartment (STA), which is necessary for Avalon (and Windows Forms).
Create an Avalon Page
Next, we create a DLL that defines an Avalon Page. It’s often easiest to create the Avalon Page as a stand-alone application, and write and debug the Avalon portion that way. Once done, that project can be turned into a DLL by right-clicking the project, clicking on Properties, going to the Application, and changing Output type to Windows Class Library.
The Avalon dll project and then be combined with the Win32 project (one solution that contains two projects) – right-click on the solution, select Add\Existing Project.
To use that Avalon dll from the Win32 project, we need to add a reference:
Next, we use HwndSource to make the Avalon Page look like an hwnd. We add this block of code to a C++ file:
Let’s discuss what each part does. The first part is a bunch of using clauses so that we don’t need to fully qualify all of our names:
Then we define a function that creates the Avalon Page, puts an HwndSource around it, and returns the hwnd:
First it creates an HwndSource, whose parameters are similar to CreateWindow:
Next, we register our Avalon dll with the Avalon resource loader, so it knows where to find the resources and BAML files used by our Avalon clock:
Then we create the Avalon Page by calling its constructor:
We then connect the page to the HwndSource:
And in the final line, return the hwnd for the HwndSource:
Positioning the hwnd
So now we have an hwnd which contains the Avalon clock, we need to put that hwnd inside the Win32 dialog. If we knew just where to put the hwnd, we would just pass that size and location to the GetHwnd function we defined earlier. But we used a resource file to define our dialog, so we aren’t exactly sure where any of the hwnds are positioned. So we use the Visual Studio dialog editor to put a Win32 STATIC control where we want the clock to go (“Insert clock here”), and use that to position the Avalon clock.
Where we handle WM_INITDIALOG, we use GetDlgItem to retrieve the hwnd for our placeholder STATIC:
We then calculate the size and position of that placeholder STATIC, so that we can put our Avalon clock in that place:
Then we hide the placeholder STATIC:
And create the Avalon clock hwnd in that location: