Использование визуального уровня на платформе Windows Forms

Вы можете использовать API Composition среды выполнения Windows (также называемые визуальным уровнем) в приложениях Windows Forms, чтобы создавать современные интерфейсы для пользователей Windows.

Полный код для этого руководства доступен на сайте GitHub: пример Windows Forms HelloComposition.

Необходимые компоненты

Для использования API размещения для UWP накладываются приведенные ниже предварительные требования.

Как использовать интерфейсы API Composition в Windows Forms

При работе с этим руководством вы создадите пользовательский интерфейс приложения Windows Forms и добавите в него анимированные элементы Composition. Компоненты Windows Forms и компоненты Composition простые, но приведенный код взаимодействия одинаков независимо от сложности компонентов. Завершенное приложение выглядит следующим образом.

The running app UI

Создание проекта Windows Forms

Первым шагом является создание проекта приложения Windows Forms, включающего в себя определение приложения и главную форму для пользовательского интерфейса.

Чтобы создать проект приложения Windows Forms на Visual C# с именем HelloComposition, сделайте следующее.

  1. Откройте Visual Studio и выберите Файл>Создать>Проект.
    Появится диалоговое окно Создать проект.
  2. В категории Установлены разверните узел Visual C#, а затем выберите Windows Desktop.
  3. Выберите шаблон Приложение Windows Forms (.NET Framework).
  4. Введите имя HelloComposition, выберите платформу .NET Framework 4.7.2, а затем нажмите кнопку ОК.

Visual Studio создаст проект и откроет конструктор для окна приложения по умолчанию, Form1.xaml.

Настройка проекта для использования интерфейсов API среды выполнения Windows

Чтобы использовать API среды выполнения Windows (WinRT) в приложении Windows Forms, необходимо настроить проект Visual Studio для доступа к среде выполнения Windows. Кроме того, API Composition активно используют векторы, поэтому нужно добавить ссылки, необходимые для использования векторов.

Для решения этих задач предоставляются соответствующие пакеты NuGet. Установите последние версии этих пакетов, чтобы добавить необходимые ссылки в проект.

Примечание.

Хотя мы рекомендуем использовать для настройки проекта пакеты NuGet, необходимые ссылки можно добавить вручную. Дополнительные сведения см. в статье Улучшение классического приложения для Windows. В следующей таблице приведены файлы, на которые необходимо добавить ссылки.

Файл Местонахождение
System.Runtime.WindowsRuntime C:\Windows\Microsoft.NET\Framework\v4.0.30319
Windows.Foundation.UniversalApiContract.winmd C:\Program Files (x86)\Windows Kits\10\References<версия пакета SDK>\Windows.Foundation.UniversalApiContract<версия>
Windows.Foundation.FoundationContract.winmd C:\Program Files (x86)\Windows Kits\10\References<версия пакета SDK>\Windows.Foundation.FoundationContract<версия>
System.Numerics.Vectors.dll C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\System.Numerics.Vectors\v4.0_4.0.0.0__b03f5f7f11d50a3a
System.Numerics.dll C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v4.7.2

Создание пользовательского элемента управления для управления взаимодействием

Для размещения содержимого, созданного с помощью визуального слоя, создается пользовательский элемент управления, производный от Control. Этот элемент управления предоставляет доступ к дескриптору окна, который нужен, чтобы создать контейнер для содержимого визуального уровня.

Здесь потребуется выполнить большую часть настройки для размещения интерфейсов API Composition. В этом элементе управления для добавления интерфейсов API Composition в приложение Windows Forms используются службы вызова платформы (PInvoke) и взаимодействие COM. Дополнительные сведения о PInvoke и взаимодействии COM см. в разделе Взаимодействие с неуправляемым кодом.

Совет

