创建一个简单用户控件是开始自定义控件的好方法。本章主要介绍创建一个基本的颜色拾取器。接下来分析如何将这个控件分解成功能更强大的基于模板的控件。

  创建基本的颜色拾取器很容易。然而,创建自定义颜色拾取器仍是有价值的联系,因为这不仅演示了构建控件的各种重要概念,而且提供了一个实用的功能。

  可为颜色拾取器创建自定义对话框。但如果希望创建能集成进不同窗口的颜色拾取器,使用自定义控件是更好的选择。最简单的自定义控件类型是用户控件,当设计窗口或页面时通过用户控件可以使用相同的方式组装多个元素。因为仅通过直接组合现有控件并添加功能并不能实现颜色拾取器,所以用户控件看起来是更合理的选择。

  典型的颜色拾取器允许用户通过单击颜色梯度中的某个位置或分别指定红、绿和蓝三元色成分来选择颜色。下图显示了创建的基本颜色拾取器。该颜色拾取器包含三个Slider控件,这些控件用于调节颜色成分,同时使用Rectangle元素预览选择的颜色。

一、定义依赖性属性

  创建颜色拾取器的第一步是为自定义控件库项目添加用户控件。当添加用户控件后,Visual Studio会创建XAML标记文件和相应的包含初始化代码即事件处理代码的自定义类。这与创建新的窗口或也卖弄是相同的——唯一的区别在与顶级容器是UserControl类:

<UserControl x:Class="CustomControls.ColorPickerUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Name="colorPicker">
</UserControl>

  最简单的起点是设计用户控件对外界公开的公共接口。换句话说,就是设计控件使用者使用的鱼颜色拾取器进行交互的属性、方法和事件。

  最基本的细节是Color属性——毕竟,颜色拾取器不过是用于显示和选择颜色的特定工具。为支持WPF特性,如数据绑定、样式以及动画,控件的可写属性几乎都是依赖项属性。

  在前面章节中学习过,创建依赖项属性的第一步是为之定义静态字段,并在属性名称的后面加上单词Property:

public static DependencyProperty ColorProperty;

  Color属性将允许控件使用者通过代码设置或检索颜色值。然而,颜色拾取器中的滑动条控件也允许用户修改当前颜色的一个方面。为实现这一设计,当滑动条额值发生变化时,需要使用事件处理程序进行响应,并且响应地更新Color属性。但使用数据绑定关联滑动条会更加清晰。为使用数据绑定,需要将每个颜色成分定义为单独的依赖项属性:

public static DependencyProperty RedProperty;
public static DependencyProperty GreenProperty;
public static DependencyProperty BlueProperty;

  尽管Color属性存储了System.Windows.Media.Color对象,但Red、Green以及Blue属性将存储表示每个颜色成分的单个字节值。

  为属性定义静态字段只有第一步。还需要有静态构造函数,用于在用户控件中注册这些依赖性属性,指定属性的名称、数据类型以及拥有属性的控件类。可通过传递具有正确标记设置的FrameworkPropertyMetadata对象,在静态构造函数中指定选择的特定属性特性(如值继承)。还可指出在什么地方为验证、数据强制以及属性更改通知关联回调函数。

  在颜色拾取器中,只需要考虑一个因素——当各种属性变化时需要关联回调函数进行响应。因为Red、Green和Blue属性实际上时Color属性的不同表示,并且如果一个属性发生变化,就需要确保其他属性保持同步。

  下面是注册颜色拾取器的4个依赖性属性的静态构造函数的代码:

static ColorPickerUserControl()
{
ColorProperty = DependencyProperty.Register("Color", typeof(Color),
typeof(ColorPickerUserControl),
new FrameworkPropertyMetadata(Colors.Black, new PropertyChangedCallback(OnColorChanged))); RedProperty = DependencyProperty.Register("Red", typeof(byte),
typeof(ColorPickerUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged))); GreenProperty = DependencyProperty.Register("Green", typeof(byte),
typeof(ColorPickerUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged))); BlueProperty = DependencyProperty.Register("Blue", typeof(byte),
typeof(ColorPickerUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnColorRGBChanged)));
}

  现在已经定义了依赖性属性,可添加标准的属性封装器,使范文它们变得更加容易,并可在XAML中使用它们:

public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
public byte Red
{
get { return (byte)GetValue(RedProperty); }
set { SetValue(RedProperty, value); }
} public byte Green
{
get{return (byte)GetValue(GreenProperty);}
set{SetValue(GreenProperty,value);}
}
public byte Blue
{
get { return (byte)GetValue(BlueProperty); }
set { SetValue(BlueProperty, value); }
}

  请记住,属性封装器不能包含任何逻辑,因为可直接使用DependencyObject基类的SetValue()和GetValue()方法设置和检索属性。例如,在这个示例中的属性同步逻辑是使用回调函数实现的,当属性发生变化时通过属性封装器或者直接调用SetValue()方法引发回调函数。

  属性变化回调函数负责使Color属性与Red、Green以及Blue属性保持一致。无论何时Red、Green以及Blue属性发生变化,都会相应地调整Color属性:

private static void OnColorRGBChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;
Color color = colorPicker.Color;
if (e.Property == RedProperty)
color.R = (byte)e.NewValue;
else if (e.Property == GreenProperty)
color.G = (byte)e.NewValue;
else if (e.Property == BlueProperty)
color.B = (byte)e.NewValue; colorPicker.Color = color;
}

  当设置Color属性时,也会更新Red、Green和Blue值:

 private static void OnColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;
Color oldColor = (Color)e.OldValue;
Color newColor = (Color)e.NewValue;
colorPicker.Red = newColor.R;
colorPicker.Green = newColor.G;
colorPicker.Blue = newColor.B;
}

  尽管很明显,但当各个属性试图改变其他属性时,上面的代码不会引起一系列无休止的调用。因为WPF不允许重新进入属性变化回调函数。例如,如果改变Color顺序,就会触发OnColorChanged()方法。OnColorChanged()方法会修改Red、Green以及Blue属性,从而触发OnColorRGBChanged()回调方法三次(每个属性触发一次)。然而,OnColorRGBChanged()方法不会再次触发OnColorChanged()方法。

二、定义路由事件

  通过添加路由事件,当发生一些事情时用于通知控件使用者。在颜色拾取器示例中,当颜色发生变化后,触发一个事件是很有用处的。尽管可将这个事件定义为普通的.NET事件,但使用路由事件可提供冒泡和隧道特性,从而可在更高层次的父元素中处理事件。

  与依赖项属性一样,定义路由事件的一个步骤是为值创建静态属性,并在时间名称的后面添加单词Event:

public static readonly RoutedEvent ColorChangedEvent;

  然后可在静态构造函数中注册事件。在静态构造函数中指定事件的名称、路由策略、签名以及拥有事件的类:

ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged", RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<Color>), typeof(ColorPickerUserControl));

  不一定要为事件签名创建新的委托,有时可重用已经存在的委托。两个有用的委托是RoutedEventHandler(用于不带额外信息的路由事件)和RoutedPropertyChangedEventHandler(用于提供属性发生变化之后的旧值和新值得路由事件)。上例中使用RoutedPropertyChangedEventHandler委托,是被类型参数化了的泛型委托。所以,可为任何属性数据类型使用该委托,而不会牺牲类型安全功能。

  定义并注册事件后,需要创建标准的.NET事件封装器来公开事件。事件封装器可用于关联和删除事件监听程序:

public event RoutedPropertyChangedEventHandler<Color> ColorChanged
{
add { AddHandler(ColorChangedEvent, value); }
remove { RemoveHandler(ColorChangedEvent, value); }
}

  最后的细节是在适当时候引发事件的代码。该代码必须调用继承自DependencyObject基类的RaiseEvent()方法。

  在颜色拾取器示例中,只需要在OnColorChanged()方法之后添加如下代码即可:

RoutedPropertyChangedEventArgs<Color> args = new RoutedPropertyChangedEventArgs<Color>(oldColor, newColor);
args.RoutedEvent = ColorChangedEvent;
colorPicker.RaiseEvent(args);

  请记住,无论何时修改Color属性,不管是直接修改还是通过修改Red、Green以及Blue成分,都会触发OnColorChanged()回调函数。

