Utilização da Camada Visual com o Windows Forms

Pode usar APIs de Composição do Windows Runtime (também chamadas de Camada Visual) nas suas aplicações Windows Forms para criar experiências modernas que melhorem para os utilizadores do Windows.

O código completo deste tutorial está disponível em GitHub: Windows Forms exemplo HelloComposition.

Pré-requisitos

A API de hospedagem UWP tem esses pré-requisitos.

Como usar APIs de Composições no Windows Forms

Neste tutorial, crias uma interface simples do Windows Forms e adicionas elementos animados de Composição. Tanto os componentes Windows Forms como Composition são mantidos simples, mas o código de interoperabilidade mostrado é o mesmo independentemente da complexidade dos componentes. O aplicativo concluído tem esta aparência.

A interface do usuário do aplicativo em execução

Criar um projeto Windows Forms

O primeiro passo é criar o projeto de aplicação Windows Forms, que inclui uma definição de aplicação e o formulário principal da interface.

Para criar um novo projeto Windows Forms Application em Visual C# chamado HelloComposition:

  1. Abra o Visual Studio e selecione Arquivo>Novo Projeto>.
    O diálogo do Novo Projeto abre-se.
  2. Na categoria Instalado, expanda o nó Visual C# e depois selecione Ambiente de Trabalho do Windows.
  3. Selecione o modelo de aplicação Windows Forms (.NET Framework).
  4. Insira o nome HelloComposition, selecione Framework .NET Framework 4.7.2, depois clique em OK.

Visual Studio cria o projeto e abre o designer da janela de aplicação padrão chamada Form1.cs.

Configurar o projeto para usar APIs do Tempo de Execução do Windows

Para usar APIs do Windows Runtime (WinRT) na sua aplicação Windows Forms, precisa de configurar o seu projeto Visual Studio para aceder ao Windows Runtime. Além disso, os vetores são amplamente usados pelas APIs de Composição, por isso é necessário adicionar as referências necessárias para usar vetores.

Existem pacotes NuGet disponíveis para responder a ambas as necessidades. Instale as versões mais recentes destes pacotes para adicionar as referências necessárias ao seu projeto.

Note

Embora recomendemos usar os pacotes NuGet para configurar o seu projeto, pode adicionar manualmente as referências necessárias. Para mais informações, consulte Melhorar a sua aplicação de ambiente de trabalho para Windows. A tabela seguinte mostra os ficheiros aos quais precisa de adicionar referências.

File Local
System.Runtime.WindowsRuntime C:\Windows\Microsoft.NET\Framework\v4.0.30319
Windows.Foundation.UniversalApiContract.winmd C:\Program Files (x86)\Windows Kits\10\References<sdk versão>\Windows.Foundation.UniversalApiContract<versão>
Windows.Foundation.FoundationContract.winmd C:\Program Files (x86)\Windows Kits\10\References<versão do SDK>\Windows.Foundation.FoundationContract<versão>
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

Crie um controlo personalizado para gerir a interoperabilidade

Para alojar o conteúdo que crias com a camada visual, crias um controlo personalizado derivado do Control. Este controlo permite-lhe aceder a um Handle de janela, necessário para criar o contentor para o conteúdo da sua camada visual.

É aqui que fazes a maior parte da configuração para alojar APIs de Composição. Neste controlo, utiliza-se Platform Invocation Services (PInvoke) e COM Interop para integrar APIs de Composições na sua aplicação de Windows Forms. Para mais informações sobre o Invoke e o COM Interop, veja Interoperar com código não gerido.

Sugestão

Se você precisar, verifique o código completo no final do tutorial para se certificar de que todo o código está nos lugares certos enquanto você trabalha no tutorial.

  1. Adicione um novo ficheiro de controlo personalizado ao seu projeto que derive do Control.

    • No Explorador de Soluções , clique com o botão direito do mouse no projeto HelloComposition.
    • No menu de contexto, selecione Adicionar>Novo Item....
    • No diálogo Adicionar Novo Item , selecione Controlo Personalizado.
    • Nomeia o CompositionHost.cs de controlo e depois clica em Adicionar. CompositionHost.cs abre na vista Design.
  2. Mude para a vista de código para CompositionHost.cs e adicione o seguinte código à classe.

    // 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. Adicionar código ao construtor.

    No construtor, chama os métodos InitializeCoreDispatcher e InitComposition . Você cria esses métodos nas próximas etapas.

    public CompositionHost()
    {
        InitializeComponent();
    
        // Get the window handle.
        hwndHost = Handle;
    
        // Create dispatcher queue.
        dispatcherQueue = InitializeCoreDispatcher();
    
        // Build Composition tree of content.
        InitComposition(hwndHost);
    }
    
  4. Inicializar um thread com um CoreDispatcher. O despachante principal é responsável pelo processamento de mensagens de janelas e por despachar eventos para APIs WinRT. Novas instâncias do Compositor devem ser criadas numa thread que tenha um CoreDispatcher.

    • Crie um método chamado InitializeCoreDispatcher e adicione código para configurar a fila do despachante.
    // 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;
    }
    
    • A fila do despachante requer uma declaração de PInvoke. Coloque esta declaração no final do código da classe. (Colocamos este código dentro de uma região para manter o código da classe organizado.)
    #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
    

    Agora tem a fila do dispatcher pronta e pode começar a inicializar e criar Conteúdo de Composição.

  5. Inicializar o Compositor. O Compositor é uma fábrica que cria uma variedade de tipos no namespace Windows.UI.Composition abrangendo a camada visual, o sistema de efeitos e o sistema de animação. A classe Compositor também gere a vida útil dos objetos criados a partir da fábrica.

    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 e ICompositionTarget requerem importações COM. Coloque este código após a classe CompositionHost , mas dentro da declaração do namespace.
    #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
    