При необходимости сверьтесь с полным кодом в конце руководства, чтобы убедиться в том, что весь код добавлен в соответствующе места при работе с этим руководством.

  1. Добавьте в проект новый файл пользовательского элемента управления, производного от Control.

    • В обозревателе решений щелкните проект HelloComposition правой кнопкой мыши.
    • В контекстном меню выберите Добавить>Новый элемент.
    • В окне Добавление нового элемента выберите Пользовательский элемент управления.
    • Присвойте элементу управления имя CompositionHost.cs, а затем щелкните Добавить. Файл CompositionHost.cs откроется в представлении конструктора.
  2. Перейдите к представлению кода для CompositionHost.cs и добавьте приведенный ниже код в класс.

    // Add
    // using Windows.UI.Composition;
    
    IntPtr hwndHost;
    object dispatcherQueue;
    protected ContainerVisual containerVisual;
    protected Compositor compositor;
    
    private ICompositionTarget compositionTarget;
    
    public Visual Child
    {
        set
        {
            if (compositor == null)
            {
                InitComposition(hwndHost);
            }
            compositionTarget.Root = value;
        }
    }
    
  3. Добавьте код в конструктор.

    В этом конструкторе вызываются методы InitializeCoreDispatcher и InitComposition. Эти методы создаются на следующих шагах.

    public CompositionHost()
    {
        InitializeComponent();
    
        // Get the window handle.
        hwndHost = Handle;
    
        // Create dispatcher queue.
        dispatcherQueue = InitializeCoreDispatcher();
    
        // Build Composition tree of content.
        InitComposition(hwndHost);
    }
    
  4. Инициализируйте поток с помощью CoreDispatcher. Основной диспетчер отвечает за обработку сообщений окон и диспетчеризацию событий интерфейсов API WinRT. Новые экземпляры Compositor должны создаваться в потоке с CoreDispatcher.

    • Создайте метод InitializeCoreDispatcher и добавьте код для настройки очереди диспетчера.
    // Add
    // using System.Runtime.InteropServices;
    
    private object InitializeCoreDispatcher()
    {
        DispatcherQueueOptions options = new DispatcherQueueOptions();
        options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA;
        options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
        options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));
    
        object queue = null;
        CreateDispatcherQueueController(options, out queue);
        return queue;
    }
    
    • Для очереди диспетчера требуется объявление PInvoke. Поместите это объявление в конец блока кода класса. (Мы разместим этот код внутри области, чтобы сделать код класса аккуратным.)
    #region PInvoke declarations
    
    //typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
    //{
    //    DQTAT_COM_NONE,
    //    DQTAT_COM_ASTA,
    //    DQTAT_COM_STA
    //};
    internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
    {
        DQTAT_COM_NONE = 0,
        DQTAT_COM_ASTA = 1,
        DQTAT_COM_STA = 2
    };
    
    //typedef enum DISPATCHERQUEUE_THREAD_TYPE
    //{
    //    DQTYPE_THREAD_DEDICATED,
    //    DQTYPE_THREAD_CURRENT
    //};
    internal enum DISPATCHERQUEUE_THREAD_TYPE
    {
        DQTYPE_THREAD_DEDICATED = 1,
        DQTYPE_THREAD_CURRENT = 2,
    };
    
    //struct DispatcherQueueOptions
    //{
    //    DWORD dwSize;
    //    DISPATCHERQUEUE_THREAD_TYPE threadType;
    //    DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
    //};
    [StructLayout(LayoutKind.Sequential)]
    internal struct DispatcherQueueOptions
    {
        public int dwSize;
    
        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_TYPE threadType;
    
        [MarshalAs(UnmanagedType.I4)]
        public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
    };
    
    //HRESULT CreateDispatcherQueueController(
    //  DispatcherQueueOptions options,
    //  ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController
    //);
    [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
    internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options,
                                            [MarshalAs(UnmanagedType.IUnknown)]
                                            out object dispatcherQueueController);
    
    #endregion PInvoke declarations
    

    Теперь очередь диспетчера готова, и можно начать инициализацию и создание содержимого Composition.

  5. Инициализируйте Compositor. Compositor — это фабрика, которая создает разные типы в пространстве имен Windows.UI.Composition, охватывающем визуальный уровень, систему эффектов и систему анимации. Класс Compositor также управляет временем существования объектов, созданных из фабрики.

    private void InitComposition(IntPtr hwndHost)
    {
        ICompositorDesktopInterop interop;
    
        compositor = new Compositor();
        object iunknown = compositor as object;
        interop = (ICompositorDesktopInterop)iunknown;
        IntPtr raw;
        interop.CreateDesktopWindowTarget(hwndHost, true, out raw);
    
        object rawObject = Marshal.GetObjectForIUnknown(raw);
        compositionTarget = (ICompositionTarget)rawObject;
    
        if (raw == null) { throw new Exception("QI Failed"); }
    
        containerVisual = compositor.CreateContainerVisual();
        Child = containerVisual;
    }
    
    • Для ICompositorDesktopInterop и ICompositionTarget требуется импорт COM. Поместите этот код после класса CompositionHost внутри объявления пространства имен.
    #region COM Interop
    
    /*
    #undef INTERFACE
    #define INTERFACE ICompositorDesktopInterop
        DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807")
        {
            IFACEMETHOD(CreateDesktopWindowTarget)(
                _In_ HWND hwndTarget,
                _In_ BOOL isTopmost,
                _COM_Outptr_ IDesktopWindowTarget * *result
                ) PURE;
        };
    */
    [ComImport]
    [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ICompositorDesktopInterop
    {
        void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
    }
    
    //[contract(Windows.Foundation.UniversalApiContract, 2.0)]
    //[exclusiveto(Windows.UI.Composition.CompositionTarget)]
    //[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)]
    //interface ICompositionTarget : IInspectable
    //{
    //    [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value);
    //    [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value);
    //}
    
    [ComImport]
    [Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
    public interface ICompositionTarget
    {
        Windows.UI.Composition.Visual Root
        {
            get;
            set;
        }
    }
    
    #endregion COM Interop
    

Создание пользовательского элемента управления для размещения элементов композиции

Рекомендуется разместить код, который создает элементы композиции и управляет ими, в отдельном элементе управления, производном от CompositionHost. Это позволяет многократно использовать код взаимодействия, созданный в классе CompositionHost.

Здесь создается пользовательский элемент управления, производный от CompositionHost. Этот элемент управления добавляется на панель элементов Visual Studio, чтобы его можно было добавить в форму.

  1. Добавьте в проект новый файл пользовательского элемента управления, производного от CompositionHost.

    • В обозревателе решений щелкните проект HelloComposition правой кнопкой мыши.
    • В контекстном меню выберите Добавить>Новый элемент.
    • В окне Добавление нового элемента выберите Пользовательский элемент управления.
    • Присвойте элементу управления имя CompositionHostControl.cs, а затем щелкните Добавить. Файл CompositionHostControl.cs откроется в представлении конструктора.
  2. В области "Свойства" представления конструктора для CompositionHostControl.cs задайте для свойства BackColor значение ControlLight.

    Задавать цвет фона необязательно. Мы делаем это здесь, чтобы пользовательский элемент управления выделялся на фоне формы.

  3. Переключитесь на представление кода CompositionHostControl.cs и обновите объявление класса, чтобы он стал производным от CompositionHost.

    class CompositionHostControl : CompositionHost
    
  4. Обновите конструктор, чтобы вызвать базовый конструктор.

    public CompositionHostControl() : base()
    {
    
    }
    

Добавление элементов композиции

Теперь, когда инфраструктура настроена, можно добавить содержимое Composition в пользовательский интерфейс приложения.

В этом примере вы добавите в класс CompositionHostControl код, который создает и анимирует простой элемент SpriteVisual.

  1. Добавьте элемент композиции.

    В CompositionHostControl.cs добавьте приведенные ниже методы в класс CompositionHostControl.

    // Add
    // using Windows.UI.Composition;
    
    public void AddElement(float size, float offsetX, float offsetY)
    {
        var visual = compositor.CreateSpriteVisual();
        visual.Size = new Vector2(size, size); // Requires references
        visual.Brush = compositor.CreateColorBrush(GetRandomColor());
        visual.Offset = new Vector3(offsetX, offsetY, 0);
        containerVisual.Children.InsertAtTop(visual);
    
        AnimateSquare(visual, 3);
    }
    
    private void AnimateSquare(SpriteVisual visual, int delay)
    {
        float offsetX = (float)(visual.Offset.X);
        Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
        float bottom = Height - visual.Size.Y;
        animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f));
        animation.Duration = TimeSpan.FromSeconds(2);
        animation.DelayTime = TimeSpan.FromSeconds(delay);
        visual.StartAnimation("Offset", animation);
    }
    
    private Windows.UI.Color GetRandomColor()
    {
        Random random = new Random();
        byte r = (byte)random.Next(0, 255);
        byte g = (byte)random.Next(0, 255);
        byte b = (byte)random.Next(0, 255);
        return Windows.UI.Color.FromArgb(255, r, g, b);
    }
    

