一步步教你制作WPF圆形玻璃按钮

1.介绍 从我开始使用vista的时候,我就非常喜欢它的圆形玻璃按钮。WPF最好的一个方面就是允许自定义任何控件的样式。用了一段时间的Microsoft Expression Blend后。我做出了这个样式。我觉得做的还行。因为。我决定分享。如我所说。我使用Microsoft Expression Blend来做。但是。我也是用XAML编辑器–Kaxaml。 2.概述 玻璃按钮样式包含了三层。组织了玻璃效果(Glass Effect)和一个ContentPresenter 来存储按钮的内容。所有的这些层都在一个最外层的Grid里。当鼠标放到按钮上,按下去的时候也定义了一些触发器(Triggers),来增加一些交互。 我把这个样式做成了资源文件。但是这个Key可以删除,来使得所有的按钮都是这个效果。 好我们来看一下这些层次。这些被广泛应用在微软产品中的按钮。 **3.按钮层次 ** 3.1背景层 第一层是一个椭圆。其实是一个canvas,一会在上面画反射和折射层,填充的颜色和按钮的背景(Background)关联。 下面是Blend中的截图 图2 <!-- Background Layer --> <Ellipse Fill="{TemplateBinding Background}"/> 3.1.1折射层 第二层模拟了光从上到下的折射。被放在反射层之前是因为,要达到反光玻璃的效果,反射层必须在按钮的中间某处有一个硬边缘。这一层实际上是另一个椭圆。但是这次。我们使用一个径向渐变(白色-透明)的填充。来模拟光的折射。渐变开始于第一层底部的中央。结束于上面的中间。然而。为了降低折射光的强度。渐变还是开始于椭圆的底部再下一点为好。可以从图上和代码里清晰的看到。 <!-- Refraction Layer --> <Ellipse x:Name="RefractionLayer"> <Ellipse.Fill> <RadialGradientBrush GradientOrigin="0.496,1.052"> <RadialGradientBrush.RelativeTransform> <TransformGroup> <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/> <TranslateTransform X="0.02" Y="0.3"/> </TransformGroup> </RadialGradientBrush.RelativeTransform> <GradientStop Offset="1" Color="#00000000"/> <GradientStop Offset="0.4" Color="#FFFFFFFF"/> </RadialGradientBrush> </Ellipse.Fill> </Ellipse> 3.1.2反射层 第三层是光的反射层。是最难的部分。问题是反射效果不能使用任何标准的形状来画。因此。使用路径(Path)来画反射区域。当时。手工画也是可以的。但老实说。手工画图实在没什么可享受的(除非你是一个艺术家,或者有一个数位板),无论如何。我现在MS Blend中华好一个椭圆并转换成一个路径,然后我使用贝塞尔曲线点调整得到平滑的路径,你可以添加渐变到一个复杂的Path对象上。就像你对其他与定义的图形,比如椭圆,矩形所做的一样。为了得到光泽反射。我额每年需要一个透明-白色的径向渐变填充,从路径的底部开始(也就是按钮的中间某处),结束在顶部。我想如果我是一个艺术家。我会让渐变更准一点。可是我不是。因此。就这样。因为我们要把我们的按钮放在一个Grid里。所有我们设置VerticalAlignment=“Top” 这样反射区域在按钮的中间的结束了。 图三 <!-- Reflection Layer --> <Path x:Name="ReflectionLayer" VerticalAlignment="Top" Stretch="Fill"> <Path.RenderTransform> <ScaleTransform ScaleY="0.5" /> </Path.RenderTransform> <Path.Data> <PathGeometry> <PathFigure IsClosed="True" StartPoint="98.999,45.499"> <BezierSegment Point1="98.999,54.170" Point2="89.046,52.258" Point3="85.502,51.029"/> <BezierSegment IsSmoothJoin="True" Point1="75.860,47.685" Point2="69.111,45.196" Point3="50.167,45.196"/> <BezierSegment Point1="30.805,45.196" Point2="20.173,47.741" Point3="10.665,51.363"/> <BezierSegment IsSmoothJoin="True" Point1="7.469,52.580" Point2="1.000,53.252" Point3="1.000,44.999"/> <BezierSegment Point1="1.000,39.510" Point2="0.884,39.227" Point3="2.519,34.286"/> <BezierSegment IsSmoothJoin="True" Point1="9.106,14.370" Point2="27.875,0" Point3="50,0"/> <BezierSegment Point1="72.198,0" Point2="91.018,14.466" Point3="97.546,34.485"/> <BezierSegment IsSmoothJoin="True" Point1="99.139,39.369" Point2="98.999,40.084" Point3="98.999,45.499"/> </PathFigure> </PathGeometry> </Path.Data> <Path.Fill> <RadialGradientBrush GradientOrigin="0.498,0.526"> <RadialGradientBrush.RelativeTransform> <TransformGroup> <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1" ScaleY="1.997"/> <TranslateTransform X="0" Y="0.5"/> </TransformGroup> </RadialGradientBrush.RelativeTransform> <GradientStop Offset="1" Color="#FFFFFFFF"/> <GradientStop Offset="0.85" Color="#92FFFFFF"/> <GradientStop Offset="0" Color="#00000000"/> </RadialGradientBrush> </Path.Fill> </Path> 最后。我添加一个ContentPresenter 到按钮中间。经验告诉我,内容区域再向下一个像素会使得按钮看起来更漂亮。因此,在这里我用了margin属性(注意。因为内容区域在Grid的中间(Center)。所以2个像素的top实际上是向下移动了一个像素 ) 好了。最后在Blend中看起来大概是这样 图4 4.添加一些交互性 4.1鼠标悬停效果 为了有鼠标悬停效果,我们需要增加光源的亮度。因此。我们为IsMouseOver 事件定义一个触发器,复制并且粘贴反射和折射层的渐变设置代码。对于折射层。我仅仅移动了渐变的起点向上了一点。在反射层中。我改变了渐变停止点。使不透明的白色多一点。 <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="RefractionLayer" Property="Fill"> <Setter.Value> <RadialGradientBrush GradientOrigin="0.496,1.052"> <RadialGradientBrush.RelativeTransform> <TransformGroup> <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/> <TranslateTransform X="0.02" Y="0.3"/> </TransformGroup> </RadialGradientBrush.RelativeTransform> <GradientStop Offset="1" Color="#00000000"/> <GradientStop Offset="0.45" Color="#FFFFFFFF"/> </RadialGradientBrush> </Setter.Value> </Setter> <Setter TargetName="ReflectionLayer" Property="Fill"> <Setter.Value> <RadialGradientBrush GradientOrigin="0.498,0.526"> <RadialGradientBrush.RelativeTransform> <TransformGroup> <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1" ScaleY="1.997"/> <TranslateTransform X="0" Y="0.5"/> </TransformGroup> </RadialGradientBrush.RelativeTransform> <GradientStop Offset="1" Color="#FFFFFFFF"/> <GradientStop Offset="0.85" Color="#BBFFFFFF"/> <GradientStop Offset="0" Color="#00000000"/> </RadialGradientBrush> </Setter.Value> </Setter> </Trigger> ...