三、添加标记

  现在已经定义好用户控件的公有接口,需要做的所有工作就是创建控件外观的标记。在这个示例中,需要使用一个基本Grid控件将三个Slider控件和预览颜色的Rectangle元素组合在一起。技巧是使用数据绑定表达式,将这些控件连接到合适的属性,而不需要使用事件处理代码。

  总之,颜色拾取器中总共使用4个数据绑定表达式。三个滑动条被绑定到Red、Green和Blue属性。而且属性值得允许范围是0~255(一个字节可以接受的数值)。Rectangle.Fill属性使用SolidColorBrush画刷进行设置。画刷的Color属性被绑定到用户控件的Color属性。

  下面是完整的标记:

<UserControl x:Class="CustomControls.ColorPickerUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Name="colorPicker">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Slider Name="sliderRed" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker,Path=Padding}"
Value="{Binding ElementName=colorPicker,Path=Red}"></Slider>
<Slider Grid.Row="1" Name="sliderGreen" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker,Path=Padding}"
Value="{Binding ElementName=colorPicker,Path=Green}"></Slider>
<Slider Grid.Row="2" Name="sliderBlue" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker,Path=Padding}"
Value="{Binding ElementName=colorPicker,Path=Blue}"></Slider>
<Rectangle Grid.Column="1" Grid.RowSpan="3"
Margin="{Binding ElementName=colorPicker,Path=Padding}"
Width="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding ElementName=colorPicker,Path=Color}"></SolidColorBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</UserControl>

  用于用户控件的标记和无外观控件的控件模板扮演相同的角色。如果希望使标记中的一些细节是可配置的,可使用将他们连接到控件属性的绑定表达式。例如,目前Rectangle元素的宽度被固定为50个单位。然而,可使用数据绑定表达式从用户控件的依赖性属性中提取数值来代替这些细节。这样,控件使用者可通过修改属性来选择不同的宽度。同样,可使笔画颜色和宽度也是可变的。然而,如果希望使控件具有真正的灵活性,最好的创建无外观的控件,并在模板中定义标记。

  偶尔可选用数据绑定表达式,重用已在控件中定义过的核心属性。例如,UserControl类使用Padding属性在外侧边缘和用户定义的内部内容之间添加空间(这一细节是通过UserControl控件的控件模板实现的)。然而,也可以使用Padding属性在每个滑动条的周围设置空间,如下所示:

 <Slider Name="sliderRed" Minimum="0" Maximum="255"
Margin="{Binding ElementName=colorPicker,Path=Padding}"
Value="{Binding ElementName=colorPicker,Path=Red}"></Slider>

  类似地,也可从UserControl类的BorderThickness和BorderBrush属性为Rectan元素获取边框设置。同样,这样快捷方式对于创建简单的控件是非常合理的,但可通过引入额外的属性(如SliderMargin、PreviewBorderBrush以及PreviewBorderThickness)或创建功能完备的基于模板的控件加以改进。

四、使用控件

  现在完成了控件,使用该控件很容易。为在另一个窗口中使用颜色拾取器,首先需要将程序集合.NET名称控件映射到XAML名称空间,如下所示:

<Window x:Class="CustomControlsClient.ColorPickerUserControlTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lib="clr-namespace:CustomControls;assembly=CustomControls" ...>

  使用定义的XML名称控件和用户控件类名,在XAML标记中可像创建其他类型的对象那样创建自定义的用户控件。还可在控件标记中设置它的属性,以及直接关联事件处理程序,如下所示:

<lib:ColorPickerUserControl
Name="colorPicker" Margin="2" Padding="3" ColorChanged="colorPicker_ColorChanged" Color="Yellow"></lib:ColorPickerUserControl>

  因为Color属性使用Color数据类型,并且Color数据类型使用TypeConverter特性进行了修饰,所以在设置Color属性之前,WPF知道使用ColorConverter转换器将颜色名称字符串转换成相应的Color对象。

  处理ColorChanged事件的代码很简单:

private void colorPicker_ColorChanged(object sender, RoutedPropertyChangedEventArgs<Color> e)
{
if (lblColor != null) lblColor.Text = "The new color is " + e.NewValue.ToString();
}

  现在已经完成了自定义控件。

五、命令支持

  许多控件具有命令支持。可使用以下两种方法为自定义控件添加命令支持:

  •   添加将控件链接到特定命令的命令绑定。通过这种方法,控件可以相应命令,而且不需要借助于任何外部代码。
  •   为命令创建新的RoutedUICommand对象,作为自定义控件的静态字段。然后为这个命令对象添加命令绑定。这种方法可使自定义控件自动支持没有在基本命令类集合中定义的命令。

  接下来的将使用第一种方法为ApplicationCommands.Undo命令添加支持。

  在颜色拾取器中为了支持Undo功能,需要使用成员字段跟踪以前选择的颜色:

private Color? previousColor;

  将该字段设置为可空是合理的,因为当第一次创建控件时,还没有设置以前选择的颜色。

  当颜色发生变化时,只需要记录旧值。可通过在OnColorChanged()方法的最后添加以下代码行来达到该目的:

colorPicker.previousColor = oldColor;

  现在已经具备了支持Undo命令需要的基础框架。剩余的工作是创建将控件链接到命令以及处理CanExecute和Executed事件的命令绑定。

  第一次创建控时是创建命令绑定的最佳时机。例如,下面的代码使用颜色拾取器的构造函数为ApplicationCommands.Undo命令添加命令绑定:

 public ColorPickerUserControl()
{
InitializeComponent();
SetUpCommands();
} private void SetUpCommands()
{
CommandBinding binding = new CommandBinding(ApplicationCommands.Undo,
UndoCommand_Executed, UndoCommand_CanExecute);
this.CommandBindings.Add(binding);
}

  为使命令奏效,需要处理CanExecute事件,并且只要有以前的颜色值就允许执行命令:

private void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = previousColor.HasValue;
}

  最后,当执行命令后,可交换新的颜色:

private void UndoCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
this.Color = (Color)previousColor;
}

  可通过两种不同方式触发Undo命令。当用户控件中的某个元素具有焦点时,可以使用默认的Ctrl+Z组合键绑定,也可为客户添加用于触发命令的按钮,如下所示:

<Button Command="Undo" CommandTarget="{Binding ElementName=colorPicker}"  Margin="5,0,5,0" Padding="2">Undo</Button>

  这两种方法都会丢弃当前颜色并应用以前的颜色。

  更可靠的命令

  前面描述的技术是将命令链接到控件的相当合理的方法,但这不是在WPF元素和专业控件中使用的技术。这些元素使用更可靠的方法,并使用CommandManager.RegisterClassCommandBinding()方法关联静态的命令处理程序。

  上一个示例中演示的实现存在问题:使用公用CommandBindings集合。这使得命令比较脆弱,因为客户可自由修改CommandBindings集合。而使用RegisterClassCommandBinding()方法无法做到这一点。WPF控件使用的就是这种方法。例如,如果查看TextBox的CommandBindings集合,不会发现任何用于硬编码命令的绑定,例如Undo、Redo、Cut、Copy以及Paste等命令,因为他们被注册为类绑定。

  这种技术非常简单。不在实例构造函数中创建命令绑定,而必须在静态构造函数中创建命令绑定,使用如下所示的代码:

CommandManager.RegisterClassCommandBinding(typeof(ColorPickerUserControl),
new CommandBinding(ApplicationCommands.Undo, UndoCommand_Executed, UndoCommand_CanExecute));

  尽管上面的代码变化不大,但有一个重要变化。因为 UndoCommand_Executed()和UndoCommand_CanExecute()方法是在构造函数中引用的,所以必须是静态方法。为检索实例数据(例如当前颜色和以前颜色的信息),需要将事件发送者转换为ColorPickerUserControl对象,并使用该对象。

  下面是修改之后的命令处理代码:

private static void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;
e.CanExecute =colorPicker.previousColor.HasValue;
} private static void UndoCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;
colorPicker.Color = (Color)colorPicker.previousColor.Value;
}

  此外,这种技术不局限于命令。如果希望将事件处理逻辑硬编码到自定义控件,可通过EventManager.RegisterClassHandler()方法使用类事件处理程序。类事件处理程序总在实例事件处理程序之前调用,从而允许开发人员很容易地抑制事件。

