伏鹏飞

疑难杂症博物馆,.net老司机

Blog

03 Jul 2020

WPF路由事件的答疑解惑

在理解WPF路由事件之前我们先来回顾下传统的.net事件模型。事件的本质是一种观察者模式,具备当某个行为发生时,通知订阅事件的代码执行某些事情的特征。 很多人(比如我)刚接触事件这个章节的时候会感到很不适应,无法理解为什么需要使用事件的编程方式,难道当默写行为发生时直接调用订阅者的相关代码不香么?其实我认为事件提高了开发效率,降低了代码耦合;因为在实际开发的过程中,团队中的开发发人员都有自己的功能逻辑域,比如:职员管理的系统中,在删除或者新增一个一条记录后需要发出邮件通知这样的场景,那么开发A负责职员档案的CURD逻辑,开发B负责邮件发送逻辑实现,如果没有使用事件模式的话,开发A可能需要调用开发B的发送邮件代码,这样的话如果邮件代码有变动,那么开发A不得不重新编译代码发布,或者新增了一个同时需要发送短信和邮件的业务逻辑,那么开发A又需要改动代码增加发送短信逻辑。但是事件模式下,开发A只需要定义一个事件,在完成职员的新增或者删除后触发事件,只要订阅了该事件的模块就会收到事件发送者传递的事件参数从而自己执行相关的代码逻辑。下面我们通过代码介绍下事件模式: 假设没有 using System; namespace SimpleEvent { using System; public class Master { private int value; public Slave slave;...

30 Jun 2020

C#中泛型扩展方法

这两天有个朋友发来这样一段代码,无法编译通过: public static IEnumerable<T> Except<T>(this IEnumerable<T> soucrce, T item) { return soucrce.Where(p=> p!=item); } vs会提示:运算符“!=”无法运用于T和T…。首先我们先来查找一下报错的原因:因为T是一个泛型,在编译时无法确定类型,而操作符并“!=”不是所有类型都有的,所以vs提示错误。那么 我们需要怎么改动呢? ###方法一 public static IEnumerable<T>...

30 May 2020

WPF中使用StyleSelector实现多样元素展现

在《WPF高级编程》中作者介绍StyleSeletor时,使用了ListView控件展现了器功能;这篇文章将使用ItemControl介绍StyleSelector展现它更强大的编辑能力。 首相我们介绍在XAML里面定义如下: <UserControl x:Class="AGVDisplay.UserControls.DiagramControl" 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" xmlns:local="clr-namespace:AGVDisplay.UserControls" xmlns:s="clr-namespace:AGVDisplay.StyleSelectors" xmlns:ucdp="clr-namespace:UICommon.UserControls.DrawParts;assembly=UICommon" xmlns:att="clr-namespace:UICommon.AttachedProperties;assembly=UICommon" xmlns:arrows="clr-namespace:UICommon.UserControls.DrawParts.Arrows;assembly=UICommon" xmlns:con="clr-namespace:UICommon.Converters;assembly=UICommon" mc:Ignorable="d" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:hc="https://handyorg.github.io/handycontrol" d:DesignHeight="450" d:DesignWidth="800"> <Border>...

09 May 2020

async和await 线程上下文的问题

大家都知道使用async和await可以极大的方便开发人员使用异步编程,但是有些时候,我们可能会困扰某些代码是在什么线程上执行的,特别是在界面图形变成时,如果在UI线程之外处理调用控件则会引起异常。 下面我们看一段控制台代码: public class A { public void show() { Console.WriteLine($"一 :{Thread.CurrentThread.ManagedThreadId}"); M1(); Console.WriteLine($"四 :{Thread.CurrentThread.ManagedThreadId}"); } private async Task M1()...

05 May 2020

关于Dapper使用的一些技巧和知识点

这两天在写AGV5.0的架构,其中数据访问想使用Dapper,因为这个系统对数据读写太频繁,又不能使用redis这类内存数据库,所以dappers是最理想的选择。 在之前的项目里面我都会将数据层单独抽离出来,类似一个DAL的组件。但是这个系统由于行业软件的特殊性所以不打算再单独剥离DAL层,只需将数据操作写一个通用的类,demo代码如下: public class DBHelper { public static string connectionString = "Server=.\\MSSQL12; Database=AGV3.0; User ID=sa; Password=11;"; public static int Insert(string...

13 Apr 2020

C# 使用内存流复制实现对象深拷贝

在实际开发过程中,我们常常需要实现对某个对象实现复制从而生成一个对象的副本,同时新生成的副本与原来的对象是完全独立的,可以使用以下代码: /// <summary> /// 实现对象的深拷贝 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="obj">对象</param> /// <returns>T</returns> public static T CreateDeepCopy<T>(T obj)...

14 Mar 2020

windows国际化遇到的小数问题

这两天把程序交给俄罗斯的客户试用时,出现了奇怪的问题,查询后发现程序生成的文件里面格式化好的字符窜的小数点都变成了英文逗号,;搜索了下发现有些欧洲小国家的小数点是有逗号表示的,.net在格式化时候会根据系统的设置生成。 string.format("{0:f2}",3.1415); //3,14 如果想要将系统的小数掉用点号表示,可以用下面代码: private static void SetInternation() { RegistryKey rk = Registry.CurrentUser.OpenSubKey(@"Control Panel\International", true); if (rk.GetValue("sDecimal").ToString() != ".") {...

09 Mar 2020

windows下方便快捷的打开dos并到指定目录

这次给大家分享的是快速打开dos并且运行目录为我们需要的目录。 方式一: 我们打开我们想要启动的目录文件夹比如e:\website,然后按住shift键并且鼠标右键点击,这时候右键菜单湖出现在此处打开powershell或者在此处打开cmd 我们选择后,那么启动的dos窗口并且路径为e:\website。 方式二: 打开目录文件夹比如e:\website,直接在地址栏输入cmd或者powershell。

05 Mar 2020

C# 中byte转int 高级方式

今天和大家分享一下C#中 byte转int的一点知识,如果你已经熟悉,那么就当我是个菜鸟吧,连这个这要写篇博文。 写代码接触最多的就是数值类型,我们习惯了声明一个int表示一个数值,但是接触到上位机通讯后,这一切都变得不那么随意了,我们知道,在通讯数据包中都是用一串byte表示数据,当我们拿到数据后需要检查收到的数据是什么样的,现在假设有一个场景如下: 1.某个byte表示传过来的音量值,数值范围是0-100;那么一个byte完全可以存下,这个时候byte与int存储值是等价的,我们可以使用int i=byte[1]这样的代码。 2.假设传输的指令中还包含编号信息,范围是0-5000,那么这个时候一个byte是表示不了的(一个byte有8个bit 也就是2的8次方个数值:0-255),所以需要至少两个字节表示。通常我们会规定高字节在前。假设这两个字节是byte[0]byte[1],那么我们可以使用int i =byte[1]<<8 +byte[0]得到想到的数值。

05 Mar 2020

Asp.net core为什么使用taghelper

今天和大家分享一下Asp.net core中为什么需要使用TagHelper. asp.net core为我们提供了TagHelper,让我们在html标签中可以指定控制器、方法、以及参数等等,比如下面一行代码: <a class="navbar-brand" asp-controller="Home" asp-action="Index" >WebApplication1</a> 上面代码运行后,会产生一个a标签。熟悉razor的小伙伴可能会问,为什么我们不直接写href属性,而需要使用TagHelper定义,其实在复杂项目中,我们有各种路由规则,假设我们路由规则是: app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "test/{controller=Home}/{action=Index}/{id?}"); }); 那么在我们使用TagHelper后...

01 Mar 2020

分项C#操作注册表时权限问题答疑解惑

今天和大家分享一下C#操作注册表时遇到的问题。 前些时候写业务逻辑时需要持久化一些数据到注册表,C#操作注册表非常简单,相关的解决方案网上也非常多,接下来为大家稍微说明一下: RegistryKey key = Registry.ClassesRoot; //key = Registry.CurrentUser; //key = Registry.LocalMachine; //key = Registry.Users; //key = Registry.CurrentConfig; C#中采用 Registry类定义一个注册表对象。分别可以定义ClassesRoot、CurrentUser、LocalMachine、Users、CurrentConfig。这几个选项分别对应了注册表根目录的几个类型:...

01 Mar 2020

关于Async和await的思考

今天和大家分享一下C# async和await的一点想法。 关于用法上,这篇文章不做敷述。网上有大量的博文介绍,本真只是将我理解这个技术过程中,自己感到比较关键的点与大家做分享。 首先我们要记住一句话:await 之后的代码将以委托的的形式在原有的线程上执行。这句话非常重要。我之前一直纠结于为什么有时候界面阻塞,有时候没有阻塞。其实就是没有注意微软官方文档的这句话。我们有一段代买描述下: private async void button1_Click(object sender, EventArgs e) { var t = Task.Run(() => { Thread.Sleep(5000);...

26 Feb 2020

WPF中自定义控件绑定的一些探索

今天分享一下WPF中的用户自定义控件如何定义一些属性并且将他们绑定到界面上。 如果之前接触的了较长时间的winform程序开发,我们可能一下子比较难接受wpf这种“没头没脸”的控件使用方式。在winform中我们实现一个用户自定义控件,我们采用的方式一般是: 1.新建用户控件。 2.在设计界面添加一些基础控件。 3.在用户控件的cs文件中编写一些功能逻辑代码、增加一些属性,抛出来给使用的设置。 同样,在wpf中我们也是相同的流程,但是,为了利用好wpf中的绑定以及动画效果,我们需要将定义的属性变为依赖属性;至少只有依赖属性才能执行动画。 下面我们来结合一个小例子来工看看绑定自身属性,例子是这样的:我们需要做一个时钟控件,有小时刻度和分钟刻度,并且有三个指针分别代表时分秒的指针,在调用时可以指定三个指针的颜色分别是什么,为了方便理解,我先把效果图贴出来。 图中刻度和指针的画法这里不做详细描述,就是用不通大小的Rectangle围着时钟中心点旋转。 具体的代码如下: <UserControl x:Class="NewEzposter.Controls.UCClockArea" 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" xmlns:local="clr-namespace:NewEzposter.Controls" mc:Ignorable="d" x:Name="UserControl" d:DesignHeight="200" d:DesignWidth="200">...

24 Feb 2020

C#获取拼接屏幕内容生成图片

之前有个需求获取屏幕生成图片提交给服务器(其实就是服务端有个按钮,让客户端截图)。公司前辈写的代码是采用.net框架的API。获取屏幕的长宽来生成图片。但是后来由于业务需要,针对拼接屏幕的截屏处理失效。所以我和后来换了方案: 模拟键盘的PrintScreen按键。之后从剪贴板获取数据生成图片。非常方便,而且无关驱动,不管多屏幕设置的是复制还是扩展都可以很好的支持。 ScreenPrint.printScreen(); IDataObject data = System.Windows.Forms.Clipboard.GetDataObject(); //Metafile metafile = MetafileHelper.GetEnhMetafileOnClipboard(IntPtr.Zero); //从粘贴板获取数据 Image bm = (Image)(data.GetData(typeof(System.Drawing.Bitmap))); using (Graphics g =...

24 Feb 2020

C#中的STA线程

单线程单元访问外部是串行的,在前一个任务完全结束之前不会进行下一个任务(an object is always executed to completion before another is invoked),开发者一般情况下可以不用担心竞争和死锁等问题(The developer therefore does not need to worry about thread...

20 Feb 2020

Asp.net Core MVC 登录后返回登录前页面的技巧

本文记录最近使用ASP.net Core做Web应用服务时登录用到的技巧。 在一些场景下,客户可以浏览web应用中的一些页面且不写要登录,但客户需要进行某些操作时,此时到登录页面。比如我们浏览“知乎”时候可以不登录,如果需要进行评论,那么会跳转到登录页面。此时用户如果登录完成后直接跳转到首页而不是刚刚浏览的内容,那么体验会大大降低(也许再也找不到刚刚的帖子)。 那么我们怎么在登录后回到之前的页面呢? asp.net core MVC中如果authorize验证不通过,会跳转到指定的页面(比如登录页面)。 首先我们注册服务指定无权限是跳转的方式如下: services.ConfigureApplicationCookie(options => { options.LoginPath = "/Account/Login"; options.LogoutPath = "/Account/Login"; options.ExpireTimeSpan = new...

18 Feb 2020

Asp.net Core 部署的答疑解惑

本文记录下在Asp.net Core在署时候可能会遇到的一些疑问。 本文针对有.net framework web程序部署经验并刚刚接触到asp.net core的小伙伴,且暂时不介绍Lunix上部署 — 作为一名有过.net framework部署经验的人员,我们知道,当在vs发布项目到文件夹后,会生成项目的dll,如果有依赖的第三方的dll会一并发布到对应的文件夹下。之后我们在iis中新建网站指定文件夹为我们发布的文件夹即可。网站的运行方式为托管的方式,即:我们的网站不能独立运行,程序的入口为iis的w3wp.exe;我们发布的dll为这个程序的扩展或者说外挂,当iis接收到浏览器请求时候,会交给我们生成的dll处理,处理好的结果再返回给iis,而iis返回给客户浏览器。 那么在asp.net core中有哪些不同了呢?首先我们知道.net core是跨平台的,iis是只能运行在windows,那么我们就会有一个疑问 .net core 是如何解决这个问题的?.net core 生成的dll我们是否可以在iis以原有的方式进行托管?下面我们就开始学习下 我们使用vs2017新建一个asp.net core的web应用程序,并且配置如下图: 通过以上两个步骤,我们已经建立好一个可以发布的web应用(这个模版是一个包含权限配置功能和页面的,并且可以直接运行,有一些示范的页)。...

18 Feb 2020

sql server和 sqlite 判断建表语句

本文记录下sql server和sqlite 创建表的时候需要判断表存不存在,如果存在则不作处理否则新建表。 sql server if not Exists(select 1 from sysobjects where id = object_id('TB{0}SERIALLOG') and type = 'U') begin...

13 Feb 2020

以简单的方式实现离线升级包的自动安装

这两天有个需求如下: 用户已经安装应用程序为P,假如安装目录为“C:\Program(x86)\P”,后期如果程序需要升级给用户升级包,无论升级包在什么位置,用户双击升级包可以自动升级P。 以上需求分析后需要解决如下问题: 1.双击升级包可以执行。 2.找到原程序的安装路径。 针对第一个问题我想到了两种解决方案: 1.可以自己写程序生成exe文件,用户双击运行后释放文件到指定目录(也就是找到原安装路径)中,这显然不合适,因为每次升级都需要制作安装包,而且这个安装包中需要有一定的业务逻辑,制作复杂,成本较高。 2.定义一种文件格式做文件关联,关联程序为安装目录底下的update.exe,升级包采用zip压缩后将后缀改为自定义后缀,用户双击升级包时会运行时运行升级程序同时传入升级包路径。升级程序要做的就是解压、替换。 — 比较连个方案,很容易发现,方案二很快捷而且同时解决了上面的问题二(因为升级程序的路径就是原程序的安装路径)。 这边我们来看下文件关联的代码: [DllImport("shell32.dll")] public static extern void SHChangeNotify(uint wEventId, uint uFlags,...

11 Feb 2020

记录wpf中canvas保存为图片的过程

本文记录使用RenderTargetBitmap导出Canvas的过程 最基础的尝试 大家可能同过网上一些资料了解到使用RenderTargetBitmap导出Canvas代码如下: bool SaveImg(string path) { try { RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int)this.c1.ActualWidth, (int)this.c1.ActualHeight, 96d, 96d, PixelFormats.Pbgra32); targetBitmap.Render(this.c1); PngBitmapEncoder...