WPF(三)

23 程序集资源

	WPF中的程序集资源基本概念是为项目添加文件,从而VS可将其嵌入到编译过的
应用程序的exe或dll文件中

添加资源

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
(注意:资源文件的属性中的生成操作(build action)要设置为Resource)

内容文件

资源文件有时是不方便的:
	希望改变文件,但不想重新编译程序
	资源文件非常大
	资源文件时可选的
	资源文件是声音文件
将未编译的文件专门标记为内容文件:
	方法:将文件的生成操作属性换成内容,将复制到输出目录设置为总是复制

24 将元素绑定到一起

数据绑定的最简单情形是,源对象是WPF元素而且源属性是依赖项属性,当在源对象中
改变依赖项属性的值时,会立即更新目标对象中的绑定属性。

绑定表达式

当使用数据绑定是,不必使用源对象做任何改动,只需要配置源对象使其属性具有正确的值的范围。
绑定表达式例子:
 FontSize="{Binding ElementName=slider1,Path=Value,Mode=TwoWay}"
数据绑定表达式使用XAML标记扩展(因此具有花括号),因为正在创建System.Windows.Data.Binding类的一个实例,所以绑定表达式以单词Binding开头。尽管
有很多种方法配置Binding对象,但在本例中只需要设置两个属性:ElementName属性和Path属性

绑定错误

绑定模式

数据绑定的一个特性是目标会被自动更新,而不考虑源的修改方式。
可采用一种方式强制在两个方向上传递数值:从源到目标以及从目标到源。
技巧是设置Binding对象的Mode属性。
当设置Bingding.Mode属性时,WPF允许使用5个System.Windows.Data.BindingMode枚举值中任何一个。

在这里插入图片描述

使用代码创建绑定

基于标记的绑定比通过代码创建的绑定更常见
创建绑定:

Binding binding=new Binding();
binding.Source=slider;
binding.Path=new PropertyPath("value");
binding.Mode =BindingMode.TwoWay;
lblSampleText.SetBinding(TextBlock.FontSize,binding);

删除绑定:
BindingOperations.ClearAllBindings(lblSampleText);

多绑定

Q:一个属性如何绑定到两个元素
A::利用Mode(TwoWay)

绑定更新

当使用OneWay或者TwoWay绑定时,改变或的值会立即从源传播到目标。
然而,反向的变化传递----从目标到源----未必会立即放生。他们的行为由Binding.UpdateSourceTrigger属性控制。

在这里插入图片描述
例子:
(代码:WpfApp8_1)
在这里插入图片描述

25 绑定到非元素对象

在数据驱动的应用程序中,更常见的情况是创建从不可见对象中提取数据的绑定表达式。
唯一的要求是希望显示的信息必须存储在公有属性中。WPF数据绑定基础结构不能获取私有信息或公有字段。
当绑定到非元素对象时,需要放弃Binding ElementName属性,并使用以下属性中的一个:Sourse; RelativeSource;DataContext

Sourse属性

Source属性的唯一问题似乎为了研究绑定,需要有数据对象。
可从资源中提取数据对象,可通过编写代码生成数据对象,也可在数据提供程序的帮助下获取数据对象。
最简单的选择时将Source只想一些已经准备好了 的静态对象:
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}"><TextBlock>
另一种选择时绑定到先前作为资源创建的对象。例如,下面的标记创建只想Calibri字体的FontFamiy对象:
<Window.Resources>
	<FontFamily x:Key="CustomFont">Calibri</FontFamily>
</Window.Resources>
绑定:
<TextBlock Text="{Binding Source={StaticResource CustomFont},Path=Source}"></TextBlock>

RelativeSource属性