2012-10-12 · 2 min · bystander

自定义WPF LinkLabel 控件

WPF里是没有LinkLabel控件的。因此我自己写一个。首先。我们看一下WPF中什么类似的组件可以实现这个链接功能 如果你想要模拟一个LinkLabel控件。你可以在TextBlock里使用内联的Hyperlink。像下面这样 <TextBlock> <Hyperlink> <Run Text="Test link"/> </Hyperlink> </TextBlock> 你可以使用Label控件。加一个内联的HyperLink,但是我认为TextBlock更好。因为你可以在Expression Blend 中通过InlineCollection 编辑所有子元素的属性 图1 虽然这种方法也行,但是我还是不太喜欢。因为我觉得我还是写一个类似windows窗体程序中的LinkLabel控件。然后我就做了。首先看一下控件的样子 图2 第一个是默认的LinkLabel控件。第二个是LinkLabelBehavior 属性被设置为HoverUnderline ,第三个的Foreground和 HoverForeground 属性都使用了自定的颜色。 LinkLabel控件支持的属性 1.Foreground和 HoverForeground属性 允许自定义这两个属性的值 2.LinkLabelBehavior 属性 允许设置下划线的显示与否 3.自定义HyperlinkStyle 属性 你可以使用这个属性给超链接设置自定义的样式。如果你已经自定了Foreground和 HoverForeground。则会被覆盖。 Url 超链接的目标 所有这些属性都继承自标准的System.Windows.Controls.Label 控件 通过Blend/Xaml设置这些属性很简单 <ThemedControlLibrary:LinkLabel Content="Link Label" FontSize="22"/> <ThemedControlLibrary:LinkLabel Content="Link Label" LinkLabelBehavour="HoverUnderline" /> <ThemedControlLibrary:LinkLabel Foreground="#FF847901" HoverForeground="#FF06C8F2" Content="Link Label" LinkLabelBehavour="NeverUnderline"/> 图三 然后是控件的使用方法。仅仅添加命名空间到xaml中。然后使用就行了。 <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="DemoApplication.Window1" Title="DemoApplication" Height="300" Width="300" xmlns:ThemedControlsLibrary="clr-namespace:ThemedControlsLibrary;assembly=ThemedControlsLibrary" > <Grid> <ThemedControlsLibrary:LinkLabel HorizontalAlignment="Left" VerticalAlignment="Top" Content="LinkLabel"/> </Grid> </Window> 控件的完整代码很简单。就定义一下需要的属性,和控制这些属性应该显示在Blend中的(category)目录位置就行了。 public class LinkLabel : Label { private const string _linkLabel = "LinkLabel"; public static readonly DependencyProperty UrlProperty = DependencyProperty.Register("Url", typeof(Uri), typeof(LinkLabel)); [Category("Common Properties"), Bindable(true)] public Uri Url { get { return GetValue(UrlProperty) as Uri; } set { SetValue(UrlProperty, value); } } public static readonly DependencyProperty HyperlinkStyleProperty = DependencyProperty.Register("HyperlinkStyle", typeof(Style), typeof(LinkLabel)); public Style HyperlinkStyle { get { return GetValue(HyperlinkStyleProperty) as Style; } set { SetValue(HyperlinkStyleProperty, value); } } public static readonly DependencyProperty HoverForegroundProperty = DependencyProperty.Register("HoverForeground", typeof(Brush), typeof(LinkLabel)); [Category("Brushes"), Bindable(true)] public Brush HoverForeground { get { return GetValue(HoverForegroundProperty) as Brush; } set { SetValue(HoverForegroundProperty, value); } } public static readonly DependencyProperty LinkLabelBehavourProperty = DependencyProperty.Register("LinkLabelBehavour", typeof(LinkLabelBehaviour), typeof(LinkLabel)); [Category("Common Properties"), Bindable(true)] public LinkLabelBehaviour LinkLabelBehavour { get { return (LinkLabelBehaviour)GetValue(LinkLabelBehavourProperty); } set { SetValue(LinkLabelBehavourProperty, value); } } static LinkLabel() { FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata( typeof(LinkLabel), new FrameworkPropertyMetadata(typeof(LinkLabel))); } } ...

