学习 Code 总有这样一个过程:入门时候比较依赖设计器、标记语言等辅助工具;等到玩熟练了就会发现纯代码写 UI 其实更高效。而且,纯代码编写也是最灵活的。Windows Forms 项目是肯定可以全代码编写的,哪怕你使用了设计器,它最后也是生成代码文件;而 WPF 就值得探索一下了。咱们知道,WPF 使用 XAML 标记来构建 UI 部分。由于 XAML 扩展了许多功能,用起来自然比 HTML 舒服。但是,老周向来不喜欢标记语言,这也是我向来不喜欢搞前端的原因。尽管某些前端框架模仿 WPF 也搞出数据绑定、MVVM、数据模板之类的名堂,也很难说用得特舒服。

有很多中小型项目都会把 Web 前端部分外包出去,尤其是给私人做——比如一两个人或两三个人做,也不外给其他公司做。有些人总以为前端很火(这里头媒体造势的功劳不小),可往深层一挖,那可不一定了。Web 其实只做了一套 UI 罢了,后端许多是通用模型,既可以和 Web 前端对接,也可以和桌面前端对接,B/S、C/S 通杀的项目也不少。

很多行业软件,如工业医疗,甚至财务、进销存等,还是用成熟的技术好,尤其是桌面技术体验更佳。当然有些行业软件也有 Web UI,纯辅助,一般就是看看报表看看大图查查订单而已。生产力悠关的东西,你还得相信桌面的魅力,娱乐相关的就随便。当然也有用 Web 技术开发桌面UI的框架,这些东西能用但效果不算好,尤其是性能。老周这里说的性能只是要求较宽的性能,而不是苛刻要求下的性能。啥意思呢,就是说用 Web 技术做桌面程序,存在性能问题不需要专用工具测,肉眼就能感觉到严重的性能问题了——吃内存特大,占CPU有亿点高(虽说占得不算恐怖)。这里所说的性能问题要排除 VS Code,因为这货是个奇葩,性能表现挺好。

许多人容易被表面现象迷惑,比如认为招聘信息多的就以为很吃香。那可不一定,有些技术,招聘少并不代表用的人少。老在招聘的顶多说明这些岗位流动性大,这个公司的员工热爱跳槽罢了。近年来 Python 被“利益携带体”们炒得可热了,甚至一些新手以为 Python 是刚出来的新语言。你想多了,就算没有 C 语言早(1972),那也是 80 年代末的东东了。我在学 Python 的时候,估计某些小菜鸟还没出生呢。不要听那些培训班胡说八道,它们的目的是你的钱包,而不是你的码农生涯,它们说话从来不需要负责的。如果你除了 Python 什么都不会的话,那除了会写点“脚毛”外,你什么也干不成。不管你想玩人工智障、视觉神经还是别的东西,你得掌握C语言,特别是想搞更底层的。只会 Py 没准连工作都难找,更别说年薪 500W 越南币了。

在你不知道的领域,你可曾想象,VB6、易语言、Delphi、MFC 还有不少人在用呢。告诉你个秘密,学好汇编可能更吃香,以后会这个的人更少了。信不信由你。当然,积极学习新东西是没错的,这也老周一向主张。不过,你同时得清楚,许多技术之间并不存在相互替代的关系,只不过是你做什么样的程序,就用什么样的技术罢了。比如这桌面程序,你不用纠结,很简单:考虑跨平台的,首选 Qt;仅考虑 Windows 的,那多了去,随便,当然,微软自家自然是最合适的。

可是,一些脑子太灵活的人又纠结了,我选了 Qt,那我用 Widgets 做还是用 QML 做?我选了.NET,那我用 Windows Forms 还是 WPF?还是 MAUI ?对于这种问题,老周送你一句:“像你们这种人是没法改变的,只有滚出码农界”。