通过RelativeSource属性可根据相对于目标对象的关系指向源对象。例如,可使用RelativeSource属性将元素绑定到自身或其父元素
为了设置Binding.RelativeSource属性,需要使用RelativeSource对象。
一种选择时使用属性设置语法而不是使用Binding标记扩展。
下面是创建一个Binding对象。
	<TextBlock>
		<TextBlock.Text>
			<Binding Path="Title">
				<Binding.RelativeSource>
					<relativeSource Mode="FindAncestor" AncestorType="{x:Type Window}"/>
				</Binding.RelativeSource>
			</Binding >	
		</TextBlock.Text>
	</TextBlock>

DataContext 属性

当大量元素绑定到同一个对象,可为每个元素使用Sources属性,但会使标记变得非常长。
对于这种情况使用FrameworkElement.DataContext属性一次性定义绑定源会更清晰。
如为包含所有TextBlock元素的StackPannel面板设置DataContext属性是合理的
可使用和设置Binding.Source属性相同的方法设置元素的DataContext属性。
<StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">

现可通过省略源信息来精简绑定表达式

<TextBlock Margin="5" Text="{Binding Path=Source}"></TextBlock>
当在绑定表达式中省略源信息时,WPF会检查元素的DataContext属性。如果属性为null,WPF会继续向上在元素树中查找第一个不为null的数据上下文。

26 资源基础

WPF资源

WPF资源系统是一种保管一系列有用对象的简单方法,从而使你可以更容易地重用这些对象。WPF允许在代码中以及在标记中的各个位置定义资源。

资源集合

每个元素都有Resources属性,该属性存储了一个资源字典集合(它是ResourcesDictionary 类的
实例)。资源集合可以包含任意类型的对象,并根据字符串编写索引。
尽管每个元素都提供了Resources属性,但通常在窗口级别定义资源,这是因为每个元素
都可以访问各自资源集合中的资源,也可以访问所有父元素的资源集合中的资源。
下面在窗口的资源中定义图像画刷:
<Window.Resources>
	<ImageBrush x:Key="TileBrush" TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 32 32" ImageSource="happyface.jpg" Opacity="0.3">
	</ImageBrush>
</Window.Resources>
重要的是第一个特性,即Key特性。
为了使用XAML标记中的资源,需要一种引用资源的方法。这是通过标记扩展完成的。
下面是使用画刷资源的按钮:
<Button Background="{StaticResource TileBrush}"
	Margin="5" Padding="5" FontWeight="Bold" FontSize="14">
	A Tile Button
</Button>
可使用动态资源执行相同的操作:
<Button Background="{DynamicResource TileBrush}"

资源层次

为了找到期望的资源,WPF在元素树中进行递归搜索。
当使用静态资源是,必须总是在引用资源之前在标记中定义资源。
只要不在同一集合中多次使用相同的资源名,就可以重用资源名称。

静态资源和动态资源

区别在于静态资源只从资源集合中获取对象一次。根据对象类型(以及对象使用方式),
对象的任何变化都可能被立即注意到。然而,动态资源在每次需要第项时都会重新从资源集合中查找对象。这意味着可在同一键下放置一个全新对象,
而且动态资源会应用该变化。
实例:
	this.Resource["TileBrush"]=new  SoildColorBrush(Colors.LightBlue);
动态资源会应用该变化,而静态资源不知道它的画刷已在Resource集合中被其他内容替换,它仍继续使用原来的ImageBrush.
通常不需要使用动态资源,除非:
	资源具有以来于系统设置的属性
	准备通过编程方式替换资源对象

通过代码访问

可通过名称从资源集合中提取资源。为此,需要使用正确元素的资源集合。对于标记没有这一限制。
示例:
private void cmdChange_Click(object sender ,RoutedEventArgs e)
{
	Button cmd =(Button)sender;
	ImageBrush brush=(ImageBrush)sender.FindResource("TileRrush")
}

应用程序资源

窗口不是查找资源的最后一站。如果在控件或其容器中(直到包含窗口或者页面)找不到指定的资源,WPF会继续检查为应用程序定义的资源集合。在Visual Studio中,这些资源时在App.xmal文件的标记中定义的资源。

