65.9K
CodeProject is changing. Read more.
Home

Creating a WPF Frame Inside of a Window with AllowsTransparency Set to True

emptyStarIconemptyStarIconemptyStarIconemptyStarIconemptyStarIcon

0/5 (0 vote)

May 19, 2011

CPOL
viewsIcon

12722

How to create a WPF frame inside of a window with AllowsTransparency set to true

As much as I love Microsoft, it's a wonder that they don't update some of their developer controls/tools to use the technology that they are writing everything else in.

Thankfully, the developer community is on top of it.

WPF: Using a Frame element in a custom shaped window

Here's my modified code to enable this functionality as a reusable class:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using DrawingPoint = System.Drawing.Point;

namespace VisibleFrame
{
    /// <summary>
    /// Original code from: http://davidfulop.spaces.live.com/blog/cns!BC8EF43294ECB87!427.entry
    /// Converted window into reusable class: http://www.aronweiler.com
    /// </summary>
    internal class VisibleFrame
    {
        //The following two windows-calls will be necessary to draw the Frame onto the main window
        [DllImport("user32.dll")]
        internal static extern bool ClientToScreen(IntPtr hWnd, ref DrawingPoint point);

        [DllImport("user32.dll")]
        internal static extern bool MoveWindow
        (IntPtr hWnd, int x, int y, int width, int height, bool repaint);

        //The invisible window in which the Frame will be hosted
        Window w = new Window { WindowStyle = WindowStyle.None, 
        ShowInTaskbar = false, ResizeMode = ResizeMode.NoResize };

        Window targetWindow;
        Border targetControl;

        Frame frame;

        internal VisibleFrame
        (Window targetWindow, Border targetControl, string initialLocation)
        {
            this.targetWindow = targetWindow;
            this.targetControl = targetControl;

            targetWindow.Loaded += WindowLoad;

            frame = new Frame();

            if(!string.IsNullOrEmpty(initialLocation))
            {
                Navigate(initialLocation);
            }
        }

        internal void WindowLoad(object sender, RoutedEventArgs e)
        {
            //It is crucial to set the Owner, 
            //because if we don't the inner window won't be repainted after a task-change
            w.Owner = targetWindow;

            //Creating the Frame and registering to the repaint-related events
            w.Content = frame;
            targetControl.SizeChanged += delegate { RepaintFrame(); };
            targetWindow.LocationChanged += delegate { RepaintFrame(); };

            //Showing the Window that contains our Frame
            w.Show();

            //Initial call to the RepaintFrame 
            //lets us place the Frame to the correct location
            RepaintFrame();
        }

        private void RepaintFrame()
        {
            //We get a Win32 representation of the main window
            HwndSource hostWindow = HwndSource.FromVisual(targetWindow) as HwndSource;

            //We get a visual manager for point transformations
            CompositionTarget ct = hostWindow.CompositionTarget;

            //We need to translate the coordinates of the 
            //embedded window to the Border element (browserhost)
            //Note, that the ClientToScreen function don't understand 
            //WPF's Point struct, that's why we have to convert it
            Point loc = ct.TransformToDevice.Transform
            (targetControl.TranslatePoint(new Point(), targetWindow));
            DrawingPoint locD = new DrawingPoint((int)loc.X, (int)loc.Y);
            ClientToScreen(hostWindow.Handle, ref locD);

            //We have to calculate the size - basically the 
            //bottom right coordinate of the Border element
            Point size = ct.TransformToDevice.Transform(new Point
            (targetControl.ActualWidth, targetControl.ActualHeight));

            //Last thing to do: call a Win32 function to paint 
            //the Frame to the correct location
            MoveWindow((HwndSource.FromVisual(w) as HwndSource).Handle, 
            locD.X, locD.Y, (int)size.X, (int)size.Y, true);
        }

        internal void Navigate(string location)
        {
            frame.Navigate(new Uri(location));
        }
    }
}