WPF快速入门系列(2)——深入解析依赖属性

一、引言

  感觉最近都颓废了,好久没有学习写博文了,出于负罪感,今天强烈逼迫自己开始更新WPF系列。尽管最近看到一篇WPF技术是否老矣的文章,但是还是不能阻止我系统学习WPF。今天继续分享WPF中一个最重要的知识点——依赖属性。

二、依赖属性的全面解析

  听到依赖属性,自然联想到C#中属性的概念。C#中属性是抽象模型的核心部分,而依赖属性是专门基于WPF创建的。在WPF库实现中,依赖属性使用普通的C#属性进行了包装,使得我们可以通过和以前一样的方式来使用依赖属性,但我们必须明确,在WPF中我们大多数都在使用依赖属性,而不是使用属性。依赖属性重要性在于,在WPF核心特性,如动画、数据绑定以及样式中都需要使用到依赖属性。既然WPF引入了依赖属性,也自然有其引入的道理。WPF中的依赖属性主要有以下三个优点:

  • 依赖属性加入了属性变化通知、限制、验证等功能。这样可以使我们更方便地实现应用,同时大大减少了代码量。许多之前需要写很多代码才能实现的功能,在WPF中可以轻松实现。
  • 节约内存:在WinForm中,每个UI控件的属性都赋予了初始值,这样每个相同的控件在内存中都会保存一份初始值。而WPF依赖属性很好地解决了这个问题,它内部实现使用哈希表存储机制,对多个相同控件的相同属性的值都只保存一份。关于依赖属性如何节约内存的更多内容参考:WPF的依赖属性是怎么节约内存的
  • 支持多种提供对象:可以通过多种方式来设置依赖属性的值。可以配合表达式、样式和绑定来对依赖属性设置值。

2.1 依赖属性的定义

  上面介绍了依赖属性所带来的好处,这时候,问题又来了,怎样自己定义一个依赖属性呢?C#属性的定义大家再熟悉不过了。下面通过把C#属性进行改写成依赖属性的方式来介绍依赖属性的定义。下面是一个属性的定义:

1 public class Person
2     {
3         public string Name { get; set; }
6     }

  在把上面属性改写为依赖属性之前,下面总结下定义依赖属性的步骤:

  1. 让依赖属性的所在类型继承自DependencyObject类。
  2. 使用public static 声明一个DependencyProperty的变量,该变量就是真正的依赖属性。
  3. 在类型的静态构造函数中通过Register方法完成依赖属性的元数据注册。
  4. 提供一个依赖属性的包装属性,通过这个属性来完成对依赖属性的读写操作。

  根据上面的四个步骤,下面来把Name属性来改写成一个依赖属性,具体的实现代码如下所示:

复制代码
// 1. 使类型继承DependencyObject类public class Person : DependencyObject{// 2. 声明一个静态只读的DependencyProperty 字段public static readonly DependencyProperty nameProperty;static Person(){// 3. 注册定义的依赖属性nameProperty = DependencyProperty.Register("Name", typeof(string), typeof(Person), new PropertyMetadata("Learning Hard",OnValueChanged)); }// 4. 属性包装器,通过它来读取和设置我们刚才注册的依赖属性public string Name{get { return (string)GetValue(nameProperty); }set { SetValue(nameProperty, value); }}private static void OnValueChanged(DependencyObject dpobj, DependencyPropertyChangedEventArgs e){// 当只发生改变时回调的方法}}
复制代码

  从上面代码可以看出,依赖属性是通过调用DependencyObject的GetValue和SetValue来对依赖属性进行读写的。它使用哈希表来进行存储的,对应的Key就是属性的HashCode值,而值(Value)则是注册的DependencyPropery;而C#中的属性是类私有字段的封装,可以通过对该字段进行操作来对属性进行读写。总结为:属性是字段的包装,WPF中使用属性对依赖属性进行包装。

