ControlTemplate(控件模板)   https://blog.csdn.net/qq_23018459/article/details/79899838

WPF包含数据模板和控件模板,其中控件模板又包括ControlTemplate和ItemsPanelTemplate,这里讨论一下ControlTemplate。

在WPF中每一个控件都有一个默认的模板,该模板描述了空间的外观以及外观对外界刺激所做出的反应(比如各种事件,按钮鼠标触摸后的背景色变化等)。然而在这个浮躁的社会,人们追求酷炫,色彩,所以这个任务就交给作为还要在这个社会上混生活的各位程序猿,啊啊啊 。。。。。。 我们可以自定义一个模板来替换控件默认模板以便打造个性化的控件。

这里讲一下控件模板与Style的异同,都是为了实现空间的个性化。Style只能改变控件已有的属性(比如字体颜色等),但控件模板可以改变空间的内部结构(VisualTree,视觉树)来完成更为复杂的定制,比如我们可以定制一个带图片的按钮。

可以这样理解,Style:一个正常健康的人,要追求个性,一般的做法是穿一些个性的衣服,或者留一个杀马特发型,更甚者纹身。这些只是改变作为人已有的一些属性。ControlTemplate:假如技术可以实现,一些人渴望拥有一双翅膀,这样就可以实现飞天梦了,一些人可能希望拥有和鱼一样的鳃,这样就可以像鱼一样在水中自由自在的呼吸。这些都是改变了人的内在属性。回归正题。
要替换控件的模板,我们只需要声明一个ControlTemplate对象,并对该ControlTemplate对象做相应的配置,然后将该ControlTemplate对象赋值给控件的Template属性就可以了。
ControlTemplate包含两个重要的属性:
1,VisualTree,该模板的视觉树,其实我们就是使用这个属性来描述控件的外观的
2,Triggers,触发器列表,里面包含一些触发器Trigger,我们可以定制这个触发器列表来使控件对外界的刺激发生反应,比如鼠标经过时文本变成粗体等。
参考以下代码:
<Button>
<Button.Template>
<ControlTemplate>
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
</ControlTemplate>
</Button.Template>
</Button>
在上面的代码中,我们修改了Button的Template属性,我们定义了一个ControlTemplate,在<ControlTemplate> ... </ControlTemplate>之间包含的是模板的视觉树,也就是如何显示控件的外观,我们这里使用了一个Ellipse(椭圆)和一个TextBlock(文本块)来定义控件的外观。
很容易联想到一个问题:控件(Button)的一些属性,比如高度、宽度、文本等如何在新定义的外观中表现出来呢?
我们使用TemplateBinding 将控件的属性与新外观中的元素的属性关联起来Width="{TemplateBinding Button.Width}" ,这样我们就使得椭圆的宽度与按钮的宽度绑定在一起而保持一致,同理我们使用Text="{TemplateBinding Button.Content}"将TextBlock的文本与按钮的Content属性绑定在一起。

除了定义控件的默认外观外,也许我们想还定义当外界刺激我们的控件时,控件外观做出相应的变化,这是我们需要触发器。参考以下代码:
<Button Content="test btn" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" >
<Button.Template>
<ControlTemplate>
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Button.Template>
</Button>
在上面的代码中注意到<ControlTemplate.Triggers>... </ControlTemplate.Triggers>之间的部分,我们定义了触发器 <Trigger  Property="Button.IsMouseOver"  Value="True">,其表示当我们Button的IsMouseIOver属性变成True时,将使用设置器<Setter Property="Button.Foreground" Value="Red" />  来将Button的Foreground属性设置为Red。这里有一个隐含的意思是:当Button的IsMouseIOver属性变成False时,设置器中设置的属性将回复原值。

如果我要重用我的模板,应该怎么办呢?
我相信你已经想到了,你只要把它定义为资源
<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Window.Resources>
上面的代码将我们原来的模板定义为窗体范围内的资源,其中TargetType="Button"指示我们的模板作用对象为Button,这样在整个窗体范围内的按钮都可以使用这个模板了,模板的使用方法也很简单
<Button Content="test btn" Template="{StaticResource ButtonTemplate}" />
代码:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>

<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->
<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->
</ControlTemplate>
</Window.Resources>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>

<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />
<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

</Grid>
</Window>
如果你想加入一些动画效果的话,可以在触发器中,调用一个故事板来达到对事件响应时的动画效果
代码如下:
<!--定义动画资源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickButtonStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
我们为模板定义了一个动画资源,此后在模板的触发器中我们就可以调用该资源来实现一个动画效果了:
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
代码:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ControlTemplateTest" Height="300" Width="300"
>

<Window.Resources>
<ControlTemplate TargetType="Button" x:Key="ButtonTemplate">
<!--定义视觉树-->
<Grid>
<Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/>
<TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" />
</Grid>
<!--定义视觉树_end-->