2012-10-11 · 2 min · bystander

C#制作进度窗体

介绍 这是我在CodeProject上的第一篇文章。我希望对你有用 当我开发软件的时候。我通常因为一个很耗时是任务需要完成。而请求让用户等待,并且通过也允许用户取消。不论我做何种操作(比如下载文件。保存大文件等等)。我都需要做下面几件事: 通过一个模态对话框来让用户等待操作完成 能让用户看到进度。 能让用户随时取消。 我搜了好久也没找到拿来就能用的窗体控件,也许是我没找到。于是我自己写。。 图1 背景 BackgroundWorker 类包含了我需要完成任务的所有东西。我只需要给他提供一个对话框。 使用代码 ProgressForm 包含了一个BackgroundWorker ,你要做的仅仅就是提供了一个完成工作的方法。 ProgressForm form = new ProgressForm(); form.DoWork += new ProgressForm.DoWorkEventHandler(form_DoWork); //如果想为后台任务提供参数的话 form.Argument = something; 为了开始BackgroundWorker,只需要调用ShowDialog 方法。返回值则取决于任务是怎么完成的。 DialogResult result = form.ShowDialog(); if (result == DialogResult.Cancel) { //用户点击了取消 } else if (result == DialogResult.Abort) { /未处理的异常抛出 //你可以得到异常信息 MessageBox.Show(form.Result.Error.Message); } else if (result == DialogResult.OK) { //正常完成 //结果存储在 form.Result里 } 最后。任务方法看起来是这样的。 void form_DoWork(ProgressForm sender, DoWorkEventArgs e) { //得到参数 object myArgument = e.Argument; //做一些耗时的任务... for (int i = 0; i < 100; i++) { //通知进度 sender.SetProgress(i, "Step " + i.ToString() + " / 100..."); //... //检查是否点击了取消 if (sender.CancellationPending) { e.Cancel = true; return; } } } 如果你想要改改进度条,或者进度条显示的文本。SetProgress 有一些重载的方法 public void SetProgress(string status); public void SetProgress(int percent); public void SetProgress(int percent, string status); 最后一个可自定义的字符串是:有两个预定义的字符串CancellingText 和DefaultStatusText. CancellingText ,这两个字符串,当用户点击取消的时候显示 如何实现 ProgressForm 紧紧嵌入了一个BackgroundWorker ,并包装进了主函数。 首先。我设计了如图所示的一个窗体,然后。添加了BackgroundWorker。 public partial class ProgressForm : Form { public ProgressForm() { InitializeComponent(); worker = new BackgroundWorker(); worker.WorkerReportsProgress = true; worker.WorkerSupportsCancellation = true; worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork); worker.ProgressChanged += new ProgressChangedEventHandler( worker_ProgressChanged); worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( worker_RunWorkerCompleted); } void worker_DoWork(object sender, DoWorkEventArgs e) { } void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { } void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { } BackgroundWorker worker; } 我们必须把DoWork事件暴露给用户。我添加了一个委托。这样。我可以很容易的访问窗体成员 ...

2012-10-10 · 4 min · bystander

C#编写FTP客户端软件