系统资源

动态资源主要用于辅助应用程序对系统环境设置的变化做出响应,为此需要三个类 SystemColors,SystemFonts,SystemParameters,这些类
都位于System.Windows名称空间中。
示例:
label.Foreground=new SoildBrush(SystemColors.WindowTextColor);

例子:
(代码WpfApp10_1)
在这里插入图片描述

27 资源字典

资源字典出现的初衷就在于可以实现多个项目之间的共享资源,资源字典只是一个简单
的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情。

创建资源字典

创建资源字典的过程比较简单,只是将需要使用的资源全都包含在一个xaml文件之中即
可。如下面的例子(文件名test.xaml,与后面的app.xaml文件中的内容相对应):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <LinearGradientBrush x:Key="FadeBrush">
       <GradientStop Color="Red" Offset="0"/>
       <GradientStop Color="Gray" Offset="1"/>
    </LinearGradientBrush>
</ResourceDictionary>
  说明:在创建资源的时候要确保资源文件的编译选项为page,这样就能够保证XAML
  资源文件最终能够编译为baml文件。但是如果设置为Resource也是一个不错的选择,
  这样它能够嵌入到程序集中,但是不被编译,当然其解析的速度回稍微慢一点。

使用资源字典

**集成资源:**
	 要是用资源字典,首先要将资源字典集成到应用程序的某些资源集合中。一般的做
	 法都是在app.xaml文件中进行集成。代码如下:
<ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Test.xaml"/>
            </ResourceDictionary.MergedDictionaries>
 </ResourceDictionary>
说明:上面的标记通过明确地创建一个ResourceDictionary对象进行工作,资源集合总
是ResourceDictionary对象。但是这只是需要明确指定细节从而可以设定ResourceDictionary.MergedDictionaries属性的一种情况。
如果没有这个步骤ResourceDictionary.MergedDictionaries属性将为空。
ResourceDictionary.MergedDictionaries属性是一个ResourceDictionary对象的集合,可以使用这个集合提供自己需要使用的资源的集合。也就是说如果需要某个资源,只需要将与该资源相关的xaml文件
。添加到这个属性中即可。如上面添加test.xaml一样。

**使用资源:**
	集成之后就可以在当前的工程中使用这些资源了。使用方法如下:
<Window x:Class="HelloWpf.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="340" Width="406" WindowStartupLocation="CenterScreen"
        Icon="SC.ico" >
    <Grid Height="304" Width="374">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Button Margin="121,30,107,230" Grid.Row="2" Click="Button_Click"  Background="{StaticResource FadeBrush}">
        </Button>
    </Grid>
</Window>
使用资源的方法比较简单只需要使用StaticResource 关键字去添加即可。
使用资源字典的主要原因有两个:
	  提供皮肤功能。
 	 存储需要被本地话的内容(错误消息字符串等,实现软编码)

在程序集之间共享资源

28 样式基础

样式基础

样式(Style)是组织和重用格式化选项的重要工具。不是使用重复的标记填充XAML,以便设置外边距、内边距、颜色以及字体等细节,而是创建一系列封装所有这些细节的样式,然后再需要之处通过属性来应用样式。
样式是可应用于元素的属性值集合。使用资源的最常见原因之一就是保存样式。
使按钮具有统一格式的实现方式一:资源
<Window.Resources>
        <FontFamily x:Key="ButtonFontFamily">Times New Roman</FontFamily>
        <sys:Double x:Key="ButtonFontSize">18</sys:Double>
        <FontWeight x:Key="ButtonFontWeight">Bold</FontWeight>
</Window.Resources>
 <Grid Name="gird1" ShowGridLines="True">
        <Button FontFamily="{StaticResource ButtonFontFamily}" FontSize="{StaticResource ButtonFontSize}" FontWeight="{StaticResource ButtonFontWeight}"> 
        </Button>
 </Grid>
