美文网首页Noesis Gui
教程21:Blend集成

教程21:Blend集成

作者: YottaYuan | 来源:发表于2020-03-15 04:45 被阅读0次

Blend集成教程

教程数据

介绍

什么是Blend?

Microsoft Blend是一种视觉工具,用于创建引人注目的,基于矢量的图形用户界面。您可以通过绘制形状和路径,绘制控件(例如按钮和列表框),使应用程序的各个部分响应鼠标单击和其他用户输入,以及对所有内容进行样式设置以使其看起来独特,来构建界面。

创建界面时,您在Blend的设计图面上看到的是用户在运行应用程序时所看到的。

在Blend中,您可以处理正在运行的应用程序的真实部分,但是仍然可以像在其他插图软件中一样轻松地绘制和设置所有样式,或者从其他流行的矢量图形工具(如Adobe Illustrator)导入设计。当您想要绘制表示交互式控件的内容时,实际上可以选择一个可用的控件(例如按钮或列表框),然后设置其样式或从头开始创建控件。在任何情况下,强大的蒙皮机制都可以完全自定义视觉外观。

Blend会产生什么?

Blend产生了视觉设计,该视觉设计由XAML格式的文本文件表示。就像HTML是Web应用程序的标记语言一样,XAML是Blend应用程序的标记语言。这些XAML文件以及其他资源(例如图像或字体)将成为NoesisGUI应用程序的输入。

本教程基于Microsoft Blend for Visual Studio,并将向您展示如何使用它来设计要由NoesisGUI框架导入和执行的应用程序用户界面。

混合编辑器

支持资产

以下各节描述了由NoesisGUI实现的Blend提供的功能。随着NoesisGUI变得更加成熟,该子集将随着新版本的增加而增加。

控制项

Blend具有可供用户使用的巨大控制面板。目前,NoesisGUI框架中不支持所有控件,但是大多数重要的控件都可以使用。下图显示了当前版本中支持的主要控件的快速视图。有关更多信息,请遵循“ 控制”教程

BlendTutorialImg1.png

面板

面板确定其内容的布局(大小和位置),由其他界面元素形成。仅使用基于静态像素的坐标和基于静态像素的大小来排列用户界面元素可以在有限的环境中工作,但是这些类型的界面无法正确处理不同的屏幕分辨率和尺寸,用户设置(例如字体大小)或内容以无法预测的方式进行更改(例如将文本翻译成不同的语言)。

Blend定义了各种面板,足以避免上述问题并创建任何类型的应用程序。NoesisGUI框架从Blend提供的那些类中实现以下Panel类。可以在“ 布局和面板”教程中找到更多信息。

BlendTutorialImg2.png

形状

形状是一个基本的2D绘图,结合了几何(用于绘制轮廓)和刷子 (用于填充几何形状)。形状可以直接放置在用户界面中,而无需自定义代码或复杂的对象层次结构。形状对象共享以下公共属性。

  • 填充:描述如何绘制形状的内部。
  • 描边:描述如何绘制形状的轮廓。
  • StrokeThickness:描述形状轮廓的厚度。
  • Strecth:描述形状如何拉伸以填充其容器中的可用空间。

NoesisGUI实现以下形状。有关更多信息,请遵循Shapes教程

BlendTutorialImg3.png

注意

实际上,所有形状都受支持,因为所有形状都可以通过单击形状并执行“路径->转换为路径”来转换为路径。如果您需要Noesis不支持的Shape,请采用这种方法。

行为举止

行为是可重用的打包代码段,可以将其拖放到任何对象上,然后通过更改其属性进行微调,以向应用程序添加交互性。行为具有触发器动作和通常是目标:

  • 触发器是调用行为的事件。最有用的触发器是用户界面控制事件(例如单击)以及由系统状态更改(例如情节提要结束或属性更改)调用的事件。
  • 操作指定行为应执行的操作。例如,一项操作可能是运行情节提要或更改元素的属性。
  • 目标是其行为将在采取行动的元素。

以下行为类在NoesisGUI中实现。可以在“ 交互性”教程中找到更多信息。

BlendTutorialImg30.png

特效