2.2 依赖属性的优先级

   WPF允许在多个地方设置依赖属性的值,则自然就涉及到依赖属性获取值的优先级问题。例如下面XMAL代码,我们在三个地方设置了按钮的背景颜色,那最终按钮会读取那个设置的值呢?是Green、Yellow还是Red?

复制代码
<Window x:Class="DPSample.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="MainWindow" Height="350" Width="525"><Grid><Button x:Name="myButton" Background="Green" Width="100" Height="30"><Button.Style><Style TargetType="{x:Type Button}"><Setter Property="Background" Value="Yellow"/><Style.Triggers><Trigger Property="IsMouseOver" Value="True"><Setter Property="Background" Value="Red" /></Trigger></Style.Triggers></Style></Button.Style>Click Me </Button></Grid>
</Window>
复制代码

  上面按钮的背景颜色是Green。之所以背景色是Green,是因为WPF每访问一个依赖属性,它都会按照下面的顺序由高到底处理该值。具体优先级如下图所示:

  在上面XAML中,按钮的本地值设置的是Green,自定义Style Trigger设置的为Red,自定义的Style Setter设置的为Yellow,由于这里的本地值的优先级最高,所以按钮的背景色或者的是Green值。如果此时把本地值Green去掉的话,此时按钮的背景颜色是Yellow而不是Red。这里尽管Style Trigger的优先级比Style Setter高,但是由于此时Style Trigger的IsMouseOver属性为false,即鼠标没有移到按钮上,一旦鼠标移到按钮上时,此时按钮的颜色就为Red。此时才会体现出Style Trigger的优先级比Style Setter优先级高。所以上图中优先级是比较理想情况下,很多时候还需要具体分析。

2.3 依赖属性的继承

  依赖属性是可以被继承的,即父元素的相关设置会自动传递给所有的子元素。下面代码演示了依赖属性的继承。

复制代码
<Window x:Class="Custom_DPInherited.DPInherited"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" d:DesignHeight="300" d:DesignWidth="300"FontSize="18"Title="依赖属性的继承"><StackPanel ><Label Content="继承自Window的FontSize" /><Label Content="显式设置FontSize" TextElement.FontSize="36"/><StatusBar>Statusbar没有继承自Window的FontSize</StatusBar></StackPanel>
</Window>
复制代码

上面的代码的运行效果如下图所示:

  在上面XAML代码中。Window.FontSize设置会影响所有内部子元素字体大小,这就是依赖属性的继承。如第一个Label没有定义FontSize,所以它继承了Window.FontSize值。但一旦子元素提供了显式设置,这种继承就会被打断,所以Window.FontSize值对于第二个Label不再起作用。

  这时,你可能已经发现了问题:StatusBar没有显式设置FontSize值,但它的字体大小没有继承Window.FontSize的值,而是保持了系统的默认值。那这是什么原因呢?其实导致这样的问题:并不是所有元素都支持属性值继承的,如StatusBar、Tooptip和Menu控件。另外,StatusBar等控件截获了从父元素继承来的属性,并且该属性也不会影响StatusBar控件的子元素。例如,如果我们在StatusBar中添加一个Button。那么这个Button的FontSize属性也不会发生改变,其值为默认值。

  前面介绍了依赖属性的继承,那我们如何把自定义的依赖属性设置为可被其他控件继承呢?通过AddOwer方法可以依赖属性的继承。具体的实现代码如下所示:

复制代码
 1  public class CustomStackPanel : StackPanel2     {3         public static readonly DependencyProperty MinDateProperty;4 5         static CustomStackPanel()6         {7             MinDateProperty = DependencyProperty.Register("MinDate", typeof(DateTime), typeof(CustomStackPanel), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));8         }9 