Добавление элемента управления в форму

Теперь, когда у вас есть пользовательский элемент управления для размещения содержимого Composition, его можно добавить в пользовательский интерфейс приложения. Здесь следует добавить экземпляр CompositionHostControl, созданный на предыдущем шаге. CompositionHostControl автоматически добавляется на панель элементов Visual Studio в области имя проекта Компоненты.

  1. В представлении конструктора для Form1.CS добавьте кнопку в пользовательский интерфейс.

    • Перетащите элемент Button с панели элементов на Form1. Поместите его в левый верхний угол формы. (Чтобы проверить расположение элементов управления, посмотрите на картинку в начале руководства.)
    • В области "Свойства" измените значение свойства Text с Button1 на Add composition element.
    • Измените размер элемента Button, чтобы вместить весь текст.

    (Дополнительные сведения см. в статье Практическое руководство. Добавление элементов управления в Windows Forms.)

  2. Добавьте элемент CompositionHostControl в пользовательский интерфейс.

    • Перетащите CompositionHostControl из панели элементов на форму Form1. Поместите его справа от Button.
    • Измените размер CompositionHost, чтобы заполнить оставшуюся часть формы.
  3. Обработайте событие нажатия кнопки.

    • В области "Свойства" щелкните значок молнии, чтобы перейти к представлению "События".
    • Из списка событий выберите событие Click, введите Button_Click и нажмите клавишу ВВОД.
    • Этот код добавится в Form1.cs.
    private void Button_Click(object sender, EventArgs e)
    {
    
    }
    
  4. Добавьте в обработчик нажатия кнопки код для создания новых элементов.

    • В Form1.cs добавьте код в обработчик событий Button_Click, созданный ранее. Этот код вызывает CompositionHostControl1.AddElement для создания нового элемента со случайно заданным размером и смещением. (Экземпляр CompositionHostControl был автоматически назван compositionHostControl1 при перетаскивании на форму.)
    // Add
    // using System;
    
    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Random random = new Random();
        float size = random.Next(50, 150);
        float offsetX = random.Next(0, (int)(compositionHostControl1.Width - size));
        float offsetY = random.Next(0, (int)(compositionHostControl1.Height/2 - size));
        compositionHostControl1.AddElement(size, offsetX, offsetY);
    }
    

