• 【Win 10 应用开发】UI Composition 札记(三):与 XAML 集成


    除了 DirectX 游戏开发,我们一般很少单独使用 UI Composition ,因此,与 XAML 互动并集成是必然结果。这样能够把两者的优势混合使用,让UI布局能够更灵活。

    说到与 XAML 的集成,则我们必须先认识一位伙计,他非常重要,位于 Windows.UI.Xaml.Hosting 命名空间下,名叫 ElementCompositionPreview ,有了它,我们才可以在 XAML 元素与 Composition UI 元素之间游走。来看看它都公开了哪些成员。

        public sealed class ElementCompositionPreview : IElementCompositionPreview
        {
    
            public static void SetImplicitShowAnimation(UIElement element, ICompositionAnimationBase animation);
    
            public static void SetImplicitHideAnimation(UIElement element, ICompositionAnimationBase animation);
    
            public static void SetIsTranslationEnabled(UIElement element, bool value);
    
            public static CompositionPropertySet GetPointerPositionPropertySet(UIElement targetElement);
    
            public static Visual GetElementVisual(UIElement element);
    
            public static Visual GetElementChildVisual(UIElement element);
    
            public static void SetElementChildVisual(UIElement element, Visual visual);
    
            public static CompositionPropertySet GetScrollViewerManipulationPropertySet(ScrollViewer scrollViewer);
        }

    这个类公开的方法都是静态的,无需实例调用。我们不要急于弄懂每个方法的作用,这样会把自己带入死胡同。此处,我们先重点掌握以下几个方法的用法。

    1、GetElementVisual:要使 XAML 元素与 Composition API 交互,这个方法特别要紧,通过调用它,我们可以得到 XAML 元素中的 Composition UI 树的表示元素,并且能获取到关联的 Compositor 对象,有了关联的 Compositor实例,我们才能创建各种 UI 元素。

    2、SetElementChildVisual:当我们使用 Composition API 创建完自定义的 UI 元素后,要调用这个方法把它插入到 XAML 对象的可视化树中。注意,UI 元素总是被插入到可视化树的最后一个位置,因此,我们自己组装的元素总是会挡住原来的 XAML 元素的。这个你得注意。

    3、GetElementChildVisual:只有你调用过 SetElementChildVisual 方法把自定义 Visual 元素加入过可视化树,才能调用这个方法,这个方法就是返回你上一次插入的元素。如果你没有插入过自定义的可视化元素,则该方法会返回 null。注意,不要把这个方法跟 GetElementVisual 方法混淆,两者不同。GetElementVisual 方法是获得与目标 XAML 对象关联的可视化对象,而 GetElementChildVisual 方法是获取你上一次插入到可视化树的自定义元素。

    下面给大伙看一个例子。

    在页面上,我放了一个 Border 对象。

        <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <Border Name="bd" Background="Black"/>
        </Grid>

    顺便把它的背景设置为黑色,方便后面看电影。

    切换到代码文件,现在我们自定义组装一下UI,然后插入到 Border 元素的子树中。

            public MainPage()
            {
                this.InitializeComponent();
                CompositMyVisual();
            }
    
    
            void CompositMyVisual()
            {
                Visual visualForbd = ElementCompositionPreview.GetElementVisual(bd);
                // 组建UI
                Compositor compos = visualForbd.Compositor;
                // 根元素
                ContainerVisual rootvs = compos.CreateContainerVisual();
                // 第一个可视化元素
                SpriteVisual v1 = compos.CreateSpriteVisual();
                v1.Brush = compos.CreateColorBrush(Colors.Green);
                v1.Size = new Vector2(450f, 300f); //大小
                v1.Offset = new Vector3(30f, 20f, -1f); //位移
                rootvs.Children.InsertAtTop(v1);
                // 第二个可视化元素
                SpriteVisual v2 = compos.CreateSpriteVisual();
                v2.Brush = compos.CreateColorBrush(Colors.SkyBlue);
                v2.Size = new Vector2(400f, 400f);
                v2.Offset = new Vector3(180f, 125f, 0f);
                rootvs.Children.InsertAtTop(v2);
    
                // 这一句很重要
                ElementCompositionPreview.SetElementChildVisual(bd, rootvs);
            }

    记得,在组装完UI元素后,要调用 ElementCompositionPreview.SetElementChildVisual方法,把自定义的元素插入到可视化树中。

    在前面的博文中,老周介绍过,ContainerVisual 是个容器元素,它公开一个 Children 集合,我们可以向其中添加子元素,这里头有四个方法我们可以调用。

    1、InsertAtTop :子元素会始终位于其他元素的顶部,所以这个元素会遮挡住其他元素。

    2、InsertAtBottom:所添加的子元素始终在显示层的底部,它会被其他元素遮挡。

    3、InsertAbove:把当前子元素放到某个元素之上。比如我们可以明确指定让当前元素位于 A 元素之上,这使得当前元素可能遮挡住 A 元素。

    4、InsertBelow:把当前元素放到某个元素的下方。如果当前元素放在 A 元素下面,那么,该元素可能被 A 元素遮挡。

    我们看看这个例子的效果。

     在上面的代码中,第一个元素是绿色画刷填充的,第二个是天蓝色的,而我们在插入元素树时,都把它们放到所有元素的顶部。

        rootvs.Children.InsertAtTop(v1);
        rootvs.Children.InsertAtTop(v2);

    都位于顶部,那么后加上去的元素就会挡住前面的元素,所以我们看到,天蓝色的那块区域挡住了绿色区域的一部分。

    使用这种交互,我们还可以很轻松地对 XAML 元素进行三维旋转。

    这个例子的界面分为两部分。左边咱们放三个滑块,分别调节 XAML 对象在 X、Y、Z 轴上的旋转角度,范围在 -90 到 90 之间。

                <Slider Name="sldX" Header="X 轴" Maximum="90" Minimum="-90" SmallChange="1" StepFrequency="1" Value="0" ValueChanged="OnSliderValChanged"/>
                <Slider Name="sldY" Header="Y 轴" Maximum="90" Minimum="-90" SmallChange="1" StepFrequency="1" Value="0" ValueChanged="OnSliderValChanged"/>
                <Slider Name="sldZ" Header="Z 轴" Maximum="90" Minimum="-90" SmallChange="1" StepFrequency="1" Value="0" ValueChanged="OnSliderValChanged"/>

    界面的右边是一个矩形。

      <Rectangle Grid.Column="1" Width="300" Height="300" Fill="Brown" Name="rect"/>

    我们就是要让这个矩形进行三维旋转。

    定义一个方法,从以上三个 Slider 控件中获得实时的值,然后改变矩形的三维方向(在三个轴上的旋转角度)。

            void SetUI()
            {
                Visual v = ElementCompositionPreview.GetElementVisual(rect);
                // 设置方向
                float x = (float)sldX.Value;
                float y = (float)sldY.Value;
                float z = (float)sldZ.Value;
                Quaternion q = new Quaternion(x, y, z, 1f);
                v.Orientation = q;
            }

    一个 Quaternion 实例包含四个值,前三个就是三个坐标轴上旋转的值,那个 W 不管它,始终给它分配 1 就行了。

    来,看看效果。

    这个示例的源代码可以 点击这里下载

    好,本篇咱们就聊到这里,下一篇老周再介绍一下如何用 Win 2D 组件在 XAML 元素上画点东东,可以实现 WPF 中自定义 Renderer 的效果。

  • 相关阅读:
    linux系统缓存机制
    信号“未决”与“阻塞”
    异步I/O
    Unix下五种IO模型
    【设计模式
    【设计模式
    【设计模式
    【设计模式
    【设计模式
    【设计模式
  • 原文地址:https://www.cnblogs.com/tcjiaan/p/7798041.html
Copyright © 2020-2023  润新知