这个示例可以正常工作,它将字体细节(所谓的magic number)移出的标记。但也存在两个问题。
除了资源的名称相似之外,没有明确指定三个资源是相关的。这使维护应用程序变得复杂。
需要使用资源的标记非常繁琐。还没有原来不使用资源时简明。
样式为解决这个问题提供了非常好的解决方案。可独立的用户封装所有希望设置的属性的样式。
<Window.Resources>
        <Style x:Key="BigFontButtonStyle">
            <Setter Property="Control.FontFamily" Value="Times New Roman"/>
            <Setter Property="Control.FontSize" Value="18"/>
            <Setter Property="Control.FontWeight" Value="Bold"/>
        </Style>
 </Window.Resources>
 
 <Grid Name="gird1" ShowGridLines="True">
        <Button Style="{StaticResource BigFontButtonStyle}"> 
        </Button>
 </Grid>
通过样式系统不仅可以创建多组明显相关的属性设置,而且使得应用程序这些设置更加容易,从而精简了标记。

创建样式对象

设置属性

每个Style对象都封装了一个Setter对象的集合。每个Setter对象设置元素的单个属性。在某些情况下不能使用简单的特性字符设置属性值。可使用嵌套的元素代替如:
 <Setter Property="Control.Background">
        <Setter.Value>
            <ImageBrush  TileMode="Tile" ViewboxUnits="Absolute" Viewport="0 0 32 32" ImageSource="" Opacity="0.3"></ImageBrush>
         </Setter.Value>
</Setter>

创建只应用按钮的样式:

  <Style x:Key="BigFontButtonStyle" TargetType="Button">
            <Setter Property="FontFamily" Value="Times New Roman"/>
            <Setter Property="FontSize" Value="18"/>
            <Setter Property="FontWeight" Value="Bold"/>
  </Style>

关联事件处理程序

<Window.Resources>
        <Style x:Key="MouseOverHighlightStyle">
            <EventSetter Event="TextBlock.MouseEnter" Handler="element_MouseEnter"></EventSetter>
            <EventSetter Event="TextBlock.MouseLeave" Handler="element_MouseLeave"></EventSetter>
        </Style>
</Window.Resources>
<Grid Name="gird1" ShowGridLines="True">
        <TextBlock Style="{StaticResource MouseOverHighlightStyle}"></TextBlock>
</Grid>

多层样式

每个WPF元素一次只能使用一个样式对象,这像是一种限制,但由于属性值的继承和样式继承特性,这种限制实际是不存在的。
例如,希望为一组控件使用相同的字体,又不想为每个控件应用相同的样式。对于这种情况,可将他们放置到面板中,并设置容器的样式。
只要设置的属性具有属性值继承特性,这些值就会传递到子元素。使用这种模型的属性包括IsEnabled、IsVisible、Foreground以及所有字体属性。
另外一些情况,可能希望在另一个样式的基础上建立样式,可用BasedOn特性。
<Window.Resources>
        <Style x:Key="BigFontButtonStyle">
            <Setter Property="Control.FontFamily" Value="Times New Roman"/>
            <Setter Property="Control.FontSize" Value="18"/>
            <Setter Property="Control.FontWeight" Value="Bold"/>
        </Style>
        <Style x:Key="NewBigButtonBigFontStyle" BasedOn="{StaticResource BigFontButtonStyle}">
            <Setter Property="Control.Foreground" Value="Red"/>
            <Setter Property="Control.Background" Value="DarkBlue"/>
        </Style>
</Window.Resources>
<Grid Name="gird1" ShowGridLines="True">
        <TextBlock Style="{StaticResource NewBigButtonBigFontStyle}">test</TextBlock>
</Grid>

通过类型自动应用样式