六、深入分析用户控件

  用户控件提供了一种非常简单的,但是有一定限制的创建自定义控件的方法。为理解其中的原因,深入分析用户控件的工作原理是很有帮助的。

  在后台,UserControl类的工作方式和其父类ContentControl非常类似。实际上,只有几个重要的区别:

  •   UserControl类改变了一些默认值。即该类将IsTabStop和Focusable属性设置为false(从而在Tab顺序中没有占据某个单独的额位置),并将HorizontalAlignment和VerticalAlignment属性设置为Stretch(而非Left或Top),从而可以填充可用空间。
  •   UserControl类应用了一个新的控件模板,该模板由包含ContentPresenter元素的Border元素组成。ContentPresenter元素包含了用标记添加的内容。
  •   UserControl类改变了路由事件的源。当事件从用户控件内的控件向用户控件外的元素冒泡或隧道路由时,事件源变为指向用户控件而不是原始元素。这提供了更好的封装性。

  用户控件和其他类型的自定义控件之间最重的区别是设计用户控件的方法。与所有控件一样,用户控件有控件模板。然而,很少改变控件模板——反而,将作为自定义用户控件类的一部分提供标记,并且当创建了控件后,会使用InitializeComponet()方法处理这个标记。另一个方面,无外观控件是没有标记——需要的所有内容都在模板中。

  普通的ContentControl控件具有下面的简单模板:

<ControlTemplate TargetType="ContentControl">
<ContentPresenter
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
Content="{TemplateBinding ContentControl.Content}"/>
</ControlTemplate>

  这个模板仅填充所提供的内容并应用可选的内容模板。Padding、Background、HorizontalAlignment以及VerticalAlignment等熟悉没有任何影响(除非显示绑定属性)。

  UserControl类有一个类似的模板,并又更多的细节。最明显的是,它添加了一个Border元素并将其属性绑定到用户控件的BorderBrush、BorderThickness、Background以及Padding属性,以确保它们具有相同的含义。此外,内部的ContentPresenter元素已绑定到对齐属性。

<ControlTempalte TargetType="UserControl">
<Border BorderBrush="{TemplateBinding Border.BorderBrush}"
BorderThickness="{TemplateBinding Border.BorderThickness}"
Background="{TemplateBinding Border.Background}"
Padding="{TemplateBinding Border.Padding}"
SnapsToDevicePixels="True">
<ContentPresenter
HorizontalAlignment="{TemplateBinding Control.HorizontalAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalAlignment}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
Contenttemplate="{TemplateBinding ContentControl.ContentTemplate}"
Content="{TemplateBinding ContentControl.Content}"/>
</Border>
</ControlTemplate>

  从技术角度看,可改变用户控件的模板。实际上,只需要进行很少的调整,就可以将所有标记移到模板中。但却是没有理由采取该方法——如果希望得到更灵活的控件,时可视化外观和由自定义控件类定义的借款分开,创建无外观的自定义控件可能会更好一些。

  本章程序源代码:CustomControl.zip