效果是简单的像素处理操作。效果将位图源作为输入,并在应用效果后生成新的位图,例如模糊或阴影。每个效果都公开属性来控制它。

效果可以应用于UIElement对象,例如ButtonTextBox。当您将效果设置到布局容器(例如DockPanelCanvas)时,该效果将应用于元素或视觉的视觉树,包括其所有子元素。

NoesisGUI尚未实现支持效果的体系结构。

支持的项目类型

创建新项目时,Blend显示以下对话框:

BlendTutorialImg4.png

Blend支持多种技术:WPFSilverlightUWP。Microsoft设计WPF为开发桌面应用程序提供一致的编程模型。后来创建了Silverlight,以将WPF的功能引入网络。Silverlight定义了一个更简单的运行时,但是与WPF相比,它有一些区别:它实现的控件较少,样式功能(触发器)更有限,但是包含一个新属性,该属性模拟应用于图形元素的3D转换,即Projection属性。

当前,NoesisGUI支持从Blend 创建WPFSilverlight项目。

在本教程中,我们将使用应用程序框架创建功能齐全的应用程序

创建主窗口UI

使用Blend可以在应用程序主窗口中添加和组织控件和图形元素(如果您不熟悉此工具,则应阅读Blend用户指南以了解如何使用它的基础知识)。控件和其他元素可以在“ 资产”选项卡中的“混合”中选择,也可以直接从左侧工具栏上的相应按钮中选择。

经过一些工作,我们得出了类似以下内容的结论:

BlendTutorialImg5.png

控制样式

了解改变元素外观的不同技术很重要。样式模板主题是揭示和掌握XAML必须提供的自定义功能的基本概念。我们建议您阅读有关样式和模板的简介,以及有关此问题的Blend文档。

对于我们的应用程序,我们可以通过添加自定义控件样式来添加个性化外观。为了组织资源,我们将创建一个新的ResourceDictionary并将所有样式定义放在此处:

BlendTutorialImg6.png BlendTutorialImg7.png

我们将通过选择一个窗口控件(例如“ AddButton”)开始样式编辑,然后转到“ 格式”菜单->“ 编辑样式” ->“ 创建空白...”

BlendTutorialImg8.png

然后,我们通过设置一些属性来自定义按钮样式。最后,我们创建按钮模板,以使按钮具有个人外观。

BlendTutorialImg9.png BlendTutorialImg10.png

在创建和编辑我们正在使用的所有控件的样式和模板后,我们的主窗口如下所示:

BlendTutorialImg11.png

用户控件

有时我们需要创建可在其他应用程序中重用的控件。在这种情况下,最好创建一个由标准控件组成的用户控件,以便控件可以吸收使用该控件的应用程序的外观,同时保持操作所需的逻辑不变。现在,我们将为应用程序添加一个UserControl来定义颜色选择器:

BlendTutorialImg6.png BlendTutorialImg12.png

用户控件的外观由标准控件的组成定义。然后,我们添加逻辑来管理颜色选择器,并且该控件已准备好在我们的应用程序中使用:

BlendTutorialImg13.png

用户控件教程中提供了更多详细信息。

字型

NoesisGUI框架允许在您的应用程序中使用本地资源字体。首先,您需要在项目中包含字体:

BlendTutorialImg14.png

然后可以在XAML中将字体引用为本地资源:

<pre style="box-sizing: inherit; overflow: auto; font-family: Consolas, "Courier New", Courier, monospace; font-size: 0.9em; background: rgb(244, 244, 244); border: 1px solid rgb(230, 230, 230); color: rgb(0, 0, 0); margin: 0px; padding: 0.25em;"><Window FontFamily = “字体/#WeblySleek UI常规” >
</pre>

动画

动画可以使有吸引力的用户界面更加壮观和实用。通过仅设置背景颜色动画或应用动画变换,您可以创建戏剧性的屏幕过渡或提供有用的视觉提示。

使用Blend等工具可以更加轻松地完成此过程。接下来是对使用Blend的动画系统的简要介绍。

有关NoesisGUI中动画的更多信息,请参见动画教程。

故事板