上面都是具有名称的样式,还有一种为特定类型元素自动应用的样式
下面的例子中第二个按钮显示替换了样式,将style设置为null,有效的删除了样式。
<Window.Resources>
        <Style TargetType="Button">
            <Setter Property="Control.FontFamily" Value="Times New Roman"/>
            <Setter Property="Control.FontSize" Value="18"/>
            <Setter Property="Control.FontWeight" Value="Bold"/>
        </Style>
</Window.Resources>
<Grid Name="gird1" ShowGridLines="True">
        <Button>one</Button>
        <Button Style="{x:Null}">two</Button>
        <Button>three</Button>
</Grid>

示例:
(代码:WpfApp11_1)
在这里插入图片描述

29 触发器

使用触发器可自动完成简单的样式的改变,不需要使用代码,也可以完成不少工作。
触发器通过Style.Trigger集合链接到样式。
每个样式可以有任意多个触发器。每个触发器都是System.Windows.TriggerBase的实例。							

简单触发器

可为任何依赖项属性关联简单触发器。例如,可通过相应Control类的IsFocused、IsMouseOver以及IsPressed属性的变化,创建鼠标悬停效果和焦点效果。
每个触发器都制定了正在监视的属性以及正在等待的属性值。当属性值出现时,将应用Trigger.Setters集合里的设置器。
<Window.Resources>
        <Style x:Key="BigFontButton">
            <Style.Setters>
                <Setter Property="Control.FontFamily" Value="Times New Roman"/>
                <Setter Property="Control.FontSize" Value="18"/>
                <Setter Property="Control.FontWeight" Value="Bold"/>
            </Style.Setters>
            <Style.Triggers>
                <Trigger Property="Control.IsFocused" Value="True">
                    <Setter Property="Control.Foreground" Value="DarkRed"/>
                </Trigger>
            </Style.Triggers>
        </Style>
</Window.Resources>
触发器的优点是不需要为翻转他们而编写任何代码。
如果希望几个条件都满足时才激活触发器,可使用MultiTrigger	
<Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Property="Control.IsFocused" Value="true"/>
                        <Condition Property="Control.IsMouseOver" Value="true"/>
                    </MultiDataTrigger.Conditions>
                    <MultiDataTrigger.Setters>
                        <Setter Property="Control.Foreground" Value="DarkRed"/>
                    </MultiDataTrigger.Setters>
           		</MultiDataTrigger>
</Style.Triggers>

事件触发器

普通触发器等待属性发生变化,而事件触发器等待特定事件被引发。事件触发器要求用户提供一系列修改控件的动作。
这些动作常用于动画。如监听MouseEnter事件,然后动态改变按钮的FontSize属性从而形成动画效果,在0.2秒的时间内容字体放大到22个单位。
与属性触发器不同如果希望元素返回原始状态,需要反转事件触发器。如上面例子,需要编写MouseLeav事件的触发器。不需要指明字体的大小,默认会恢复成第一次动画之前的字体大小。
<Window.Resources>
        <Style x:Key="BigFontButton">
            <Style.Setters>
                <Setter Property="Control.FontFamily" Value="Times New Roman"/>
                <Setter Property="Control.FontSize" Value="18"/>
                <Setter Property="Control.FontWeight" Value="Bold"/>
            </Style.Setters>
            <Style.Triggers>
                <EventTrigger RoutedEvent="Mouse.MouseEnter">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Duration="0:0:3" Storyboard.TargetProperty="FontSize"
                                                 To="50"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
                <EventTrigger RoutedEvent="Mouse.MouseLeave">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Duration="0:0:3" Storyboard.TargetProperty="FontSize"/>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid Name="gird1" ShowGridLines="True">
        <Button HorizontalAlignment="Center" VerticalAlignment="Center" Style="{StaticResource BigFontButton}">onesfsfsfsfsdfsdf</Button>
    </Grid>

示例:
(代码:WpfApp11_2)
在这里插入图片描述

30 行为

行为

行为旨在封装一些UI功能,从而可以不用编写代码就能够将其应用到元素上。 

获取行为支持

添加对System.Windows.Interactivity.dll的引用