【WPF学习】第六十四章 构建基本的用户控件的更多相关文章

  1. 【WPF学习】第十四章 事件路由

    由上一章可知,WPF中的许多控件都是内容控件,而内容控件可包含任何类型以及大量的嵌套内容.例如,可构建包含图形的按钮,创建混合了文本和图片内容的标签,或者为了实现滚动或折叠的显示效果而在特定容器中放置 ...

  2. 【WPF学习】第二十四章 基于范围的控件

    WPF提供了三个使用范围概念的控件.这些控件使用在特定最小值和最大值之间的数值.这些控件——ScrollBar.ProgressBar以及Slider——都继承自RangeBase类(该类又继承自Co ...

  3. Gradle 1.12用户指南翻译——第六十四章. 发布到Ivy(新)

    其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上的地址:https://gith ...

  4. “全栈2019”Java第六十四章:接口与静态方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. C++ Primer Plus学习:第十四章

    第十四章 C++中的代码重用 包含对象成员的类 将类的对象作为新类的成员.称为has-a关系.使用公有继承的时候,类可以继承接口,可能还有实现(纯虚函数不提供实现,只提供接口).使用包含时,可以获得实 ...

  6. 【WPF学习】第六十二章 构建更复杂的模板

    在控件模板和为其提供支持的代码之间又一个隐含约定.如果使用自定义控件模板替代控件的标准模板,就需要确保新模板能够满足控件的实现代码的所有需要. 在简单控件中,这个过程比较容易,因为对模板几乎没有(或完 ...

  7. 【WPF学习】第二十九章 元素绑定——将元素绑定到一起

    数据banding的最简单情形是,源对象时WPF元素而且源属性是依赖性属性.前面章节解释过,依赖项属性具有内置的更改通知支持.因此,当在源对象中改变依赖项属性的值时,会立即更新目标对象中的绑定属性.这 ...

  8. 【WPF学习】第十五章 WPF事件

    前两章学习了WPF事件的工作原理,现在分析一下在代码中可以处理的各类事件.尽管每个元素都提供了许多事件,但最重要的事件通常包括以下5类: 生命周期事件:在元素被初始化.加载或卸载时发生这些事件. 鼠标 ...

  9. 【WPF学习】第二十二章 文本控件

    WPF提供了三个用于输入文本的控件:TextBox.RichTextBox和PasswordBox.PasswordBox控件直接继承自Control类.TextBox和RichTextBox控件间接 ...

随机推荐

  1. 浏览器渲染流程&Composite(渲染层合并)简单总结

    梳理浏览器渲染流程 首先简单了解一下浏览器请求.加载.渲染一个页面的大致过程: DNS 查询 TCP 连接 HTTP 请求即响应 服务器响应 客户端渲染 这里主要将客户端渲染展开梳理一下,从浏览器器内 ...

  2. 个人理解TCP中SYN Cookie

    说起SYN Cookie还是得从TCP3次握手开始说起,先给出计网的体系结构图 然后解释一下SYN,seq,ack,ACK的相关名词 SYN(建立连接) ACK(确认后全部为1) PSH(传送) FI ...

  3. 使用AQS自定义重入锁

    一.创建MyLock import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.AbstractQueuedSyn ...

  4. Asp.Net Core 2.0实现HttpResponse中繁切换

    随笔背景:因为项目中有个简单的功能是需要实现中文简体到繁体的切换,数据库中存储的源数据都是中文简体的,为了省事就想着通过HttpHeader的方式来控制Api返回对应的繁体数据. 实现方式:通过Asp ...

  5. JDK中线程池参详细解析

    在jdk中为我们提供了三种创建线程池的方式,但是在阿里的编码规范里面都是明确禁止使用这三种api去创建线程池,推荐我们去自定义线程池.为什么? 要回答为什么,我们需要明白创建线程池时,各参数的作用: ...

  6. mysql & Tomcat使用问题记录

    mysql使用问题记录 1.mysql如何修改root密码 a.进入mysql安装目录b.登录 mysql -u root -pc.修改密码 mysql> SET PASSWORD FOR ro ...

  7. Core3.1WebApi_ 同源策略_如何支持跨域(转载)

    原文:https://mp.weixin.qq.com/s/id3fOyGrZI9lLx7PKbVYlg

  8. Netty Hello World 入门源码分析

    第一节简单提了什么是网络编程,Netty 做了什么,Netty 都有哪些功能组件.这一节就具体进入 Netty 的世界,我们从用 Netty 的功能实现基本的网络通信开始分析 各个组件的使用. 1. ...

  9. thinkPHP渗透之经验决定成败

    如上图,目标就一个登陆框,最近 Thinkphp 程序很多,根据后台地址结构,猜测可能是 ThinkPHP ,随手输入 xxx 得到 thinkPHP 报错页面,确定目标程序和版本. 然后上 5.X ...

  10. SpringMVC最详细笔记partⅠ

    一.springMVC-quickStar 解决maven加载项目过慢 archetypeCatalog internal 导入依赖 <!-- 版本锁定 --> <propertie ...