10         public DateTime MinDate
11         {
12             get { return (DateTime)GetValue(MinDateProperty); }
13             set { SetValue(MinDateProperty, value); }
14         }
15     }
16 
17     public class CustomButton :Button
18     {
19         private static readonly DependencyProperty MinDateProperty;
20 
21         static CustomButton()
22         {
23             // AddOwner方法指定依赖属性的所有者,从而实现依赖属性的继承,即CustomStackPanel的MinDate属性被CustomButton控件继承。
24             // 注意FrameworkPropertyMetadataOptions的值为Inherits
25             MinDateProperty = CustomStackPanel.MinDateProperty.AddOwner(typeof(CustomButton), new FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.Inherits));
26         }
27 
28         public DateTime MinDate
29         {
30             get { return (DateTime)GetValue(MinDateProperty); }
31             set { SetValue(MinDateProperty, value); }
32         }
33     }
复制代码

  接下来,你可以在XAML中进行测试使用,具体的XAML代码如下:

复制代码
<Window x:Class="Custom_DPInherited.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:local="clr-namespace:Custom_DPInherited"xmlns:sys="clr-namespace:System;assembly=mscorlib"Title="实现自定义依赖属性的继承" Height="350" Width="525"><Grid><local:CustomStackPanel x:Name="customStackPanle" MinDate="{x:Static sys:DateTime.Now}"><!--CustomStackPanel的依赖属性--><ContentPresenter Content="{Binding Path=MinDate, ElementName=customStackPanle}"/><local:CustomButton Content="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=MinDate}" Height="25"/></local:CustomStackPanel></Grid>
</Window>
复制代码

  上面XAML代码中,显示设置了CustomStackPanel的MinDate的值,而在CustomButton中却没有显式设置其MinDate值。CustomButton的Content属性的值是通过绑定MinDate属性来进行获取的,关于绑定的更多内容会在后面文章中分享。在这里CustomButton中并没有设置MinDate的值,但是CustomButton的Content的值却是当前的时间,从而可以看出,此时CustomButton的MinDate属性继承了CustomStackPanel的MinDate的值,从而设置了其Content属性。最终的效果如下图所示:

2.4 只读依赖属性

   在C#属性中,我们可以通过设置只读属性来防止外界恶意更改该属性值,同样,在WPF中也可以设置只读依赖属性。如IsMouseOver就是一个只读依赖属性。那我们如何创建一个只读依赖属性呢?其实只读的依赖属性的定义方式与一般依赖属性的定义方式基本一样。只读依赖属性仅仅是用DependencyProperty.RegisterReadonly替换了DependencyProperty.Register而已。下面代码实现了一个只读依赖属性。