Теперь выполните сборку приложения Windows Forms и запустите его. Если вы нажмете кнопку, в пользовательском интерфейсе должны появиться анимированные квадраты.

Следующие шаги

Более полный пример интеграции визуального уровня в Windows Forms, основанный на той же инфраструктуре, доступен на сайте GitHub.

Дополнительные ресурсы

Полный код

Ниже приведен полный код для этого руководства.

Form1.cs

using System;
using System.Windows.Forms;

namespace HelloComposition
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, EventArgs e)
        {
            Random random = new Random();
            float size = random.Next(50, 150);
            float offsetX = random.Next(0, (int)(compositionHostControl1.Width - size));
            float offsetY = random.Next(0, (int)(compositionHostControl1.Height/2 - size));
            compositionHostControl1.AddElement(size, offsetX, offsetY);
        }
    }
}

CompositionHostControl.cs

using System;
using System.Numerics;
using Windows.UI.Composition;

namespace HelloComposition
{
    class CompositionHostControl : CompositionHost
    {
        public CompositionHostControl() : base()
        {

        }

        public void AddElement(float size, float offsetX, float offsetY)
        {
            var visual = compositor.CreateSpriteVisual();
            visual.Size = new Vector2(size, size); // Requires references
            visual.Brush = compositor.CreateColorBrush(GetRandomColor());
            visual.Offset = new Vector3(offsetX, offsetY, 0);
            containerVisual.Children.InsertAtTop(visual);

            AnimateSquare(visual, 3);
        }