通过Blend创建的所有动画均基于名为Storyboard的时间轴。在情节提要板内部,使用影响许多属性的关键帧组将动画更改插入乐谱中,就像在乐谱中一样。动画告诉将要动画的属性,动画中的关键帧指定在一段时间内哪个值将采用属性。

在哪里应用情节提要

故事板可以在以下三种情况下应用:

<Button Content="Button">
  <Button.Triggers>
    <EventTrigger RoutedEvent="Button.Click">
      <BeginStoryboard>
        <Storyboard .../>
      </BeginStoryboard>
    </EventTrigger>
  </Button.Triggers>
</Button>
<Style TargetType="Rectangle">
  <Style.Triggers>
    <EventTrigger RoutedEvent="UIElement.MouseEnter">
      <BeginStoryboard>
        <Storyboard .../>
     </BeginStoryboard>
    </EventTrigger>
  </Style.Triggers>
</Button>
  • 作为对激活或停用属性(在StyleTemplate内)的响应。
<Style TargetType="Rectangle">
  <Style.Triggers>
    <Trigger Property="IsMouseOver">
      <Trigger.EnterActions>
        <BeginStoryboard>
          <Storyboard .../>
        </BeginStoryboard>
      </Trigger.EnterActions>
    </Trigger>
  </Style.Triggers>
</Button>
<ControlTemplate TargetType="Button">
  <Border x:Name="border" .../>
  <ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver">
      <Trigger.EnterActions>
        <BeginStoryboard>
          <Storyboard .../>
        </BeginStoryboard>
      </Trigger.EnterActions>
    </Trigger>
  </ControlTemplate.Triggers>
</ControlTemplate>
  • 处于可见状态(在Template内)。
<ControlTemplate TargetType="Button">
  <Border x:Name="border">
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup x:Name="CommonStates">
        <VisualState x:Name="Normal"/>
        <VisualState x:Name="MouseOver">
          <Storyboard .../>
        </VisualState>
        <VisualState x:Name="Pressed"/>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    ...
  </Border>
</ControlTemplate>

一个简单的动画

让我们以一个简单的Rectangle为其制作一些动画。

BlendTutorialAnimationImg1.png

绘制矩形后,转到“ 对象和时间线”窗口,然后单击右侧的“ 新建”按钮以创建新的Storyboard

BlendTutorialAnimationImg2.png

情节提要命名并创建它。

BlendTutorialAnimationImg3.png

现在,在“ 对象和时间线”窗口中,将显示新创建的情节提要。在时间轴中移动时间栏可设置在动画期间执行属性更改的确切时间

BlendTutorialAnimationImg4.png

创建新动画后,Blend会自动进入录制状态。这意味着在设计窗口或元素属性中所做的任何更改都将被记录到动画时间轴中,就在时间栏所指向的时间中。

BlendTutorialAnimationImg5.png

现在我们可以开始为矩形动画了。我们将在时间0开始将背景颜色更改为红色,然后在1秒钟内开始将颜色逐渐更改为绿色。因此,我们将时间栏移至时间0秒,并在录制按钮仍处于活动状态(红色)时将background属性更改为红色值。background属性的更改在该时间轴位于该确切时刻的位置中,作为新的KeyFrame在时间轴中注册。

BlendTutorialAnimationImg6.png

要插入绿色值,我们将时间栏移至1秒,然后更改矩形的颜色。Blend将在时间轴中将其表示为连续线。

BlendTutorialAnimationImg7.png

一旦一个关键帧被插入时,从以前的一个中的过渡可以被修改选择在时间线窗口中的关键帧,并改变KeySpline的easingFunction

借助KeySpline窗口,可以根据用户需求微调过渡曲线。在此示例中,我们创建了一个过渡,该过渡开始缓慢变化,然后在过渡过程中它加速,最后最终再次缓慢地到达下一个KeyFrame值。

BlendTutorialAnimationImg8.png

使用EasingFunction窗口,用户可以在许多预定义效果之间进行选择,这些效果会影响从上一个KeyFrame到选定的一个(In),新KeyFrame值的输入(Out)或两者(InOut)的退出

BlendTutorialAnimationImg9.png

一些预定义的效果具有可由用户调整的其他值。

BlendTutorialAnimationImg10.png