创建行为

然后创建一个继承自Behavior基类的类。在此类中覆盖OnAttached()和OnDetaching()方法,
在这两个方法中可以通过AssociatedObject属性访问放置行为的元素。在OnAttached()方法中关联事件处理程序,在OnDetaching()中移除事件处理程序。
 以下是创建一个可以为任意元素提供使用鼠标在Canvas面板上拖动元素的行为类 DragInCanvasBehavior: 
	using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Windows.Media;

namespace CustomBehaviorsLibrary
{
    public class DragInCanvasBehavior : Behavior<UIElement>
    {
        private Canvas canvas;

        protected override void OnAttached()
        {
            base.OnAttached();

            // Hook up event handlers.            
            this.AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove += AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp += AssociatedObject_MouseLeftButtonUp;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Detach event handlers.
            this.AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
            this.AssociatedObject.MouseMove -= AssociatedObject_MouseMove;
            this.AssociatedObject.MouseLeftButtonUp -= AssociatedObject_MouseLeftButtonUp;
        }

        // Keep track of when the element is being dragged.
        private bool isDragging = false;

        // When the element is clicked, record the exact position
        // where the click is made.
        private Point mouseOffset;

        private void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Find the canvas.
            if (canvas == null) canvas = VisualTreeHelper.GetParent(this.AssociatedObject) as Canvas;

            // Dragging mode begins.
            isDragging = true;

            // Get the position of the click relative to the element
            // (so the top-left corner of the element is (0,0).
            mouseOffset = e.GetPosition(AssociatedObject);

            // Capture the mouse. This way you'll keep receiveing
            // the MouseMove event even if the user jerks the mouse
            // off the element.
            AssociatedObject.CaptureMouse();
        }

        private void AssociatedObject_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                // Get the position of the element relative to the Canvas.
                Point point = e.GetPosition(canvas);

                // Move the element.
                AssociatedObject.SetValue(Canvas.TopProperty, point.Y - mouseOffset.Y);
                AssociatedObject.SetValue(Canvas.LeftProperty, point.X - mouseOffset.X);
            }
        }

        private void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (isDragging)
            {
                AssociatedObject.ReleaseMouseCapture();
                isDragging = false;
            }
        }
    }
}

DragInCanvasBehavior

使用行为

为测试行为,创建一个新的WPF应用程序项目。然后添加对定义DragInCanvasBehavior类的类库以及System.Windows.Interactivity.dll程序集得应用。接下来在XML中映射这两个标记名称。假定存储DragInCanvasBehavior类的类库名为CustomBehaviorsLibrary,所需的标记如下所示:
<Window x:Class="BehaviorTest.DragInCanvasTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:custom="clr-namespace:CustomBehaviorsLibrary;assembly=CustomBehaviorsLibrary"
        Title="DragInCanvasTest" Height="300" Width="300">
 为使用该行为,只需要使用Interaction.Behaviors附加属性在Canvas面板中添加任意元素。下面的标记创建一个具有三个图形的Canvas面板。
 两个Ellipse元素使用了DragInCanvasBehavior,并能在Canvas面板中拖动。Rectangle元素没有使用DraginCanvasBehavior,因此无法移动。
<Canvas>
        <Rectangle Canvas.Left="10" Canvas.Top="10" Fill="Yellow" Width="40" Height="60"></Rectangle>
        <Ellipse Canvas.Left="10" Canvas.Top="70" Fill="Blue" Width="80" Height="60">
            <i:Interaction.Behaviors>
                <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
            </i:Interaction.Behaviors>
        </Ellipse>
        <Ellipse Canvas.Left="80" Canvas.Top="70" Fill="OrangeRed" Width="40" Height="70">
            <i:Interaction.Behaviors>
                <custom:DragInCanvasBehavior></custom:DragInCanvasBehavior>
            </i:Interaction.Behaviors>
        </Ellipse>
    </Canvas>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值