        private void AnimateSquare(SpriteVisual visual, int delay)
        {
            float offsetX = (float)(visual.Offset.X);
            Vector3KeyFrameAnimation animation = compositor.CreateVector3KeyFrameAnimation();
            float bottom = Height - visual.Size.Y;
            animation.InsertKeyFrame(1f, new Vector3(offsetX, bottom, 0f));
            animation.Duration = TimeSpan.FromSeconds(2);
            animation.DelayTime = TimeSpan.FromSeconds(delay);
            visual.StartAnimation("Offset", animation);
        }

        private Windows.UI.Color GetRandomColor()
        {
            Random random = new Random();
            byte r = (byte)random.Next(0, 255);
            byte g = (byte)random.Next(0, 255);
            byte b = (byte)random.Next(0, 255);
            return Windows.UI.Color.FromArgb(255, r, g, b);
        }
    }
}

CompositionHost.cs

using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Windows.UI.Composition;

namespace HelloComposition
{
    public partial class CompositionHost : Control
    {
        IntPtr hwndHost;
        object dispatcherQueue;
        protected ContainerVisual containerVisual;
        protected Compositor compositor;
        private ICompositionTarget compositionTarget;

        public Visual Child
        {
            set
            {
                if (compositor == null)
                {
                    InitComposition(hwndHost);
                }
                compositionTarget.Root = value;
            }
        }

        public CompositionHost()
        {
            // Get the window handle.
            hwndHost = Handle;

            // Create dispatcher queue.
            dispatcherQueue = InitializeCoreDispatcher();

            // Build Composition tree of content.
            InitComposition(hwndHost);
        }

        private object InitializeCoreDispatcher()
        {
            DispatcherQueueOptions options = new DispatcherQueueOptions();
            options.apartmentType = DISPATCHERQUEUE_THREAD_APARTMENTTYPE.DQTAT_COM_STA;
            options.threadType = DISPATCHERQUEUE_THREAD_TYPE.DQTYPE_THREAD_CURRENT;
            options.dwSize = Marshal.SizeOf(typeof(DispatcherQueueOptions));

            object queue = null;
            CreateDispatcherQueueController(options, out queue);
            return queue;
        }

        private void InitComposition(IntPtr hwndHost)
        {
            ICompositorDesktopInterop interop;

            compositor = new Compositor();
            object iunknown = compositor as object;
            interop = (ICompositorDesktopInterop)iunknown;
            IntPtr raw;
            interop.CreateDesktopWindowTarget(hwndHost, true, out raw);

            object rawObject = Marshal.GetObjectForIUnknown(raw);
            compositionTarget = (ICompositionTarget)rawObject;

            if (raw == null) { throw new Exception("QI Failed"); }

            containerVisual = compositor.CreateContainerVisual();
            Child = containerVisual;
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            base.OnPaint(pe);
        }