我们还可以控制动画将要执行的重复次数。为此,我们可以在时间轴上的动画属性上单击鼠标右键。

BlendTutorialAnimationImg11.png

并设置重复次数,或选择“ 永远”,以便动画播放无数

BlendTutorialAnimationImg12.png

最后,我们可以将动画分配给EventTrigger,以便在例如鼠标进入矩形时开始播放。我们可以为此使用“ 触发器”面板。

BlendTutorialAnimationImg13.png

使用VisualStates的动画

所述的VisualState系统是一个功能强大的机制,以基于对象的内部状态的改变和动画性能。状态在每个控件中都进行了硬编码,并且模板元素可以使用在状态之间发生变化时播放的动画对状态做出反应。

对于我们的示例,我们在设计视图中放置一个按钮,然后编辑其模板。该面板会显示这样的事情。

BlendTutorialAnimationImg14.png

选择Normal状态,我们可以创建将用于Button的默认模板。在这种情况下,将绘制一个灰色的圆角矩形。

BlendTutorialAnimationImg15.png

现在我们希望当按钮处于MouseOver状态时,背景颜色变为深灰色。因此,我们选择了MouseOver状态,并且当重新编码按钮处于活动状态时(与创建动画时完全相同),我们可以将椭圆的值更改为蓝色,因此Blend会检测到更改并为MouseOver创建一个新的故事板州。在这种情况下,Blend将MouseOver名称分配给情节提要,因为只有在按钮进入该状态时才执行此名称。

BlendTutorialAnimationImg16.png

VisualStates提供了一种非常简单而强大的动画制作方法。除了为每种状态播放的情节提要板之外,还创建了动态动画以在每种状态下设置的属性值之间进行平滑过渡。可以使用每组VisualState中存在的“ 默认”过渡图标来控制此动态动画的持续时间以及应用于在状态之间进行过渡的曲线。例如,如果我们希望使用Bounce动画曲线的过渡时间为0.5s,则可以输入以下值。

BlendTutorialAnimationImg17.png

甚至可以引入特定状态之间的转换值。这样可以对动画进行更多控制。例如,我们可以为从NormalMouseOver的过渡指定不同的持续时间和效果

BlendTutorialAnimationImg18.png

我们在控件模板中使用了一些动画,以增加应用程序外观的吸引力。

C ++实现

现在,所有设计和艺术品都已完成,我们必须实现逻辑。Blend为添加到项目中的每个项目创建两种类型的文件:XAML文件和通常称为code-behind的C#文件。将创建与XAML中定义的元素匹配的类以及将响应所需事件的方法。用户可以自由地修改此代码,并添加要执行的逻辑以响应事件,甚至可以添加应用程序所需的任何其他方法或属性。可视化定义与逻辑代码分离的强大功能允许高效的工作流程,因为程序员和设计人员可以在每个部分上分别工作。

为了使NoesisGUI正常工作,我们需要将C#代码背后的代码转换为C ++,第一步是创建一个与Blends创建的每个类匹配的Noesis组件

应用

在我们的教程中,我们有一个Application派生类,其中包含后面的App.xaml代码。该文件由Blend自动创建,可以使用Blend代码编辑器进行修改:

App.xaml.cs

using System.Windows;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
    }
}

App.xaml中是指使用此类X:类扩展。而且,正如我们所看到的,它遵循Namespace.ClassName的形式:

应用程式

<Application
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      x:Class="BlendTutorial.App"
      StartupUri="MainWindow.xaml">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>

为了匹配该类,我们必须创建一个C ++组件,该组件派生自Application,并将BlendTutorial.App作为TypeId。

namespace BlendTutorial
{

class App final: public Application
{
    NS_IMPLEMENT_INLINE_REFLECTION(App, Application)
    {
        NsMeta<TypeId>("BlendTutorial.App");
    }
};

}

主窗口

对于MainWindow.xaml文件,Blend创建了一个Window派生类,并为该类管理的每个事件添加了一个事件处理程序:

MainWindow.xaml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BlendTutorial" mc:Ignorable="d"
        x:Class="BlendTutorial.MainWindow"
        x:Name="Window"
        Title="Noesis - Getting started with Blend"
        Width="800" Height="480" Background="Gray" UseLayoutRounding="True"
        FontFamily="Fonts/#WeblySleek UI Normal">

  <Grid x:Name="LayoutRoot">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="8*"/>
      <ColumnDefinition Width="2*"/>
    </Grid.ColumnDefinitions>
    <Border x:Name="ContainerBorder" BorderBrush="DimGray" BorderThickness="1" Margin="10"
            CornerRadius="2" Background="White" Padding="1" ClipToBounds="True"
            PreviewMouseLeftButtonDown="ContainerBorder_MouseDown">
      <Canvas x:Name="ContainerCanvas"/>
    </Border>
    <Viewbox Grid.Column="1" VerticalAlignment="Top">
      <StackPanel Margin="0,10,10,10" Width="150">
        <Button x:Name="AddButton" Content="Add" Margin="0,0,0,3" Click="AddButton_Click"/>
        <Button x:Name="RemoveButton" Content="Remove" Margin="0,0,0,10"
                Click="RemoveButton_Click"/>
        <Expander Header="Position" Margin="0,0,0,10" IsExpanded="True">
          <Grid Margin="5">
            <Grid.ColumnDefinitions>
              <ColumnDefinition/>
              <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition/>
              <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Text="Left " VerticalAlignment="Center" HorizontalAlignment="Right"
                       Margin="0"/>
            <Slider x:Name="PositionLeft" Margin="0,0,0,3" Grid.Column="1"
                    Maximum="{Binding ActualWidth, ElementName=ContainerCanvas}"
                    LargeChange="10" SmallChange="1"/>
            <TextBlock Text="Top " VerticalAlignment="Center" HorizontalAlignment="Right"
                       Grid.Row="1"/>
            <Slider x:Name="PositionTop" Margin="0,0,0,3" Grid.Column="1" Grid.Row="1"
                    Maximum="{Binding ActualHeight, ElementName=ContainerCanvas}"
                    LargeChange="10" SmallChange="1"/>
          </Grid>
        </Expander>
        <Expander Header="Size" Margin="0,0,0,10" IsExpanded="True">
          <Grid Margin="5">
            <Grid.ColumnDefinitions>
              <ColumnDefinition/>
              <ColumnDefinition Width="2*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
              <RowDefinition/>
              <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Text="Width " VerticalAlignment="Center" HorizontalAlignment="Right"
                       Margin="0"/>
            <Slider x:Name="SizeWidth" Grid.Column="2" Margin="0,0,0,3" LargeChange="10"
                    SmallChange="1" Maximum="200" Minimum="10"/>
            <TextBlock Text="Height " VerticalAlignment="Center" HorizontalAlignment="Right"
                       Grid.Row="1"/>
            <Slider x:Name="SizeHeight" Grid.Column="2" Grid.Row="1" LargeChange="10"
                    SmallChange="1" Maximum="200" Minimum="10"/>
          </Grid>
        </Expander>
        <Expander Header="Color" Margin="0,0,0,10" IsExpanded="True">
          <StackPanel Margin="5" Orientation="Vertical" ToggleButton.Checked="RadioButton_Checked">
            <RadioButton x:Name="FillSelected" Content="Fill" IsChecked="True"/>
            <RadioButton x:Name="StrokeSelected" Content="Stroke" Margin="0,0,0,3"/>
            <local:ColorSelector x:Name="ColorSelect"/>
          </StackPanel>
        </Expander>
      </StackPanel>
    </Viewbox>
  </Grid>
</Window>

MainWindow.xaml.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void AddButton_Click(object sender, RoutedEventArgs e)
        {
        }

        private void RemoveButton_Click(object sender, RoutedEventArgs e)
        {
        }

        private void ContainerBorder_MouseDown(object sender, MouseButtonEventArgs e)
        {
        }

        private void RadioButton_Checked(object sender, RoutedEventArgs e)
        {
        }
    }
}

为了匹配该类,我们必须创建一个C ++组件,该组件派生自Window,并且具有BlendTutorial.MainWindow作为TypeId。另外,我们必须连接与MainWindow.xaml文件中定义的事件处理程序匹配的函数:

namespace BlendTutorial
{

class MainWindow final: public Window
{
public:
    MainWindow()
    {
        InitializeComponent();
    }

private:
    void InitializeComponent()
    {
        Noesis::GUI::LoadComponent(this, "MainWindow.xaml");
    }

    bool ConnectEvent(BaseComponent* source, const char* event, const char* handler) override
    {
        NS_CONNECT_EVENT(Button, Click, AddButton_Click);
        NS_CONNECT_EVENT(Button, Click, RemoveButton_Click);
        NS_CONNECT_EVENT(Border, PreviewMouseLeftButtonDown, ContainerBorder_MouseDown);
        NS_CONNECT_ATTACHED_EVENT(ToggleButton, Checked, RadioButton_Checked);
        return false;
    }

    void AddButton_Click(BaseComponent*, const RoutedEventArgs&)
    {
    }

    void RemoveButton_Click(BaseComponent*, const RoutedEventArgs&)
    {
    }

    void ContainerBorder_MouseDown(BaseComponent*, const MouseButtonEventArgs&)
    {
    }

    void RadioButton_Checked(BaseComponent*, const RoutedEventArgs&)
    {
    }

    NS_IMPLEMENT_INLINE_REFLECTION(MainWindow, Window)
    {
        NsMeta<TypeId>("BlendTutorial.MainWindow");
    }
};

XAML文件的命名元素可以在元素初始化期间存储,以方便逻辑实现:

namespace BlendTutorial
{

class MainWindow final: public Window
{
public:
    MainWindow()
    {
        InitializeComponent();
    }

private:
    void InitializeComponent()
    {
        Noesis::GUI::LoadComponent(this, "MainWindow.xaml");

        _containerCanvas = FindName<Canvas>("ContainerCanvas"); NS_ASSERT(_containerCanvas != 0);
        _positionLeft = FindName<Slider>("PositionLeft"); NS_ASSERT(_positionLeft != 0);
        _positionTop = FindName<Slider>("PositionTop"); NS_ASSERT(_positionTop != 0);
        _sizeWidth = FindName<Slider>("SizeWidth"); NS_ASSERT(_sizeWidth != 0);
        _sizeHeight = FindName<Slider>("SizeHeight"); NS_ASSERT(_sizeHeight != 0);
        _fillSelected = FindName<RadioButton>("FillSelected"); NS_ASSERT(_fillSelected != 0);
    }

    Canvas* _containerCanvas;
    Slider* _positionLeft;
    Slider* _positionTop;
    Slider* _sizeWidth;
    Slider* _sizeHeight;
    RadioButton* _fillSelected;
};

颜色选择器

Blend创建的最后一个类是与我们的ColorSelector用户控件关联的

ColorSelector.xaml

<UserControl
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
     x:Class="BlendTutorial.ColorSelector"
     x:Name="ColorSelectorRoot"
     Foreground="{StaticResource BlendTutorialForeground}"
     Slider.ValueChanged="Slider_ValueChanged"
     d:DesignWidth="200" d:DesignHeight="100">