1 介绍 我知道。网上有很多现成的FTP软件。但是。我们也想要了解FTP的一些底层机构,因此。 这个开源的项目在你学习FTP知识的时候也许对你有些帮组。程序的界面看起来像FileZilla,FileZilla虽然流行但是有些bug,当我打开我博客的时候总是有问题。我需要通过FTP连接我的服务器。发送文件,下载文件等等。因为。我决定写我自己的软件来处理所有的情况。FileZilla足够好。但它不是我的。 2 背景 看看我们已经知道的。我们知道FTP是一个标准的基于TCP网络协议。用于从一个主机向另一个主机传输文件。它是一个C/S架构。 图2 FTP程序曾经是基于命令行的。我们仍沿可以通过cmd.exe连接FTP服务器。因为FTP的确可以通过命令来操作。举个例子。我们可以在命令行使用“stor”命令来发送文件。为了完成这些请求。FTP服务器需要一直运行等待即将到来的客户端请求。我们可以从来自维基百科的解释更好的理解FTP: 客户端计算机可以通过服务器的21端口和服务器通信。叫做控制连接。它在一次会话期间保持开放。第一次连接的时候。叫做数据连接,服务器可以对客户端打开20端口(主动模式),建立一条数据通路,连接上客户端传输数据。或者客户端打开一个随机的端口(被动模式),去连接服务器,来传输数据。控制连接使用一个类似Telnet的协议,被用作客户端和服务器会话管理(命令,标识,密码)。。比如。“RETR filename” 会从服务器端下载文件。 图三 一个完整的FTP文件传输需要建立两种类型的连接,一种为文件传输下命令,称为控制连接,另一种实现真正的文件传输,称为数据连接。 服务器 通过三位ASCII的数字状态码,可能包含可选的描述信息,在控制连接上做出回应。比如。“200”或者是“200 OK”,表示上一条命令成功了。数字代表编号,描述信息给出了一些说明(比如“OK”),或者可能是一些需要的参数(比如需要帐号来存储文件),那么我们需要怎么做呢。很明显。发送命令,接收“OK”回应,发送数据。接收数据。完了。但是首先需要服务器已经准备好了。FTP服务器可以在主动和被动两种模式下运行。主动模式是基于服务器的连接而被动模式是基友客户端的连接。继续看。 在主动连接中,客户端把自己的ip和端口发送给服务器。然后服务器尝试连接到客户端,但是可能会因为防火墙的原因而被拒绝。我们在windows上都会使用反病毒/自带防火墙。是吧。那么我们来看看被动模式 在被动连接中。服务器通过一个“PASV”命令把自己的ip和端口发送给客户端。然后客户端通过该IP尝试连接服务器。对于发送文件非常有用。当我们发送文件的时候。优先使用“PASV”模式,如你们所说。大多数协议。像FTP/HTTP 使用ASCII编码,因为全球可用。因此我们会使用这种编码。你可以从下面得到FTP的命令列表 主动和被动都是对于服务器端来说的 3 使用代码 现在我们已经为编写软件做好准备了。我们写些有用的代码吧。:)首先。我们“打开文件对话框”,集成到我们的窗体里。 3.1 资源管理器组件 我们需要一个资源管理器组件在软件界面可以看到我们所有的文件。这样我们才可以选择哪些文件来发送到FTP服务器,新建一个Windows窗体控件库(下载包中提供了) 图四 最后看起来样子是上面这样。先添加一个TreeView,一些按钮,和一个搜索功能 TreeView.Nodes.Clear(); TreeNode nodeD = new TreeNode(); nodeD.Tag = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); nodeD.Text = “Desktop”; nodeD.ImageIndex = 10; nodeD.SelectedImageIndex = 10; TreeView.Nodes.Add(nodeD); 就像上面代码展示的那样。我们需要添加地一个主节点。我的文档。我的电脑等等。然后获得子目录。 string[] dirList; dirList = Directory.GetDirectories(parentNode.Tag.ToString()); Array.Sort(dirList); if (dirList.Length == parentNode.Nodes.Count) return; for (int i = 0; i < dirList.Length; i++) { node = new TreeNode(); node.Tag = dirList[i]; node.Text = dirList[i].Substring(dirList[i].LastIndexOf(@"\") + 1); node.ImageIndex = 1; parentNode.Nodes.Add(node); } 可以从下载包里看到完整的代码。我们还应该处理鼠标单击事件。 现在我们有了一个资源管理器。还有FTP和VS所需要的所有信息。 首先,我们连接服务器。我们应该怎么做呢? FTPSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); AppendText(rchLog,"Status : Resolving IP Address\n",Color.Red); remoteAddress = Dns.GetHostEntry(Server).AddressList[0]; AppendText(rchLog, "Status : IP Address Found ->" + remoteAddress.ToString() + "\n", Color.Red); addrEndPoint = new IPEndPoint(remoteAddress, Port); AppendText(rchLog,"Status : EndPoint Found ->" + addrEndPoint.ToString() + "\n", Color.Red); FTPSocket.Connect(addrEndPoint); 是的。我们需要一个socket连接到服务器 ,然后发送命令 AppendText(rchLog, "Command : " + msg + "\n", Color.Blue); Byte[] CommandBytes = Encoding.ASCII.GetBytes((msg + "\r\n").ToCharArray()); FTPSocket.Send(CommandBytes, CommandBytes.Length, 0); //read Response ReadResponse(); ...

2012-10-09 · 2 min · bystander

理解并实现生成器模式

介绍 本文讨论生成器设计模式,讨论该模式什么情况下使用,怎么实现。并且。最后会有一个简单的生成器模式的实现。 背景 当我们的程序需要创建一个对象。而这个对象必须由很多不同的对象来构造的时候。为了构造最后的对象。我们不得不组合那些部分对象。最后我们会发现我们的代码被各种各样的部分对象的细节所弄的难以理解 为了说明上面的情况。我们做一个手机生产制造系统的例子。假定我们我们有一个已经安装在手机供应商那块的一个系统。现在供应商系那个要根据一些参数来创造一个新手机。比如触屏,操作系统,电池等。如果我们已经有了这些部分的对象,那么上述部分的任意组合将会导致客户端代码复杂难以管理。比如决定生产哪种手机的模块。 生成器模式目的就是解决上述问题的。GoF定义生成器模式如下: Separate the construction of a complex object from its representation so that the same construction process can create different representations. 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 这个定义意味着我们不得不设计这个系统。通过一种客户端仅仅定义参数,而生成器则接管创建复杂对象 的方式。我们看一下生成器模式的类图。 然后看看上图中的每一个类都表示什么 ConcreteBuilder: 创建复杂产品的具体类.将会知道他已经创建的Product(产品),也就是他已经装配了的Product, 客户端通过该类得到Product对象. Builder: 创建Product的接口 Director: 客户端代码,定义了哪些部分应该被组合在一起来创建具体的Product Product: 这是通过组合很多部分创建的对象 使用代码 我们现在跟随上述的定义,然后试着去实现一个基本的生成器模式 我们先在合适的地方定义Product的不同部分,我们简单的定义一些枚举类型,那么我们就可以通过组合不同的部分创建Product了。 // 一些helper枚举定义各种零件 public enum ScreenType { ScreenType_TOUCH_CAPACITIVE, ScreenType_TOUCH_RESISTIVE, ScreenType_NON_TOUCH }; public enum Battery { MAH_1000, MAH_1500, MAH_2000 }; public enum OperatingSystem { ANDROID, WINDOWS_MOBILE, WINDOWS_PHONE, SYMBIAN }; public enum Stylus { YES, NO }; 然后,我们看一下Product类,我们需要有一个可以通过装配创建的Product类,这里我们定义一个MobilePhone类,也就是概念里的Product类了。 // 这是 "Product" 类 class MobilePhone { // 不同部分的字段 string phoneName; ScreenType phoneScreen; Battery phoneBattery; OperatingSystem phoneOS; Stylus phoneStylus; public MobilePhone(string name) { phoneName = name; } //公有属性访问这些部分 public string PhoneName { get { return phoneName; } } public ScreenType PhoneScreen { get { return phoneScreen; } set { phoneScreen = value; } } public Battery PhoneBattery { get { return phoneBattery; } set { phoneBattery = value; } } public OperatingSystem PhoneOS { get { return phoneOS; } set { phoneOS = value; } } public Stylus PhoneStylus { get { return phoneStylus; } set { phoneStylus = value; } } // 显示手机相关信息的方法 public override string ToString() { return string.Format("Name: {0}\nScreen: {1}\nBattery {2}\nOS: {3}\nStylus: {4}", PhoneName, PhoneScreen, PhoneBattery, PhoneOS, PhoneStylus); } } ...

2012-10-08 · 3 min · bystander

YAXLib---- XML序列化神器

今天早上翻译了Yet-Another-XML-Serialization-Library-for-the-NET,刚开始以为很短。翻译着发现不对。。然后你不逼你自己。怎么知道自己做不到。于是。将近4个小时把30页的文档翻译完了。因为文章很长。所以本文只列出前两部分。我把翻译好的做成了pdf, 文档下载:XML序列化神器 1 介绍 在本文中,会把要提到的XML序列化库叫做YAXLib,我们知道。.Net 还是提供了一些序列化功能的,尤其是XmlSerializer,该类被程序员广泛使用用来序列化对象成XML,当然,反序列化也是可以的。我认为XmlSerializer类的问题有几下几点 程序员不能自由的选择生成的xml的结构 不支持序列化一些集合类,比如Dictionary<,> 或者IEnumerable<>的属性 当反序列化的时候,如果缺失了一些域,则反序列化失败,这就使得用来存储一ixekeyi被用户编辑的配置文件变得不合适了。 2 为什么使用YAXLib YAXLib解决上述问题的特点 程序员可以决定xml文件的结构,一个属性可以是一个子元素,或者是其他属性的属性,或者是一个在类中没有对应属性的元素。 集合类也可以被序列化成一个逗号分隔(也可以是其他任何分隔符)的数据项列表,而且。为Dictionary<,>对象实现了一些特殊的格式化功能,这样,使得程序员可以完全控制生成的xml文件的结构 他支持System.Collections.Generic 命名空间中的所有泛型集合类(像Dictionary, HashSet, LinkedList, List, Queue,SortedDictionary, SortedList, 和 Stack) 和在System.Collections 命名空间中的非泛型集合类( ArrayList, BitArray, Hashtable, Queue, SortedList, 和 Stack)非泛型集合类可以包含多种不同的对象,而且,库还支持序列化和反序列化一维,多维,不规则的数组。 支持通过对基类/接口的引用,实现对一些对象集合的序列化和反序列化。 支持多级反序列化 程序员可以为生成的xml提供注释 当进行反序列化的时候,程序员可以选择性对于那些与类的属性相关,但没有出现在xml文件中的数据应该如何处理。这种情况下可以看错是一个错误,然后类库抛出一些异常,或者记录错误,或者可以被看成一个警告,然后用程序员预定义的值赋给对应的属性,而且,程序可以可以选择忽略这个问题,相关的异常将既不抛出也不作任何记录。请查看保留空引用标识那一节 看看什么时候可以忽略孤立的数据也许对你有帮助 程序员可以自己选择错误处理规则,对于数据敏感的应用程序,程序员可以选择在任何异常的情况下,库都应该抛出并且记录异常,对于其他的一些情况(比如要求不那么高的配置文件的存储),程序员可以选择把异常仅仅看成一个警告,仅仅记录一下,让程序的其他部分继续运行。 文档下载:XML序列化神器 著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

2012-10-05 · 1 min · bystander

11个高效的VS调试技巧

介绍 调试是软件开发周期中的一个很重要的部分,有时很有挑战性,有时候则让程序员迷惑,有时候让程序员发疯,但是。可以肯定的是,对于任何不是太那个微不足道的程序来说,调试是不可避免的。近年来,调试工具的发展已经使得很多调试任务简单省时了。 本文总结了十个调试技巧,当你使用VS的时候可以节省你很多时间。 1. 悬停鼠标查看表达式 调试有时候很有挑战性,当你步入一个函数想看看哪块出错的时候,查看调用栈来想想值是从哪来的。另一些情况下,则需要添加一些监视表达式,或者查看局部变量列表,这通常还是花费一些时间的,但是。如果你把你鼠标指向你感兴趣的一个变量。你会发现事情简单多了。而且,类和结构体可以通过单击展开。这样。你就可以方便快捷的找到你想查看的变量了。 2. 实时改变值 调试器不仅仅是一个分析程序崩溃或是异常结果的工具了,许多bug都可以通过步入新写的函数,检查函数是否如期望的那样运行来预防。有时候你可能会好奇“如果条件为真函数会正确运行吗”大多数情况下,根本不需要改变代码重启挑起,仅仅把鼠标悬停到一个变量上,双击值然后输入一个新值就可以了。。 3.设置下一条语句 一个典型的调试情况就是通过单步跟踪分析为什么一个函数调用失败了。当你发现一个函数调用的另一个函数返回错误的时候你会怎么做?重启调试?有更好的方法。拖动这个黄色的语句标识到你想下一步执行的语句前就可以了。比如你刚才失败的那块,然后步入。简单,不是吗? 4.编辑然后继续 调试一个复杂的程序,或是一个插件的时候,在一个被调用很多次的函数处发现一个错误。但是不想浪费时间停下来,重新编译然后重新调试。没问题,仅仅在该处改正代码然后继续单步就可以。VS会修正程序然后继续调试不需要重启 注意,编辑然后继续有大量的已知限制,首先,64位代码是不行的。如果他如果为你的C#程序工作。就去工程设置的生成选项,然后目标平台为x86.不要担心。发布版的目标平台和调试的时候是分开的。可以被设置为任何平台。。 第二.编辑然后继续改变在一个方法里应该是局部的。。如果你改变了方法签名,添加一些新方法或是类。你就不得不重启程序了。或者撤销改变来继续。改变方法也包含lambda表达式隐式修改的自动生成的代理类,因此也不能继续。 5.方便的监视窗口 大概现代的调试器都有一个监视窗口,无论如何。VS允许你简单的添加或移除变量。单击空行,输入你的表达式按下回车,或者是在不需要的表达式上按下Delete键就可以删除了。 而且。从监视窗口你不仅仅可以看到“正常”的变量。你可以输入$handles 来追踪你的程序打开了多少句柄(可以方便的修复内存泄漏) ,输入$err 可以看到上一个函数的错误码,然后使用工具-错误信息可以看到更详细的描述,或者输入@eax(64位是@rax)来查看包含函数返回值的寄存器。 6.带注释的反汇编 使用交互式的反汇编模式可以使得优化程序的关键部分变得很容易,VS给出对应你代码每一行的汇编指令,并且运行单步运行。同时,可以在任何位置设置断点。而且,表达式的查看和修改也像在C++代码里一样 7.带有栈的线程窗口 调试多线程的程序是痛苦的。。或者也可以是很有趣的。取决于你的调试器。VS2010真正优美的特性是线程窗口的栈视图,通过窗口的调用栈你可以方便的总览线程。 8.条件断点 如果你尝试通过断点再现一个罕见的事件,该情况引发了一些严重的错误。你可以添加条件断点。定义一个断点的条件,然后如果条件不成立,VS会忽略该断点 9.内存窗口 有些bug由不正确的结构体定义引起,忽略的对齐属性等等。查看内存中的内容可以定位然后修复bug。VS提供了一个放百年的内存窗口,可以把值以8/16/32/64位的形式展示。还有浮点值。也允许实时改变他们。就像在文本编辑器里一样。 10.转到定义 这个特性不是直接关于调试的,而是关于浏览大项目的。如果你尝试找到一些不是你自己写的代码中的错误,快速知道“这个类型是什么”或者“这个函数是干嘛的”,可以节省很多时间,VS通过一个转到定义命令方便了你。 11.命令窗口 第十一的技巧chaau已经建议过了。确实可以节省很多时间,VS支持命令窗口,可以通过,视图-其他窗口-命令窗口来启动。一旦激活,你可以输入不同的命令来自动化调试。举个例子。你可以通过如下命令 简单的模拟MFC COleDateTime 变量。 ? dt.Format("%Y-%m-%d %H:%M:%S") 许可 本文包括源代码和文件在CPOL下授权。 原文地址:10plus-powerful-debugging-tricks-with-Visual-Studi 著作权声明:本文由http://leaver.me 翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

2012-10-03 · 1 min · bystander

CSV导入导出工具

介绍 本文介绍并且实现了在平面文件CSV和SQL server之间的导入导出功能。使用VS2005写的。使用了.net 2.0 本文基于前一篇文章:从CSV导入数据并存储到数据库,本文包含了新功能,比如,导出功能,在数据库创建表,批量拷贝。接下来的例子中有很多注释。 导入 通过ODBC驱动连接到一个CSV文件,然后把文件读到一张表了(基于前面提到的那篇文章) 使用不同的编码和不同的行分隔符(基于前文) 加载CSV文件到DataSet(基于前文) 如何显示对CSV文件的预览(基于前文) 通过SqlBulkCopy的对象向SQL server转移数据,原始数据是DataSet实例 使用结构表,基于CSV文件,创建一个新表 使用事件来处理批量拷贝的进程 导出 浏览SQL 数据库中的用户表 使用不同的编码和分隔符 使用SqlDataReader读取数据,使用StreamWriter转移数据到平面文件 使用 下载工程 新建一个数据库,或者使用一个存在的数据库 修改软件中的数据库连接字符串,在prop.cs文件中 public static string sqlConnString = "server=(local); database=Test_CSV_impex;Trusted_Connection=True"; 运行工程 一些代码段 加载CSV到DataSet中 /* *加载CSV到DataSet. * * 如果numberOfRows parameter 是 -1, 就加载所有行, 否则就加载指定数目的行 */ public DataSet LoadCSV(int numberOfRows) { DataSet ds = new DataSet(); try { // 创建并打开ODBC连接 string strConnString = "Driver={Microsoft Text Driver (*.txt; *.csv)}; Dbq=" + this.dirCSV.Trim() + "; Extensions=asc,csv,tab,txt;Persist Security Info=False"; string sql_select; OdbcConnection conn; conn = new OdbcConnection(strConnString.Trim()); conn.Open(); //创建SQL语句 if (numberOfRows == -1) { sql_select = "select * from [" + this.FileNevCSV.Trim() + "]"; } else { sql_select = "select top " + numberOfRows + " * from [" + this.FileNevCSV.Trim() + "]"; } //创建数据适配器 OdbcDataAdapter obj_oledb_da = new OdbcDataAdapter(sql_select, conn); //用CSV的数据填充DataSet obj_oledb_da.Fill(ds, "csv"); //关闭连接 conn.Close(); } catch (Exception e) //异常处理 { MessageBox.Show(e.Message, "Error - LoadCSV", MessageBoxButtons.OK,MessageBoxIcon.Error); } return ds; } 通过SqlBulkCopy从ODBC连接中转移数据到数据库 ...

2012-09-30 · 4 min · bystander

ListView布局管理器

介绍 使用ListViewLayoutManager 可以控制ListView/GridView列的布局 固定列宽:有着固定列宽的列 范围列宽:有着最小最大宽度的列 比例列宽:成比例的列宽 范围列宽可以限制列的宽度,也包括填充列的剩余可视区域。 据我们了解的Html中的表格和Grid空间。比例列以一个百分比来定义列宽,以下几个因素共同确定了比例列的宽度。 垂直ListView滚动条的可视与否 ListView控件宽度的改变 非比例列宽度的改变 本程序支持通过XAML或是后台代码来控制ListView。如果通过XAML来控制。则允许ListViewLayoutManager 被附加到一个存在的ListView控件上。 ConverterGridColumn 类通过接口IValueConverter 提供了对象绑定。使用ImageGridViewColumn 类则允许通过DataTemplate(数据模板)将列显示成图片等。 在 User Setting Applied中,我展示了如何固定ListViewlieder顺序和大小 XAML中ListView/GridView布局 固定列 下面的例子展示了通过XAML使用固定列宽控制列 <ListView Name="MyListView" ctrl:ListViewLayoutManager.Enabled="true"> <ListView.View> <GridView> <GridViewColumn DisplayMemberBinding="{Binding Path=Name}" ctrl:FixedColumn.Width="100" Header="Name" /> <GridViewColumn DisplayMemberBinding="{Binding Path=City}" ctrl:FixedColumn.Width="300" Header="City" /> </GridView> </ListView.View> </ListView> 设置附加到ListView控件上的ListViewLayoutManager 的Enabled属性为True。然后FixedColumn.Width 就会阻止鼠标拖动改变列的宽度。 比例列 下面的例子展示了使用XAML通过比例来控制列 对比Grid控件的RowDefinition.Width 属性,ProportionalColumn.Width会计算百分比。简单来说,就是上面的例子中Name列会占到总宽度的25%,而City列占到75%。 与固定列相似。鼠标将不能改变列的宽度。 范围列 下面的例子展示了使用XAML通过最小/最小宽度来控制列 第一个范围列的IsFillColumn 属性被设置为True,因此将会自动改变大小来填满剩余的空间,而如果ListView包含一个比例列的话,范围列将不会填充 通过鼠标可以拖动范围列的宽度。鼠标指针会有一些提示。。 组合使用 在真实的世界里。组合使用很普遍。他们的顺序可以多种多样。 使用后台代码控制ListView/GridView布局 ListView listView = new ListView(); new ListViewLayoutManager( listView ); // attach the layout manager GridView gridView = new GridView(); gridView.Columns.Add( FixedColumn.ApplyWidth( new MyGridViewColumn( "State" ), 25 ) ); gridView.Columns.Add( RangeColumn.ApplyWidth( new MyGridViewColumn( "Name" ), 100, 150, 200 ) ); // 100...200 gridView.Columns.Add( ProportionalColumn.ApplyWidth( new MyGridViewColumn( "City" ), 1 ) ); // 33% gridView.Columns.Add( ProportionalColumn.ApplyWidth( new MyGridViewColumn( "Country" ), 2 ) ); // 66% listView.View = gridView; 定制列的效果 类ConverterGridColumn 作为一个基类,用来绑定列到独立的对象。 ...

2012-09-29 · 3 min · bystander

从数据库读取图片发生“无效的参数”异常

介绍 我发现对于很多人来说,当从数据库里载入一张图片然后重新创建成一张图片显示的话会有这样一个问题—-当他们尝试重新创建新的图片的时候,会抛出一个“无效的参数”异常 因此,本文介绍该异常如何产生。并且我希望未来当我或是你发生这个错误的时候还能有所帮助。。 背景 存储图片到数据库里面是一个很有效的想法。很多人在一些场合都会这样做。的确,这是一个很好的想法。在图片很小的情况下,或者图片不是太多。在这两种情况下,当你需要图片的时候,你会实时去加载它们。而当你不需要的时候如果从数据库里加载图片会浪费很多带宽。并使得你的程序有一些慢。 但问题是这种方法也很容易发生错误。–尤其是你使用字符串连接,然后组合到你的SQL语句里面—并且这个错误只有当你打算使用存储的信息的时候才会发生。然后,看起来似乎是你的读取代码写错了—不可能—它是正常的。我在其他地方都可以的。。 从数据库里加载图片 重数据库里读取一张图片然后转换成图片显示是很简单的。 using (SqlConnection con = DBAccess.DBCon) { using (SqlCommand cmd = new SqlCommand("SELECT picture FROM Pictures WHERE Id=@ID", con)) { cmd.Parameters.AddWithValue("@ID", Id); SqlDataReader reader = cmd.ExecuteReader(); if (reader.Read()) { byte[] data = (byte[])reader["Picture"]; using (MemoryStream stream = new MemoryStream(bytes)) { myImage = new Bitmap(stream); } } } } 但是-如果data因为一些原因不是有效的图片,那么这一行 myImage = new Bitmap(stream); 将会抛出一个异常—无效的参数 只有当你真正看了从数据库里返回到data里的数据-而不是简单的瞄了一眼调试器,你才能注意到是什么原因。。 {byte[21]} [0] 83 [1] 121 [2] 115 [3] 116 [4] 101 [5] 109 [6] 46 [7] 68 [8] 114 [9] 97 [10] 119 [11] 105 [12] 110 [13] 103 [14] 46 ... 它看起来不像是错的,所以它可能就是你想要的。-虽然21字节是一个很大的线索:你的图片可能只有21字节长?那图片可真小。。 但是,上面的是可以读懂的。。稍微练习一下。。每个字节是一个ASCII码。。 "83" is an uppercase 'S' "121" is an lowercase 'y' "115" is an lowercase 's' "116" is an lowercase 't' "101" is an lowercase 'e' "109" is an lowercase 'm' "46" is a '.' "68" is an uppercase 'D' "114" is an lowercase 'r' "97" is an lowercase 'a' "119" is an lowercase 'w' "105" is an lowercase 'i' "110" is an lowercase 'n' "103" is an lowercase 'g' "46" is an lowercase '.' ... 简而言之,你从数据库里得到的数据是一个人类可以读懂的字符串,意思是是 ...

2012-09-27 · 2 min · bystander