<!--定义动画资源-->
<ControlTemplate.Resources>
<Storyboard x:Key="MouseClickButtonStoryboard">
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="faceEllipse" Storyboard.TargetProperty="Width" BeginTime="00:00:00">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="50"/>
<SplineDoubleKeyFrame KeyTime="00:00:00.3" Value="100"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ControlTemplate.Resources>
<!--定义动画资源_end-->

<!--定义触发器-->
<ControlTemplate.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Red" />
</Trigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="faceEllipse">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
<EventTrigger RoutedEvent="Mouse.MouseDown" SourceName="txtBlock">
<EventTrigger.Actions>
<BeginStoryboard Storyboard="{StaticResource MouseClickButtonStoryboard}"/>
</EventTrigger.Actions>
</EventTrigger>
</ControlTemplate.Triggers>
<!--定义触发器_End-->

</ControlTemplate>

</Window.Resources>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*"/>
<ColumnDefinition Width="0.6*"/>
<ColumnDefinition Width="0.2*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.3*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>

<Button Content="test btn1" Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Grid.RowSpan="1" />
<Button Content="test btn2" Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="1" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />
<Button Content="test btn2" Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="2" Grid.RowSpan="1" Template="{StaticResource ButtonTemplate}" />

</Grid>
</Window>

最后留一个ImageButton自定义控件以供参考
ImageButton.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SoftRGB.Controls
{
/// <summary>
/// 按照步骤 1a 或 1b 操作,然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
///
/// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:SoftRGB.Controls"
///
///
/// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
/// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
/// 元素中:
///
/// xmlns:MyNamespace="clr-namespace:SoftRGB.Controls;assembly=SoftRGB.Controls"
///
/// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用,
/// 并重新生成以避免编译错误:
///
/// 在解决方案资源管理器中右击目标项目,然后依次单击
/// “添加引用”->“项目”->[浏览查找并选择此项目]
///
///
/// 步骤 2)
/// 继续操作并在 XAML 文件中使用控件。
///
/// <MyNamespace:ImageButton/>
///
/// </summary>
public class ImageButton : Button
{
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}

#region 依赖属性

public static readonly DependencyProperty NormalImageProperty =
DependencyProperty.Register("NormalImage", typeof(string), typeof(ImageButton), null);
public static readonly DependencyProperty OverImageProperty =
DependencyProperty.Register("OverImage", typeof(string), typeof(ImageButton), null);
public static readonly DependencyProperty PressedImageProperty =
DependencyProperty.Register("PressedImage", typeof(string), typeof(ImageButton), null);
public static readonly DependencyProperty DisableImageProperty =
DependencyProperty.Register("DisableImage", typeof(string), typeof(ImageButton), null);

#endregion

#region 属性

/// <summary>
/// 正常状态下显示的图片
/// </summary>
public string NormalImage
{
get { return (string)GetValue(NormalImageProperty); }
set { SetValue(NormalImageProperty, value); }
}

/// <summary>
/// 鼠标滑过状态下显示的图片
/// </summary>
public string OverImage
{
get { return (string)GetValue(OverImageProperty); }
set { SetValue(OverImageProperty, value); }
}

/// <summary>
/// 鼠标点击状态下显示的图片
/// </summary>
public string PressedImage
{
get { return (string)GetValue(PressedImageProperty); }
set { SetValue(PressedImageProperty, value); }
}

/// <summary>
/// 禁用状态下显示的图片
/// </summary>
public string DisableImage
{
get { return (string)GetValue(DisableImageProperty); }
set { SetValue(DisableImageProperty, value); }
}

#endregion

}
}
ImageButton.xaml