复制代码
 1 public partial class MainWindow : Window2     {3         public MainWindow()4         {5             InitializeComponent();6 7             // 内部使用SetValue来设置值8             SetValue(counterKey, 8);9         }
10 
11         // 属性包装器,只提供GetValue,你也可以设置一个private的SetValue进行限制。
12         public int Counter
13         {
14             get { return (int)GetValue(counterKey.DependencyProperty); }
15         }
16 
17         // 使用RegisterReadOnly来代替Register来注册一个只读的依赖属性
18         private static readonly DependencyPropertyKey counterKey =
19             DependencyProperty.RegisterReadOnly("Counter",
20             typeof(int),
21             typeof(MainWindow),
22             new PropertyMetadata(0));
23     }
复制代码

  对应的XAML代码为:

复制代码
<Window x:Class="ReadOnlyDP.MainWindow" Name="ThisWin"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="ReadOnly Dependency Property" Height="350" Width="525"><Grid><Viewbox><TextBlock Text="{Binding ElementName=ThisWin, Path=Counter}"/></Viewbox></Grid>
</Window>
复制代码

  此时Counter包装的counterKey就是一个只读依赖属性,因为其定义为private的,所以在类外也不能使用DependencyObject.SetValue方法来对其值,而包装的Counter属性又只提供了GetValue方法,所以类外部只能对该依赖属性进行读取,而不能对其赋值。此时运行效果如下图所示。

2.5 附加属性

  WPF中还有一类特殊的属性——附加属性。附加是一种特殊的依赖属性。它允许给一个对象添加一个值,而该对象可能对这个值一无所知。附加属性最常见的例子就是布局容器中DockPanel类中的Dock附加属性和Grid类中Row和Column附加属性。那问题又来了,我们怎样在自己的类中定义一个附加属性呢?其实定义附加属性和定义一般的依赖属性一样没什么区别,只是用RegisterAttached方法代替了Register方法罢了。下面代码演示了附加属性的定义。

复制代码
public class AttachedPropertyClass{// 通过使用RegisterAttached来注册一个附加属性public static readonly DependencyProperty IsAttachedProperty =DependencyProperty.RegisterAttached("IsAttached", typeof(bool), typeof(AttachedPropertyClass),new FrameworkPropertyMetadata((bool)false));// 通过静态方法的形式暴露读的操作public static bool GetIsAttached(DependencyObject dpo){return (bool)dpo.GetValue(IsAttachedProperty);}public static void SetIsAttached(DependencyObject dpo, bool value){dpo.SetValue(IsAttachedProperty, value);}}
复制代码

  在上面代码中,IsAttached就是一个附加属性,附加属性没有采用CLR属性进行封装,而是使用静态SetIsAttached方法和GetIsAttached方法来存取IsAttached值。这两个静态方法内部一样是调用SetValue和GetValue来对附加属性读写的。

2.6 依赖属性验证和强制

   在定义任何类型的属性时,都需要考虑错误设置属性的可能性。对于传统的CLR属性,可以在属性的设置器中进行属性值的验证,不满足条件的值可以抛出异常。但对于依赖属性来说,这种方法不合适,因为依赖属性通过SetValue方法来直接设置其值的。然而WPF有其代替的方式,WPF中提供了两种方法来用于验证依赖属性的值。

  • ValidateValueCallback:该回调函数可以接受或拒绝新值。该值可作为DependencyProperty.Register方法的一个参数。
  • CoerceValueCallback:该回调函数可将新值强制修改为可被接受的值。例如某个依赖属性Age的值范围是0到120,在该回调函数中,可以对设置的值进行强制修改,对于不满足条件的值,强制修改为满足条件的值。如当设置为负值时,可强制修改为0。该回调函数可作为PropertyMetadata构造函数参数进行传递。

  当应用程序设置一个依赖属性时,所涉及的验证过程如下所示:

  1. 首先,CoerceValueCallback方法可以修改提供的值或返回DependencyProperty.UnsetValue。
  2. 如果CoerceValueCallback方法强制修改了提供的值,此时会激活ValidateValueCallback方法进行验证,如果该方法返回为true,表示该值合法,被认为可被接受的,否则拒绝该值。不像CoerceValueCallback方法,ValidateValueCallback方法不能访问设置属性的实际对象,这意味着你不能检查其他属性值。即该方法中不能对类的其他属性值进行访问。
  3. 如果上面两个阶段都成功的话,最后会触发PropertyChangedCallback方法来触发依赖属性值的更改。

  下面代码演示了基本的流程。

复制代码
 1 class Program2     {3         static void Main(string[] args)4         {5             SimpleDPClass sDPClass = new SimpleDPClass();6             sDPClass.SimpleDP = 2;7             Console.ReadLine();8         }9     }
10 
11     public class SimpleDPClass : DependencyObject
12     {
13         public static readonly DependencyProperty SimpleDPProperty =
14             DependencyProperty.Register("SimpleDP", typeof(double), typeof(SimpleDPClass),
15                 new FrameworkPropertyMetadata((double)0.0,
16                     FrameworkPropertyMetadataOptions.None,
17                     new PropertyChangedCallback(OnValueChanged),
18                     new CoerceValueCallback(CoerceValue)),
19                     new ValidateValueCallback(IsValidValue));
20 
21         public double SimpleDP
22         {
23             get { return (double)GetValue(SimpleDPProperty); }
24             set { SetValue(SimpleDPProperty, value); }
25         }
26 
27         private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
28         {
29             Console.WriteLine("当值改变时,我们可以做的一些操作,具体可以在这里定义: {0}", e.NewValue);
30         }
31 
32         private static object CoerceValue(DependencyObject d, object value)
33         {
34             Console.WriteLine("对值进行限定,强制值: {0}", value);
35             return value;
36         }
37 
38         private static bool IsValidValue(object value)
39         {
40             Console.WriteLine("验证值是否通过,返回bool值,如果返回True表示验证通过,否则会以异常的形式暴露: {0}", value);
41             return true;
42         }
43     }
复制代码

  其运行结果如下图所示:

  从运行结果可以看出,此时并没有按照上面的流程先Coerce后Validate的顺序执行,这可能是WPF内部做了一些特殊的处理。当属性被改变时,首先会调用Validate来判断传入的value是否有效,如果无效就不继续后续操作。并且CoerceValue后面并没有运行ValidateValue,而是直接调用PropertyChanged。这是因为CoerceValue操作并没有强制改变属性的值,而前面对这个值已经验证过了,所以也就没有必要再运行Valudate方法来进行验证了。但是如果在Coerce中改变了Value的值,那么还会再次调用Valudate操作来验证值是否合法。

2.7  依赖属性的监听

  我们可以用两种方法对依赖属性的改变进行监听。这两种方法是:

  • 使用DependencyPropertyDescriptor类
  • 使用OverrideMetadata的方式。

  下面分别使用这两种方式来实现下对依赖属性的监听。

  第一种方式:定义一个派生于依赖属性所在的类,然后重写依赖属性的元数据并传递一个PropertyChangedCallback参数即可,具体的实现如下代码所示:

复制代码
 1 public class MyTextBox : TextBox2     {3         public MyTextBox()4             : base()5         {6         }7 8         static MyTextBox()9         {
10             //第一种方法,通过OverrideMetadata
11             TextProperty.OverrideMetadata(typeof(MyTextBox), new FrameworkPropertyMetadata(new PropertyChangedCallback(TextPropertyChanged)));
12         }
13 
14         private static void TextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
15         {
16             MessageBox.Show("", "Changed");
17         }
18     }
复制代码

  第二种方法:这个方法更加简单,获取DependencyPropertyDescriptor并调用AddValueChange方法为其绑定一个回调函数。具体实现代码如下所示:

复制代码
 public MainWindow(){InitializeComponent();//第二种方法,通过OverrideMetadataDependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(TextBox.TextProperty, typeof(TextBox));descriptor.AddValueChanged(tbxEditMe, tbxEditMe_TextChanged);}private void tbxEditMe_TextChanged(object sender, EventArgs e){MessageBox.Show("", "Changed");}
复制代码

三、总结

   到这里,依赖属性的介绍就结束了。WPF中的依赖属性通过一个静态只读字段进行定义,并且在静态构造函数中进行注册,最后通过.NET传统属性进行包装,使其使用与传统的.NET属性并无两样。在后面一篇文章将分享WPF中新的事件机制——路由事件。

  本文所有源码下载:DependencyPropertyDemo.zip

 

http://www.cnblogs.com/zhili/p/WPFLayout.html

转载于:https://www.cnblogs.com/sjqq/p/8350365.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354190.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

圆台下料展开计算方法_怎么画 圆锥台展开图

展开全部1、 画出圆台的主视抄图(等腰梯形)袭&#xff1a;圆台的上bai下底直径为等腰梯形du的上zhi下底&#xff0c;圆台的高为等dao腰梯形的高&#xff1b;2、将等腰梯形补画成等腰三角形&#xff1b;(图中的虚线三角形即为补画部分)&#xff1b;3、以三角形的顶点为圆心&…

.31-浅析webpack源码之doResolve事件流(3)

放个流程图&#xff1a; 这里也放一下request对象内容&#xff0c;这节完事后如下(把vue-cli的package.json也复制过来了)&#xff1a; /*{ context: { issuer: , compiler: undefined },path: d:\\workspace\\doc,request: ./input.js,query: ,module: false,directory: false…

c++ 虚函数,纯虚函数的本质区别

转载博客&#xff1a;https://mp.weixin.qq.com/s?__bizMzAxNzYzMTU0Ng&amp;mid2651289202&amp;idx1&amp;sn431ffd1fae4823366a50b68aed2838d4&amp;chksm80114627b766cf31f72018ef5f1fe29591e9f6f4bd72018e7aea849342ca6f0a271fb38465ae#rd 学习C的多态性&…

云通讯短信验证码实例

1.注册登录云通讯 http://www.yuntongxun.com/user/login 2.创建应用得到应用相关信息 3.下载对应相关的Demo示例  http://www.yuntongxun.com/doc/rest/sms/3_2_2_3.html 4.send.php文件添加代码方便后续操作 session_start(); //随机验证码 $code rand(100000,999999)…

java 数组 内存_图解Java数组的内存分配

1. Java数组是静态的Java是静态语言&#xff0c;所以Java的数组也是静态的&#xff0c;即&#xff1a;数组被初始化后&#xff0c;长度不可变静态初始化&#xff1a;显式指定每个数组元素的初始值&#xff0c;系统决定数组长度String[] books new String[]{"疯狂Java讲义…

libgdx和Kotlin –类[2D平台原型]

这篇文章是libgdx和Kotlin文章的后续文章。 我已经决定开发一个简单的2D平台程序的原型&#xff08;沿着我的早期文章中的Star Assault进行介绍&#xff09;&#xff0c;但是我一直在使用和学习Kotlin&#xff0c;而不是Java。 对于本教程&#xff0c;该项目应处于上一篇文章…

spring jmx_JMX和Spring –第2部分

spring jmx这篇文章从本教程的第1部分继续。 嗨&#xff0c;在我的上一篇文章中&#xff0c;我解释了如何通过Spring设置JMX服务器以及如何通过身份验证和授权保护对它的访问。 在本文中&#xff0c;我将展示如何实现一个简单的MBean&#xff0c;该MBean允许用户在运行时更改L…

LeetCode:位运算实现加法

LeetCode&#xff1a;位运算实现加法 写在前面 位运算符 实现加法的思路 两个加数&#xff0c;比如5(101)和6(110)&#xff0c;如何不用加法就能得出两者之和呢&#xff1f; 我们知道二进制计算中&#xff0c;如果使用异或将会产生无进位的两者之和&#xff0c;而两数相与将会产…

[机器学习] 模型评价参数,准确率,召回率,F1-score

很久很久以前&#xff0c;我还是有个建筑梦的大二少年&#xff0c;有一天&#xff0c;讲图的老师看了眼我的设计图&#xff0c;说&#xff1a;“我觉得你这个设计做得很紧张”&#xff0c;当时我就崩溃&#xff0c;对紧张不紧张这样的评价标准理解无能。多年后我终于明白老师当…

java记录登陆时间_Spring security如何实现记录用户登录时间功能

一、原理分析spring security提供了一个接口 AuthenticationSuccessHandler,该接口中只有一个方法&#xff0c;用来进行登录成功后的操作public interface AuthenticationSuccessHandler {/*** Called when a user has been successfully authenticated.** param request the r…

bzoj3680

$模拟退火$ $这种全局最优的问题用模拟退火$ $模拟退火就是每次向四周随机移动&#xff0c;移动的幅度和温度成正比&#xff0c;如果新的位置更优就接受&#xff0c;否则按一定概率接收&#xff0c;概率和温度成正比$ $最后稳定后再在最优解附近蹦跶几下看看有没有更好的$ $你问…

01背包(修订版)

由于时间比较充裕&#xff0c;重新修订一部分。 这次把一些补充的放进来&#xff0c;其他的基础说明见后半部分 这些一共说明&#xff1a;01背包、完全背包、多重背包 将会详细说明。 三种背包混合、二维背包、分组背包、依赖背包、泛化背包 将大致说明。 01背包 如上次说明一…

java 马踏棋盘优化_我所知道的十大常用算法之马踏棋盘算法(深度搜索、贪心思想优化 )...

前言需求今天我们学习的是马踏棋盘算法&#xff0c;我们还是从一个场景里引入看看马踏棋盘算法也被称为骑士周游问题将马随机放在国际象棋的66棋盘Board0&#xff5e;5的某个方格中提示&#xff1a;马按走棋规则(马走日字)进行移动要求&#xff1a;每个方格只进入一次&#xff…

app engine_App Engine中的Google Services身份验证,第2部分

app engine在本教程的第一部分中&#xff0c; 我介绍了如何使用OAuth进行Google API服务的访问/身份验证。 不幸的是&#xff0c;正如我稍后发现的那样&#xff0c;我使用的方法是OAuth 1.0&#xff0c;显然现在Google正式弃用了OAuth 1.0&#xff0c;改用OAuth 2.0版本。 显然…

Django知识总结(一)

壹 ● 有关http协议 一 ● 全称 超文本传输协议(HyperText Transfer Protocol) 二 ● 协议 双方遵循的规范 http协议是属于应用层的协议(还有ftp, smtp等), 即浏览器请求消息和服务器响应消息的一系列规则 三 ● http协议的特性 http是无状态、无连接的协议(stateless, connect…

mysql+if+x+mod+2_Windows 下 MantisBT 2.X + Apache 2.4 + PHP 7 + MySQL 5.7 的环境配置

Apache 2.4.25~ Visual C Redistributable for Visual Studio 2015PHP 7.1.3~ mod_fcgid-2.3.9Mantisbt-2.2.1MySQL Database 5.7.17~ NET Framework 3.5安装 Apache前期准备1.若计算机已安装了 Apache2.4 以前的版本&#xff0c;请自行卸载并删除安装目录。2.以管理员身份运行…

如何在Java中对文件进行模式匹配和显示相邻行

最近&#xff0c;我们在jOOλ0.9.9中发布了有关超棒的窗口函数支持的文章 &#xff0c;我相信这是对我们所做的库的最佳补充。 今天&#xff0c;我们将在一个用例中研究窗口函数的出色应用&#xff0c;该用例受到以下堆栈溢出问题Sean Nguyen的启发&#xff1a; 如何从Java 8…

64位内核第二讲,进程保护之对象钩子

64位内核第二讲,进程保护. 一丶什么是保护. 什么是保护. 比如我们安装了xxx杀毒软件.那么此时你用任务管理器关闭.是关闭不了的.原因是内核已经做了保护. 那么去掉保护的前提就是你要给自己的软件做保护. 比如我们给计算器做保护. 例如下图. 做保护.以前的病毒作者.都是想要退出…

jpql hql_无需部署即可测试JPQL / HQL

jpql hql您是否曾经想在不完全部署应用程序的情况下测试JPQL / HQL&#xff1f; 我们今天在这里看到的是适用于任何JPA实现的简单解决方案&#xff1a;Hibernate&#xff0c;OpenJPA&#xff0c;EclipseLink等。 这篇文章中找到的基本源代码来自本书&#xff1a;“ Pro JPA 2&a…

eclipse 代码上传github 笔记

第一步 先share project 如图所示 第二步 如果所示 第三步 点击 下面的create 然后点击完成 第四步提交 第五步&#xff1a; 第六步&#xff1a; 转载于:https://www.cnblogs.com/a8457013/p/8410471.html