好了,上面扯了几段“废腑”之话,回归正题,咱们讨论 WPF,老周这里说的是完全用代码写,指的是一行 XAML 都没有。当然,大伙伴们肯定说那没问题的。构建常规界面绝对行得通,但遇到像数据模板、控件模板、资源字典这些,就得费一点点代码。虽然网上能找到几位同道中人写的小作文,但要么版本太旧,要么过于粗糙。于是老周逮住了这个机会,可以瞎扯蛋一回了。

前文多次强调,咱们就纯代码写 WPF 的,无一行 XAML。所以,默认的 WPF 项目模板咱们就不用了。咱们用控制台应用的模板就行了。来,动手练习一下。

首先,创建一个控制台项目。

dotnet new console -n MyApp -o .

dotnet new 命令知道乎?嗯,用来创建项目的,然后是项目模板的名称,console 表示控制台应用程序。模板名字我记不住哟。记它干吗,执行一下下面这一句就能看各种模板了:

dotnet new list

这里你可别理解歪了,它不是说用名叫 list 的模板创建项目啊,list 是列出可用的项目模板。然后,你能得到这个表:

模板名                                   短名称               语言        标记
--------------------------------------- ------------------- ---------- --------------------------------
ASP.NET Core gRPC 服务 grpc [C#] Web/gRPC
ASP.NET Core Web API webapi [C#],F# Web/WebAPI
ASP.NET Core Web 应用 webapp,razor [C#] Web/MVC/Razor Pages
ASP.NET Core Web 应用(模型-视图-控制器) mvc [C#],F# Web/MVC
ASP.NET Core 与 Angular angular [C#] Web/MVC/SPA
ASP.NET Core 与 React.js react [C#] Web/MVC/SPA
ASP.NET Core 空 web [C#],F# Web/Empty
Blazor Server 应用 blazorserver [C#] Web/Blazor
Blazor Server 应用空 blazorserver-empty [C#] Web/Blazor/Empty
Blazor WebAssembly 应用 blazorwasm [C#] Web/Blazor/WebAssembly/PWA
Blazor WebAssembly 应用空 blazorwasm-empty [C#] Web/Blazor/WebAssembly/PWA/Empty
dotnet gitignore 文件 gitignore Config
Dotnet 本地工具清单文件 tool-manifest Config
EditorConfig 文件 editorconfig Config
global.json file globaljson Config
MSBuild Directory.Build.props 文件 buildprops MSBuild/props
MSBuild Directory.Build.targets 文件 buildtargets MSBuild/props
MSTest Test Project mstest [C#],F#,VB Test/MSTest
MVC ViewImports viewimports [C#] Web/ASP.NET
MVC ViewStart viewstart [C#] Web/ASP.NET
NuGet 配置 nugetconfig Config
NUnit 3 Test Item nunit-test [C#],F#,VB Test/NUnit
NUnit 3 Test Project nunit [C#],F#,VB Test/NUnit
Razor 类库 razorclasslib [C#] Web/Razor/Library
Razor 组件 razorcomponent [C#] Web/ASP.NET
Razor 页面 page [C#] Web/ASP.NET
Web 配置 webconfig Config
Windows 窗体应用 winforms [C#],VB Common/WinForms
Windows 窗体控件库 winformscontrollib [C#],VB Common/WinForms
Windows 窗体类库 winformslib [C#],VB Common/WinForms
WPF 应用程序 wpf [C#],VB Common/WPF
WPF 用户控件库 wpfusercontrollib [C#],VB Common/WPF
WPF 类库 wpflib [C#],VB Common/WPF
WPF 自定义控件库 wpfcustomcontrollib [C#],VB Common/WPF
xUnit Test Project xunit [C#],F#,VB Test/xUnit
协议缓冲区文件 proto Web/gRPC
接口 interface [C#],VB Common
控制台应用 console [C#],F#,VB Common/Console
枚举 enum [C#],VB Common
类 class [C#],VB Common
类库 classlib [C#],F#,VB Common/Library
结构 struct,structure [C#],VB Common
解决方案文件 sln,solution Solution
记录 record [C#] Common
辅助角色服务 worker [C#],F# Common/Worker/Web

咱们平常用得多的都是前几那几个,比如 mvc、web、wpf、classlib 等。我们在命令中引用的就是项目模板的短名称即可。比如控制台就是 console。

-n 参数指定项目的名称,我这里是“MyApp”,-o 参数指定项目存放目录,“.” 表示当前目录。

接下来要改一下项目文件(*.csproj)。

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup> </Project>

1、添加 MSBuild 属性 UseWPF,且设置为 true。有了这个你才能在项目中引用 WPF 有关的程序集。同理,如果要使用 Windows Forms,就将 UseWindowsForms 属性设置为 true。

2、TargetFramework 要在.NET版本后加上“-windows”,表示这是 Windows 平台特定的,Linux 上不可用的。类似的如 net7.0-android 等。

至于 OutputType 属性要不要改为 WinExe,.NET 5 以上是不需要的,它会自动判断启不启动控制台窗口。

好了,保存,关闭项目文件。可以写代码了。

在写代码前,咱们先理清楚一些核心对象的关系。你才会知道怎么写。Application 类是 WPF 程序启动的核心对象,通常表示该应用程序相关的初始化。所以,在 Main 方法中记得 new 一个。你可别太聪明,千万不要直接从 Application.Current 静态属性来获取。因为这时应用程序还没初始化呢,Current 属性还是 null。Current 属性适合在项目的其他代码中方便访问 Application 对象而使用的。

如果你没别的东西初始化,那就调用 Application 对象的 Run 方法。应用程序正式启动,并且主线程会卡在(也不是真的卡)这里,直到程序要退出了才从 Run 方法返回。其间,调度器会不断调度/处理各线程上的消息,直到消息循环终止。

窗口应用程序当然要一个主窗口。表示窗口的基类是 Window,可以直接用它,也可以派生出自己的类,然后初始化要在窗口上显示的控件。

public class MyWindow:Window
{
public MyWindow()
{
InitUI();
} private void InitUI()
{
// 本窗口的属性
this.Title = "鼠爷快乐园";
this.Height = 225;
this.Width = 315;
// 启动时窗口在屏幕中央
WindowStartupLocation = WindowStartupLocation.CenterScreen;
// 整点控件
// 两个block
TextBlock tb1 = new()
{
Text = "每天毙一鼠",
TextAlignment = TextAlignment.Center,
// 文本颜色
Foreground = new SolidColorBrush(Color.FromRgb(12, 50, 208))
};
TextBlock tb2 = new()
{
Text = "添寿又增福",
TextAlignment = TextAlignment.Center
};
// 再加一个按钮
Button btn = new()
{
Content = "行动起来",
Margin = new Thickness(0d, 15d, 0d, 2d)
};
// 单击事件
btn.Click += OnClick;
// 布局控件
StackPanel panel = new();
// 垂直方向
panel.Orientation = Orientation.Vertical;
// 添加子元素
panel.Children.Add(tb1);
panel.Children.Add(tb2);
panel.Children.Add(btn);
// 作为窗口的内容
this.Content = panel;
} private void OnClick(object sender, RoutedEventArgs e)
{
MessageBox.Show("自在其间乐");
}
}

Windows 属于内容控件,公开 Content 属性,用来设置单个对象引用。上述代码先创建两个 TextBlock 实例和一个 Button 实例,然后把它们塞进 StackPanel 中,再把 StackPanel 实例赋值给窗口的 Content 属性。

窗口类写好后,在 Main 方法中,调用 Run 方法时把窗口实例传进去。

    [STAThread]
static void Main(string[] args)
{
Application app = new();
app.Run(new MyWindow());
}

这个程序已经可以运行了。

************************************************************************************************

咱们继续探索。如果要用到数据绑定呢。在 XAML 中是用 {Binding} 扩展标记的,而在代码中对应的是 Binding 类,位于 System.Windows.Data 命名空间。

Binding 类的构造函数可以传递一个字符串常量,对应 {Binding Path=... } 中的 Path,即要绑定的对象路径。数据源则由 Source 属性设置。

public Binding(string path);

关联绑定用的是 BindingOperations 类的静态方法 SetBinding,要获取已关联的 Binding 对象就调用 GetBinding 方法。

public static Binding GetBinding(DependencyObject target, DependencyProperty dp);
public static BindingExpressionBase SetBinding(DependencyObject target, DependencyProperty dp, BindingBase binding);

BindingOperations 类本身是静态类,所以它的成员自然也是静态的。target 参数是绑定目标,即 WPF 对象,dp 是要绑定的依赖属性,binding 就是Binding对象。

咱们举个例子。

假设用以下类作为数据源。

public class Student
{
public int ID { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
}

窗口的结构:内容根为 Grid 对象,它包含三行两列,用来放六个 TextBlock 控件。

        Grid layout = new();
// 设置为窗口内容
this.Content = layout;
// 设置边距
layout.Margin = new Thickness(13.5d);
// 三行两列
layout.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = GridLength.Auto
});
layout.ColumnDefinitions.Add(new ColumnDefinition(){
// 星号,即 1*
Width = new GridLength(1.0d, GridUnitType.Star)
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
});

ColumnDefinitions 用来定义列。上述代码中,第一列的宽度为 Auto,第二列的宽度为 *。

RowDefinitions 集合用来定义行。上述代码中,三行的高度都是 Auto。

然后 new 出六个 TextBlock,三个用来显示字段标签,三个用于数据绑定,显示属性值。

        // 六个block
var tbIDf = new TextBlock(){
Text = "学号:"
};
var tbNamef = new TextBlock()
{
Text = "姓名:"
};
var tbAgef = new TextBlock()
{
Text = "年龄:"
}; TextBlock tbIDv = new();
var tbNamev = new TextBlock();
var tbAgev = new TextBlock();

把六个 TextBlock 控件添加到 Grid 的子级中。

        layout.Children.Add(tbIDf);
layout.Children.Add(tbIDv);
layout.Children.Add(tbNamef);
layout.Children.Add(tbNamev);
layout.Children.Add(tbAgef);
layout.Children.Add(tbAgev);

设置子元素所在行、列的 Grid.Row 和 Grid.Column 是附加属性,以 Grid.SetXXX 方法调用。

    layout.Children.Add(tbIDf);
layout.Children.Add(tbIDv);
layout.Children.Add(tbNamef);
layout.Children.Add(tbNamev);
layout.Children.Add(tbAgef);
layout.Children.Add(tbAgev);
// Row、Column 附加属性
// 第一行第一列
Grid.SetRow(tbIDf, 0);
Grid.SetColumn(tbIDf, 0);
// 第一行第二列
Grid.SetRow(tbIDv, 0);
Grid.SetColumn(tbIDv, 1);
// 第二行第一列
Grid.SetRow(tbNamef, 1);
Grid.SetColumn(tbNamef, 0);
// 第二行第二列
Grid.SetRow(tbNamev, 1);
Grid.SetColumn(tbNamev, 1);
// 第三行第一列
Grid.SetRow(tbAgef, 2);
Grid.SetColumn(tbAgef,0);
// 第三行第二列
Grid.SetRow(tbAgev, 2);
Grid.SetColumn(tbAgev, 1);

最后是创建三个 Binding ,为 Student 类的三个属性做绑定。

    /* ID */
Binding bindID = new(nameof(Student.ID))
{
Source = this.stu
};
BindingOperations.SetBinding(tbIDv, TextBlock.TextProperty, bindID);
/* Name */
Binding bindName = new(nameof(Student.Name))
{
Source = stu
};
BindingOperations.SetBinding(tbNamev, TextBlock.TextProperty,
bindName);
/* Age */
Binding bindAge = new(nameof(Student.Age))
{
Source = stu
};
BindingOperations.SetBinding(tbAgev, TextBlock.TextProperty, bindAge);

完整的初始化方法代码如下:

private void InitUI()
{
Title = "数据绑定";
Width = 240;
Height = 185;
// 创建Grid
Grid layout = new();
// 设置为窗口内容
this.Content = layout;
// 设置边距
layout.Margin = new Thickness(13.5d);
// 三行两列
layout.ColumnDefinitions.Add(new ColumnDefinition()
{
Width = GridLength.Auto
});
layout.ColumnDefinitions.Add(new ColumnDefinition(){
// 星号,即 1*
Width = new GridLength(1.0d, GridUnitType.Star)
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
});
layout.RowDefinitions.Add(new()
{
Height = GridLength.Auto
}); // 六个block
var tbIDf = new TextBlock(){
Text = "学号:"
};
var tbNamef = new TextBlock()
{
Text = "姓名:"
};
var tbAgef = new TextBlock()
{
Text = "年龄:"
};
TextBlock tbIDv = new();
var tbNamev = new TextBlock();
var tbAgev = new TextBlock();
// 把六个block放到 grid 上
layout.Children.Add(tbIDf);
layout.Children.Add(tbIDv);
layout.Children.Add(tbNamef);
layout.Children.Add(tbNamev);
layout.Children.Add(tbAgef);
layout.Children.Add(tbAgev);
// Row、Column 附加属性
// 第一行第一列
Grid.SetRow(tbIDf, 0);
Grid.SetColumn(tbIDf, 0);
// 第一行第二列
Grid.SetRow(tbIDv, 0);
Grid.SetColumn(tbIDv, 1);
// 第二行第一列
Grid.SetRow(tbNamef, 1);
Grid.SetColumn(tbNamef, 0);
// 第二行第二列
Grid.SetRow(tbNamev, 1);
Grid.SetColumn(tbNamev, 1);
// 第三行第一列
Grid.SetRow(tbAgef, 2);
Grid.SetColumn(tbAgef,0);
// 第三行第二列
Grid.SetRow(tbAgev, 2);
Grid.SetColumn(tbAgev, 1);
// 数据绑定
/* ID */
Binding bindID = new(nameof(Student.ID))
{
Source = this.stu
};
BindingOperations.SetBinding(tbIDv, TextBlock.TextProperty, bindID);
/* Name */
Binding bindName = new(nameof(Student.Name))
{
Source = stu
};
BindingOperations.SetBinding(tbNamev, TextBlock.TextProperty,
bindName);
/* Age */
Binding bindAge = new(nameof(Student.Age))
{
Source = stu
};
BindingOperations.SetBinding(tbAgev, TextBlock.TextProperty, bindAge);
}

运行效果如下:

这时候,估计你也想到了一件事—— WPF 元素之间的绑定咋弄?对应的 XAML 扩展标记 {Binding ElementName=..., Path=... }。这个用代码写起来也不难,Binding 类有 ElementName 属性,可以引用已命名的对象。但是,在代码里面,咱们是直接用变量名来引用对象的,并没有分配对象名称。虽然 FrameworkElement 类的子类都继承了 Name 属性,但,设置这个 Name 属性 Binding.ElementName 是找不到的,必须要在 NameScope 对象里注册到XAML名称空间后才能被 ElementName 引用。

NameScope 类其实是个 Key=String, Value = Object 的字典,维护当前名称空间范围内的对象列表。对应的是 XAML 中的 x:Name = ...。
NameScope 类定义了 NameScope 附加属性,允许将 NameScope 实例设置到目标对象上。XAML 语法是
<NameScope.NameScope>
<NameScope />
</NameScope.NameScope>

但我们在写 XAML 时是不需要,都是自动添加的,用 x:Name 就行了。

在代码中用 SetNameScope 方法设置。

看看下面的例子。

void InitUI()
{
Title = "元素之间绑定";
// 根据内容自动调整窗口大小
SizeToContent = SizeToContent.WidthAndHeight;
StackPanel panel = new(){
Orientation = Orientation.Vertical,
Margin = new Thickness(15.0d)
};
this.Content = panel; // 布局
// 文本输入控件
TextBox txt = new TextBox();
txt.Margin = new Thickness(3.0d, 5.0d, 3.0d, 8.5d);
// 给它分配一个名字,绑定时用到
NameScope myScope = new();
NameScope.SetNameScope(this, myScope);
myScope.RegisterName("txtInput", txt);
// 文本块
TextBlock tb = new TextBlock();
tb.Margin = new Thickness(5.0d, 0d, 5.0d, 0d);
// 绑定
Binding bind = new();
bind.ElementName = "txtInput";
bind.Path = new PropertyPath(TextBox.TextProperty);
bind.Mode = BindingMode.OneWay;
// 在文本框更改时更新数据
bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(tb, TextBlock.TextProperty, bind);
// 添加到布局
panel.Children.Add(txt);
panel.Children.Add(tb);
}

这个方法也是写在 Window 的派生类中,SizeToContent = SizeToContent.WidthAndHeight 表示本窗口的宽度和高度会根据它要显示的内容自动调整。

由于 TextBlock 控件的文本来源于 TextBox,因此,要为 TextBox 注册一个名字“txtInput”。由于 FrameworkElement 类有 RegisterName 方法,所以,注册名称的代码也可以这样写:

NameScope.SetNameScope(this, new NameScope());
this.RegisterName("txtInput", txt);

设置 bind.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged 允许在输入的内容更改后就马上更新绑定数据,能做到实时显示输入的内容。

效果如下:

好了,今天咱们先说到这儿,剩下的如模板、样式、动画、3D 什么的,咱们下次再探讨。

【.NET 深呼吸】全代码编写WPF程序的更多相关文章

  1. 纯javascript代码编写计算器程序

    今天来分享一下用纯javascript代码编写的一个计算器程序,很多行业都能用到这个程序,例如做装修预算.贷款利率等等. 首先来看一下完成后的效果: 具体代码如下:(关注我的博客,及时获取最新WEB前 ...

  2. 大数据之路week07--day03(Hadoop深入理解,JAVA代码编写WordCount程序,以及扩展升级)

    什么是MapReduce 你想数出一摞牌中有多少张黑桃.直观方式是一张一张检查并且数出有多少张是黑桃. MapReduce方法则是: 1.给在座的所有玩家中分配这摞牌 2.让每个玩家数自己手中的牌有几 ...

  3. 编写WPF程序,完成弹框打印和直接打印

    弹框打印 PrintDialog pd = new PrintDialog(); pd.ShowDialog(); //↓第一个参数是StackPanel控件里面放一个label放打印的文字 pd.P ...

  4. 编写高质量代码改善C#程序的157个建议——建议87:区分WPF和WinForm的线程模型

    建议87:区分WPF和WinForm的线程模型 WPF和WinForm窗体应用程序都有一个要求,那就是UI元素(如Button.TextBox等)必须由创建它的那个线程进行更新.WinForm在这方面 ...

  5. [WPF]程序全屏

    原文:[WPF]程序全屏 代码: 使用:

  6. 编写高质量代码改善C#程序的157个建议——建议138:事件和委托变量使用动词或形容词短语命名

    建议138:事件和委托变量使用动词或形容词短语命名 事件和委托使用场景是调用某个方法,只不过这个方法由调用者赋值.这决定了对应的变量应该以动词或形容词短语命名. 关于事件和委托变量妥当的命名示例如下: ...

  7. 编写高质量代码改善C#程序的157个建议——建议103:区分组合和继承的应用场合

    建议103:区分组合和继承的应用场合 继承所带来的多态性虽然是面向对象的一个重要特性,但这种特性不能在所有的场合中滥用.继承应该被当做设计架构的有用补充,而不是全部. 组合不能用于多态,但组合使用的频 ...

  8. 编写高质量代码改善C#程序的157个建议——建议85:Task中的异常处理

    建议85:Task中的异常处理 在任何时候,异常处理都是非常重要的一个环节.多线程与并行编程中尤其是这样.如果不处理这些后台任务中的异常,应用程序将会莫名其妙的退出.处理那些不是主线程(如果是窗体程序 ...

  9. 编写高质量代码改善C#程序的157个建议——建议79:使用ThreadPool或BackgroundWorker代替Thread

    建议79:使用ThreadPool或BackgroundWorker代替Thread 使用线程能极大地提升用户体验度,但是作为开发者应该注意到,线程的开销是很大的. 线程的空间开销来自: 1)线程内核 ...

  10. 编写高质量代码改善C#程序的157个建议——建议66:正确捕获多线程中的异常

    建议66:正确捕获多线程中的异常 多线程的异常处理需要采用特殊的方式.一下这种方式会存在问题: try { Thread t = new Thread((ThreadStart)delegate { ...

随机推荐

  1. What's the best way to read and understand someone else's code?

    Find one thing you know the code does, and trace those actions backward, starting at the end Say, fo ...

  2. windows通过修改注册表来修改暂停更新时间

    但通过修改注册表,我们可以将这个天数修改成自己期望的,比如10年. 在小娜或者运行中输入 regedit 打开注册表编辑器,展开至 HKEY_LOCAL_MACHINE\SOFTWARE\Micros ...

  3. 自用纯C语言实现任务调度(可用于STM32、C51等单片机)

    前言   这个任务调度模块的实现是形成于毕设项目中的,用在STM32中,断断续续跨度2个月实现了一些基本功能,可能后面再做其他项目时会一点点完善起来,也会多学习相关知识来强化模块的实用性和高效性,毕竟 ...

  4. LeeCode 433 最小基因变化

    LeeCode 433 最小基因变化 题目描述: 基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 'A'.'C'.'G' 和 'T' 之一. 假设我们需要调查从基因序列 start ...

  5. 【SpringMVC】(三)

    HTTPMessageConverter HttpMessageConverter报文信息转换器,将请求报文转换为java对象,或将java对象转换为响应报文. 1 @ResquestBody Res ...

  6. Java学习笔记09

    1. 多态 1.1 多态 ​ 多态是指同一种行为具有多种不同的表现形式. 前提 有继承或者实现关系 有方法重写(没有重写多态就没有意义) 父类引用指向子类对象 格式 父类类型 变量名 = new 子类 ...

  7. Zabbix - 部署随笔

    部署Zabbix服务端 准备机器,初始化环境 #查看IP地址 [root@Minimal ~]# ifconfig ens33 | awk 'NR==2{print $2}' 10.0.0.243 # ...

  8. AI测试101:测试AI系统的实用技巧&ML和AI自动化工具

    基于人工智能的系统,也称为神经网络(NN Neural Networks),和其他应用程序一样是 "系统",因此需要测试.本文将指导你测试AI和基于NN的系统,并理解相关概念. 测 ...

  9. node服务容器 serve 和 http-serve

    参考:https://www.cnblogs.com/leee/p/5502727.html 项目打包文件本地起serve运行 不能直接双击index.html文件直接运行 放到http容器里运行 s ...

  10. [C++核心编程] 4.7 多态

    文章目录 4.7 多态 4.7.1 多态的基本概念 4.7.2 多态案例一-计算器类 4.7.3 纯虚函数和抽象类 4.7.4 多态案例二-制作饮品 4.7.5 虚析构和纯虚析构 4.7.6 多态案例 ...