<Style TargetType="{x:Type local:ImageButton}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<Grid x:Name="Root" Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="NormalUI" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="MouseOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="OverUI" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PressedUI" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DisableUI" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource Visible}"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Image x:Name="NormalUI" Source="{Binding NormalImage, RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed"/>
<Image x:Name="OverUI" Source="{Binding OverImage, RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed"/>
<Image x:Name="PressedUI" Source="{Binding PressedImage, RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed"/>
<Image x:Name="DisableUI" Source="{Binding DisableImage, RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed"/>
<ContentPresenter x:Name="ContentPresenter" Margin="{TemplateBinding Padding}" ContentTemplate="{TemplateBinding ContentTemplate}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
 <ControlTemplate TargetType="{x:Type local:ImageButton}">... </ControlTemplate>为控件模板,这里需说明一下在xaml文件中要引用ImageButton类要先引用ImageButton所在的命名空间。。。。

效果如下:
一般状态:

鼠标触摸状态:

鼠标点击状态:

---------------------
作者:yang_B621
来源:CSDN
原文:https://blog.csdn.net/qq_23018459/article/details/79899838
版权声明:本文为博主原创文章,转载请附上博文链接!

转载 WPF -- 控件模板 (ControlTemplate)(一) https://blog.csdn.net/qq_23018459/article/details/79899838的更多相关文章

  1. MVC和WebApi 使用get和post 传递参数。 转载https://blog.csdn.net/qq373591361/article/details/51508806

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq373591361/article/details/51508806我们总结一下用js请求服务器的 ...

  2. 爬虫出现Forbidden by robots.txt(转载 https://blog.csdn.net/zzk1995/article/details/51628205)

    先说结论,关闭scrapy自带的ROBOTSTXT_OBEY功能,在setting找到这个变量,设置为False即可解决. 使用scrapy爬取淘宝页面的时候,在提交http请求时出现debug信息F ...

  3. 联想笔记本 thinkpad BIOS 超级密码 Supervisor Password 清除 破解 亲测有效 转载地址https://blog.csdn.net/ot512csdn/article/details/72571674

    联想笔记本 thinkpad BIOS 超级密码 Supervisor Password 清除 破解 亲测有效 转载地址https://blog.csdn.net/ot512csdn/article/ ...

  4. AutoFac控制反转 转载https://blog.csdn.net/u011301348/article/details/82256791

    一.AutoFac介绍 Autofac是.NET里IOC(Inversion of Control,控制反转)容器的一种,同类的框架还有Spring.NET,Unity,Castle等.可以通过NuG ...

  5. Mui本地打包笔记(一)使用AndroidStudio运行项目 转载 https://blog.csdn.net/baidu_32377671/article/details/79632411

    转载 https://blog.csdn.net/baidu_32377671/article/details/79632411 使用AndroidStudio运行HBuilder本地打包的Mui项目 ...

  6. 已知IP地址和子网掩码求出网络地址、广播地址、地址范围和主机数(转载https://blog.csdn.net/qq_39026548/article/details/78959089)

    假设IP地址为128.11.67.31,子网掩码是255.255.240.0.请算出网络地址.广播地址.地址范围.主机数.方法:将IP地址和子网掩码转化成二进制形式,然后进行后续操作. IP地址和子网 ...

  7. Socket的用法——NIO包下SocketChannel的用法 ———————————————— 版权声明:本文为CSDN博主「茶_小哥」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/ycgslh/article/details/79604074

    服务端代码实现如下,其中包括一个静态内部类Handler来作为处理器,处理不同的操作.注意在遍历选择键集合时,没处理完一个操作,要将该请求在集合中移除./*模拟服务端-nio-Socket实现*/pu ...

  8. 转载:https://blog.csdn.net/qq_22706515/article/details/52595027

    https://blog.csdn.net/qq_22706515/article/details/52595027 包含直播,即时通讯. 大平台都有免费版或基础版,对于需求不大的情况比较适合.

  9. 程序员的沟通之痛https://blog.csdn.net/qq_35230695/article/details/80283720

    个人理解: 一般刚工作的程序员总觉得技术最重要.但是当工作年限超过3年.或者岗位需要涉及汇报.需求对接等就会发现沟通非常重要.也许在大公司还不那么明显,但是在小公司.小团队或者创业,沟通甚至可以说是第 ...

随机推荐

  1. linux 解压 WinRAR 压缩文件

    1.Download rar for linux wget http://www.rarlab.com/rar/rarlinux-x64-5.5.b1.tar.gz 2.Configure rar t ...

  2. Django之ORM查询优化

    目录 only 和 defer select_related 和 prefetch_related ORM字段参数 choices res = models.Book.objects.all() # ...

  3. .git文件夹太大问题及解决方法

    最近我们做了自动化构建, 发现文件.git文件夹越来越大, 求后端小伙伴帮忙, 小伙伴指点了一下说周末弄了一下, 忘记命令的.大致的意思就是找到git 提交了哪些大文件. 然后重构git, 先分享给小 ...

  4. UICollectionView入门--使用系统UICollectionViewFlowLayout布局类

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://rainbownight.blog.51cto.com/1336585/13237 ...

  5. iOS之UITableView加载网络图片cell自适应高度

    #pragma mark- UITableView - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSI ...

  6. Python网络爬虫之三种数据解析方式 (xpath, 正则, bs4)

    引入 回顾requests实现数据爬取的流程 指定url 基于requests模块发起请求 获取响应对象中的数据 进行持久化存储 其实,在上述流程中还需要较为重要的一步,就是在持久化存储之前需要进行指 ...

  7. linux socket error code

    errno.00 is: Successerrno.01 is: Operation not permittederrno.02 is: No such file or directoryerrno. ...

  8. Python学习day40-并发编程(终)

    figure:last-child { margin-bottom: 0.5rem; } #write ol, #write ul { position: relative; } img { max- ...

  9. java笔试之计算n x m的棋盘格子

    请编写一个函数(允许增加子函数),计算n x m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和往下走,不能往左和往 ...

  10. UDP和TCP的区别?

    区别总结: 1.TCP面向连接,UDP的面向无连接的,即发送数据之前不需要建立简介. 2.TCP提供可靠的数据传输,有发送应答机制,超时重传机制,错误校验机制,流量控制机制保证传输的安全,不丢失,不重 ...