        #region PInvoke declarations

        //typedef enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
        //{
        //    DQTAT_COM_NONE,
        //    DQTAT_COM_ASTA,
        //    DQTAT_COM_STA
        //};
        internal enum DISPATCHERQUEUE_THREAD_APARTMENTTYPE
        {
            DQTAT_COM_NONE = 0,
            DQTAT_COM_ASTA = 1,
            DQTAT_COM_STA = 2
        };

        //typedef enum DISPATCHERQUEUE_THREAD_TYPE
        //{
        //    DQTYPE_THREAD_DEDICATED,
        //    DQTYPE_THREAD_CURRENT
        //};
        internal enum DISPATCHERQUEUE_THREAD_TYPE
        {
            DQTYPE_THREAD_DEDICATED = 1,
            DQTYPE_THREAD_CURRENT = 2,
        };

        //struct DispatcherQueueOptions
        //{
        //    DWORD dwSize;
        //    DISPATCHERQUEUE_THREAD_TYPE threadType;
        //    DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
        //};
        [StructLayout(LayoutKind.Sequential)]
        internal struct DispatcherQueueOptions
        {
            public int dwSize;

            [MarshalAs(UnmanagedType.I4)]
            public DISPATCHERQUEUE_THREAD_TYPE threadType;

            [MarshalAs(UnmanagedType.I4)]
            public DISPATCHERQUEUE_THREAD_APARTMENTTYPE apartmentType;
        };

        //HRESULT CreateDispatcherQueueController(
        //  DispatcherQueueOptions options,
        //  ABI::Windows::System::IDispatcherQueueController** dispatcherQueueController
        //);
        [DllImport("coremessaging.dll", EntryPoint = "CreateDispatcherQueueController", CharSet = CharSet.Unicode)]
        internal static extern IntPtr CreateDispatcherQueueController(DispatcherQueueOptions options,
                                                 [MarshalAs(UnmanagedType.IUnknown)]
                                        out object dispatcherQueueController);

        #endregion PInvoke declarations
    }

    #region COM Interop

    /*
    #undef INTERFACE
    #define INTERFACE ICompositorDesktopInterop
        DECLARE_INTERFACE_IID_(ICompositorDesktopInterop, IUnknown, "29E691FA-4567-4DCA-B319-D0F207EB6807")
        {
            IFACEMETHOD(CreateDesktopWindowTarget)(
                _In_ HWND hwndTarget,
                _In_ BOOL isTopmost,
                _COM_Outptr_ IDesktopWindowTarget * *result
                ) PURE;
        };
    */
    [ComImport]
    [Guid("29E691FA-4567-4DCA-B319-D0F207EB6807")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface ICompositorDesktopInterop
    {
        void CreateDesktopWindowTarget(IntPtr hwndTarget, bool isTopmost, out IntPtr test);
    }

    //[contract(Windows.Foundation.UniversalApiContract, 2.0)]
    //[exclusiveto(Windows.UI.Composition.CompositionTarget)]
    //[uuid(A1BEA8BA - D726 - 4663 - 8129 - 6B5E7927FFA6)]
    //interface ICompositionTarget : IInspectable
    //{
    //    [propget] HRESULT Root([out] [retval] Windows.UI.Composition.Visual** value);
    //    [propput] HRESULT Root([in] Windows.UI.Composition.Visual* value);
    //}

    [ComImport]
    [Guid("A1BEA8BA-D726-4663-8129-6B5E7927FFA6")]
    [InterfaceType(ComInterfaceType.InterfaceIsIInspectable)]
    public interface ICompositionTarget
    {
        Windows.UI.Composition.Visual Root
        {
            get;
            set;
        }
    }
    #endregion COM Interop
}