Posts Tagged ‘AdornerLayer’

Interop & IDataErrorInfo

September 17, 2011 Leave a comment

Despite of all the interop support available sometimes you will still come across some tricky issues which will be difficult to overcome. One such problem I faced recently. I had to configure the Dialog with MFC PropertySheet control. This PropertySheet control was holding few Property pages. These property pages didn’t had any MFC controls as content but hosting the user controls developed in WPF. First I will write very briefly about how to load the WPF user control inside the MFC container and then I will explain about my tricky problem and how I fixed it.

Hosting of WPF user control as content of PropertyPage wasn’t that challenging. In order to host the WPF content inside property page, first HwndSource needs to be configured. In order to create HwndSource, first you need to create HwndSourceParameter. Typical configuration for HwndSourceParameter can be as under :

System::Windows::Interop::HwndSourceParameters^ sourceParams = gcnew System::Windows::Interop::HwndSourceParameters (<Window Name>);

// X position where WPF content to be placed
sourceParams->PositionX = x;
// Y position where WPF content to be placed
sourceParams->PositionY = y;
// height of the WPF content
sourceParams->Height = height;
// width of the WPF content
sourceParams->Width = width;
// Handle for the Parent window inside which WPF content to be hosted
sourceParams->ParentWindow = IntPtr(parent);
// style attributes as needed (“WS_TABSTOP” style needs to be set if WPF content needs to get focus while tabbing
sourceParams->WindowStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP;

Another important property for HwndSourceParams is “ResetFocusMode”. Default value for this property is “Auto”. Setting this property to “Auto” makes sure that when when the PropertySheet control (in my case) looses the focus and receives focus back the control which had focus prior to loosing focus retains the focus back. If you set this property to “None” then, none of the controls inside the page will have focus when propertysheet control receives the focus.

// now create HwndSource using this parameters
System::Windows::Interop::HwndSource^ hwndSource = gcnew HwndSource(*sourceParams)

Now suppose I have a WPF user control named “PageUserControl” then create instance of this user control.

PageContentUserControl^ pageContent = gcnew PageContentUserControl();
//Now set this user control as RootVisual of HwndSource object. 
hwndSource->RootVisual = pageContent ;

With above code in place it is possible to host the WPF content inside MFC control. This is just basic information which one should know in order to host the WPF content inside MFC container however somewhat plumbing work will be needed depending upon MFC container control in which you are placing the WPF control.

After hosting the control I had one more issue to deal with. My User control developed in WPF had few checkboxes, text boxes, etc. as it’s content. In order to validate my inputs and giving visual indication about the data validation failure, I had implemented interface “IDataErrorInfo”. (Now how this interface is implemented and how it works is not covered in this Post. Google it to find more on it)

When “IDataErrorInfo” interface implemented and bound value to the control is invalid, control will have a red border around it to indicate user about the validation failure. Also, when you set focus on the control like check box through tabbing, you will not be able to see the dotted line around check box when it receives the focus. Following figure shows the focus styles for each case. When User Control is hosted within MFC container, you will assume that “IDataErrorInfo” implementation should work but infect it won’t.

The problem of not being able to see all such decoration is absence of Adorner layer. All these error templates and focus styles etc. are drawn inside the AdornerLayer. The WPF window has an AdornerLayer by default, so we can always see validation errors in WPF. When hosting a user control in MFC, adornerlayer is not available by default and need to add an AdornerLayer into your UserControl to support adorners.

So in order to view all such Focus styles / Error template styles, we need to decorate the UserControl with AdornerDecorator. So following code should do the fix for you :

<UserControl x:Class="TestPjt.TestUC"
             d:DesignHeight="50" d:DesignWidth="300">
              <TextBox x:Name="txtbox"/>

This fix will start showing the expected focus and error template style for each control. HTH!!