Crie um controlo personalizado para alojar elementos de composição

É uma boa ideia colocar o código que gera e gere os elementos da sua composição num controlo separado que deriva do CompositionHost. Isto mantém o código de interoperabilidade que criou na classe CompositionHost reutilizável.

Aqui, cria um controlo personalizado derivado do CompositionHost. Este controlo é adicionado à caixa de ferramentas do Visual Studio para que possa adicioná-lo ao seu formulário.

  1. Adicione um novo ficheiro de controlo personalizado ao seu projeto que derive do CompositionHost.

    • No Explorador de Soluções , clique com o botão direito do mouse no projeto HelloComposition.
    • No menu de contexto, selecione Adicionar>Novo Item....
    • No diálogo Adicionar Novo Item , selecione Controlo Personalizado.
    • Dá o nome CompositionHostControl.cs ao controlo, depois clica em Adicionar. CompositionHostControl.cs abre-se na vista de Design.
  2. No painel de Propriedades na vista de design de CompositionHostControl.cs, defina a propriedade BackColor para ControlLight.

    Definir a cor de fundo é opcional. Fazemos isso aqui para que possas ver o teu controlo personalizado contra o fundo do formulário.

  3. Mude para o modo de visualização de código para CompositionHostControl.cs e atualize a declaração de classe para derivar do CompositionHost.

    class CompositionHostControl : CompositionHost
    
  4. Atualize o construtor para chamar o construtor base.

    public CompositionHostControl() : base()
    {
    
    }
    

Adicionar elementos de composição

Com a infraestrutura implementada, pode agora adicionar conteúdo de Composição à interface da aplicação.

Neste exemplo, adiciona-se código à classe CompositionHostControl que cria e anima um simples SpriteVisual.

  1. Adicione um elemento de composição.

    Em CompositionHostControl.cs, adicione estes métodos à classe 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);
    }
    

Adicione o controlo ao seu formulário

Agora que tens um controlo personalizado para alojar conteúdo de Composição, podes adicioná-lo à interface da aplicação. Aqui, adiciona uma instância do CompositionHostControl que criou no passo anterior. CompositionHostControl é automaticamente adicionado à caixa de ferramentas Visual Studio sob nome do projeto Components.

  1. Na vista Form1.CS design, adiciona um botão à interface.

    • Arrasta um botão da caixa de ferramentas para o Formulário 1. Coloque-o no canto superior esquerdo do formulário. (Veja a imagem no início do tutorial para verificar a colocação dos controlos.)
    • No painel de Propriedades, altere a propriedade Texto de button1 para Adicionar elemento de composição.
    • Redimensiona o botão para que todo o texto apareça.

    (Para mais informações, veja Como: Adicionar Controlos a Windows Forms.)

  2. Adicione um CompositionHostControl à interface.

    • Arraste um CompositionHostControl da caixa de ferramentas para o Form1. Coloca-o à direita do Botão.
    • Redimensione o CompositionHost para que preencha o restante do formulário.
  3. Trata do evento de clique no botão.

    • No painel de Propriedades, clique no ícone de raio para mudar para a vista de Eventos.
    • Na lista de eventos, selecione o evento Click , escreva Button_Click e pressione Enter.
    • Este código é adicionado em Form1.cs:
    private void Button_Click(object sender, EventArgs e)
    {
    
    }
    
  4. Adicione código ao manipulador de clique do botão para criar novos elementos.

    • Em Form1.cs, adicione código ao Button_Click gestor de eventos que criou anteriormente. Este código chama CompositionHostControl1.AddElement para criar um novo elemento com tamanho e deslocamento gerados aleatoriamente. (A instância de CompositionHostControl era automaticamente chamada compositionHostControl1 quando a arrastavas para o formulário.)
    // 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);
    }
    

Agora pode construir e executar a sua aplicação Windows Forms. Quando clicar no botão, deve ver quadrados animados adicionados à interface.

Passos seguintes

Para um exemplo mais completo que se baseia na mesma infraestrutura, veja o exemplo Windows Forms Visual layer integration no GitHub.

Recursos adicionais

Código completo

Aqui está o código completo deste tutorial.

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
}