     <Grid x:Name="LayoutRoot">
         <Grid.RowDefinitions>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
             <RowDefinition Height="*"/>
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="1*"/>
             <ColumnDefinition Width="2*"/>
         </Grid.ColumnDefinitions>
         <Rectangle RadiusX="2" RadiusY="2" Grid.RowSpan="4" Margin="0,0,5,0">
             <Rectangle.Fill>
                 <LinearGradientBrush StartPoint="0,0" EndPoint="0,15" MappingMode="Absolute"
                                      SpreadMethod="Repeat">
                     <GradientStop Color="Gray" Offset="0"/>
                     <GradientStop Color="Silver" Offset="0.5"/>
                     <GradientStop Color="White" Offset="0.5001"/>
                     <GradientStop Color="Gainsboro" Offset="1"/>
                 </LinearGradientBrush>
             </Rectangle.Fill>
         </Rectangle>
         <Rectangle x:Name="ColorRect" Fill="{Binding Color, ElementName=ColorSelectorRoot}"
                    Stroke="{StaticResource BlendTutorialBorderBrush}" RadiusX="2" RadiusY="2"
                    Grid.RowSpan="4" Margin="0,0,5,0"/>
         <Slider x:Name="R" Grid.Column="1" Grid.Row="0" VerticalAlignment="Center"
                 Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="G" Grid.Column="1" Grid.Row="1" VerticalAlignment="Center"
                 Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="B" Grid.Column="1" Grid.Row="2" VerticalAlignment="Center"
                 Margin="10,1,0,1" Maximum="255" LargeChange="10" SmallChange="1"/>
         <Slider x:Name="A" Grid.Column="1" Grid.Row="3" VerticalAlignment="Center"
                 Margin="10,1,0,1" Maximum="255" SmallChange="1" LargeChange="10" Value="255"/>
         <TextBlock Text="R" Grid.Column="1" HorizontalAlignment="Left"
                    VerticalAlignment="Center"/>
         <TextBlock Text="G" Grid.Column="1" HorizontalAlignment="Left"
                    VerticalAlignment="Center" Grid.Row="1"/>
         <TextBlock Text="B" Grid.Column="1" HorizontalAlignment="Left"
                    VerticalAlignment="Center" Grid.Row="2"/>
         <TextBlock Text="A" Grid.Column="1" HorizontalAlignment="Left"
                    VerticalAlignment="Center" Grid.Row="3"/>
     </Grid>
 </UserControl>

ColorSelector.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace BlendTutorial
{
    /// <summary>
    /// Interaction logic for ColorSelector.xaml
    /// </summary>
    public partial class ColorSelector : UserControl
    {
        public ColorSelector()
        {
            this.InitializeComponent();
        }

        public static DependencyProperty ColorProperty = DependencyProperty.Register("Color",
            typeof(SolidColorBrush), typeof(ColorSelector),
            new PropertyMetadata(null, new PropertyChangedCallback(OnColorChanged)));

        public SolidColorBrush Color
        {
            get { return (SolidColorBrush)GetValue(ColorProperty); }
            set { SetValue(ColorProperty, value); }
        }

        private static void OnColorChanged(object sender,
            System.Windows.DependencyPropertyChangedEventArgs e)
        {
        }

        private void Slider_ValueChanged(object sender,
            System.Windows.RoutedPropertyChangedEventArgs<double> e)
        {
        }
    }
}

为了匹配该用户控件,我们必须创建一个C ++组件,该组件从UserControl派生并且具有BlendTutorial.ColorSelector作为TypeId。我们必须连接管理滑块中的更改的功能,并注册由ColorSelector定义的依赖项属性。

namespace BlendTutorial
{

class ColorSelector final: public UserControl
{
public:
    ColorSelector()
    {
        InitializeComponent();
    }

    SolidColorBrush* GetColor() const
    {
        return GetValue<Ptr<SolidColorBrush>>(ColorProperty);
    }

    void SetColor(SolidColorBrush* color)
    {
        SetValue<Ptr<SolidColorBrush>>(ColorProperty, color);
    }

public:
    static const DependencyProperty* ColorProperty;

private:
    void InitializeComponent()
    {
        Noesis::GUI::LoadComponent(this, "ColorSelector.xaml");
    }

    bool ConnectEvent(BaseComponent* source, const char* event, const char* handler) override
    {
        NS_CONNECT_EVENT(Slider, ValueChanged, Slider_ValueChanged);
        return false;
    }

    static void OnColorChanged(DependencyObject* d, const DependencyPropertyChangedEventArgs& /*e*/)
    {
    }

    void Slider_ValueChanged(BaseComponent*, const RoutedPropertyChangedEventArgs<float>&)
    {
    }

    NS_IMPLEMENT_INLINE_REFLECTION(ColorSelector, UserControl)
    {
        NsMeta<TypeId>("BlendTutorial.ColorSelector");

        UIElementData* data = NsMeta<UIElementData>(TypeOf<SelfClass>());
        data->RegisterProperty<Ptr<SolidColorBrush>>(ColorProperty, "Color",
            PropertyMetadata::Create(Brushes::Transparent()->Clone(),
                &ColorSelector::OnColorChanged));
    }
};

相关文章

网友评论

    本文标题:教程21:Blend集成

    本文链接:https://www.haomeiwen.com/subject/tfekshtx.html