在开始装逼之前,老周先说明一件事。有人说老周写的东西太简单了,能不能写点复杂点。这问题就来了,要写什么东西才叫“复杂”?最重要的是,写得太复杂了,一方面很多朋友看不懂,另一方面,连老周自己也不知道怎么表述。
而且,老周也不能把以前在K公司、Z公司和T公司中做项目的东西写出来的,其实嘛,工作中的编程没什么可写的,无非就是 select、insert、delete、update,无非就是连接数据库,断开连接,同步一下数据,把数据变成XML或JSON再发给另一终端。无非就是读读你的网卡CPU硬盘序列号,组成个东东再加密,计算一下授权码,又或者生成个假冒伪劣证书给用户授授权。再不是就写几个API给别人调几下。让脑细胞死亡率大增的,就是要动态生成计算工资的公式,这个嘛,当时老周是选用 Code Dom 来生成的,代码生成这玩意儿,老周前些时间就写过好些博文了,相信大伙伴们也看过了。
所以,你看,工作中用到的东西其实很片面很单一,所覆盖的面还不如老周平时闲着没事的时候写的小程序。故,还是写点简单的东西和谐一点,你懂我懂他也懂,岂不甚妙,人活着为啥老跟自己过不去呢,姜育恒大哥就曾经唱过:
- 不管明天要面对多少伤痛和迷惑
- 曾经在幽幽暗暗反反复复中追问
- 才知道平平淡淡从从容容是最真
平淡是福,简单是乐,谁谓不然?
好了,上面的鬼话说完了,下面咱们开始说人话。
我们都知道,VS 开发环境会为项目自动生成一个settings类,即用于访问应用程序设置的帮助类,数据是存到跟应用程序一起的 config 文件中,比如历史上著名的 App.config 文件。
顺便提一下,VS 自动生成的应用设置类有一个特点:基于应用程序范围的设置项是只读的,基于用户范围的设置项是可读可写的。看不懂?没事,你可以动手调戏一下这个settings类的。
在Properties节点下,你应该能看到一个 Settings 文件,然后打开它。
这里你可以自己添加设置项,注意看“范围”这一列,它就两个选项,要么是基于应用范围,要么基于用户范围。好,我们为每个范围各添加一个设置项。
随后,我们保存一下(必须,保存才会生成代码),接着打开代码,看看设计器生成的 Settings 类。
代码选段。
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("0")] public byte TestV1 { get { return ((byte)(this["TestV1"])); } set { this["TestV1"] = value; } } [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("0")] public int TestV2 { get { return ((int)(this["TestV2"])); } }
在属性上应用 ApplicationScopedSettingAttribute 表示该设置项是应用程序范围的,大伙看到,属性中只有 get 没有 set,说明它是只读的。而应用了 UserScopedSettingAttribute 的属性表示的是用户范围内的设置项,此时看到该属性同时有 get 和 set ,即可读可写。
为什么应用程序范围的设置项相关属性会生成只读属性呢,后来一看MSDN就明白了,因为当调用相关方法保存设置时,应用程序范围内的设置是不会起作用的,但是不会报错。也就是说,要自己写可以直接保存的设置项,只能把属性定义为用户范围内的。
如果觉得生成的设置类不好玩,我们可以自己写的。
编写这个类其实很简单,我们只需从 ApplicationSettingsBase 类派生即可,该类位于 System.Configuration 命名空间下,它是一个抽象类。在写自定义的应用设置类时,我们可以像普通类一样公开属性,这样读写设置项时也方便,而且,你还可以直接用于数据绑定。
在包装属性时,是通过调用基类的索引器来存取内容的,它是一个字典模型,key是字符串类型,而value是Object类型,这样你可以设置各种类型的值。
好,咱们写一个来表演一下。
internal class AppSettings : ApplicationSettingsBase { #region 常量 const string APP_TITLE = "appTitle"; const string APP_USAGE = "appUsage"; const string USER_NAME = "userName"; const string USE_YEARS = "useYears"; #endregion [UserScopedSetting] [DefaultSettingValue("my app")] public string AppTitle { set { this[APP_TITLE] = value; } get { return (string)this[APP_TITLE]; } } [UserScopedSetting] [DefaultSettingValue("用于装X")] public string AppUsage { get { return (string)this[APP_USAGE]; } set { this[APP_USAGE] = value; } } [UserScopedSetting] [DefaultSettingValue("大傻冬")] public string UserName { get { return (string)this[USER_NAME]; } set { this[USER_NAME] = value; } } [UserScopedSetting] [DefaultSettingValue("3")] public int UseYears { get { return (int)this[USE_YEARS]; } set { this[USE_YEARS] = value; } } }
由于属性实现中使用的key是字符串类型的,为了防止多次输入时出现错误,通常可以预先声明一组字符串常量。
const string APP_TITLE = "appTitle"; const string APP_USAGE = "appUsage"; const string USER_NAME = "userName"; const string USE_YEARS = "useYears";
在类公开的属性上除了应用表示用户范围内的标识特性外,还应用了 DefaultSettingValue 特性,它用来设置项的默认值,值是以字符串形式表示的。
这个示范的设置类包装了四个设置项,那么,怎么耍呢。耍起来也很简单,跟耍猴差不多。首先,你要new一个类实例,接着就可以通过我们刚才包装的那四个属性来读写设置项,最后,调用 Save 方法,就可以把修改后的数据保存到配置文件中。
让设置类与用户界面交互,最简单最方便的方法是使用绑定,比如这样。
<TextBox Grid.Column="1" Text="{Binding Path=AppTitle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <TextBox Grid.Column="1" Grid.Row="1" Text="{Binding Path=AppUsage,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> <TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Path=UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/> <TextBox Grid.Column="1" Grid.Row="3" Text="{Binding Path=UseYears,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
这个设置类是可以进行双向绑定的,因为基类 ApplicationSettingsBase 实现了 INotifyPropertyChanged 接口。通常我们可以在窗口关闭时保存配置。
protected override void OnClosing(CancelEventArgs e) { base.OnClosing(e); settings.Save(); }
以前我们开发程序,都习惯在界面上放一个保存按钮,当用户点击后保存,不过现在好像流行了,因为用户修改完设置后还要点一按钮来保存,操作有点复杂,让窗口在关闭时自动保存设置,显得更友好。
那么,这个破文件到底保存到哪里去了,在应用所在目录中的配置文件中并没有。应用目录中的配置文件存的应用程序级别的设置,用户级别的配置应该与当前用户的私人目录有关。
打开文件管理器,在地址栏中输入:%UserProfile%AppDataLocal,然后回车,就会进入当前用户文件夹下的appData的Local子目录,然后,在这个目录下,你会看到一个以你的应用程序命名的文件夹,然后你继续进入子目录,直到看到一个名为 user.config 的文件。对,就是它了,不信你打开看看。
<configuration> <configSections> <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <section name="DemoApp.AppSettings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> </sectionGroup> </configSections> <userSettings> <DemoApp.AppSettings> <setting name="UseYears" serializeAs="String"> <value>2</value> </setting> <setting name="UserName" serializeAs="String"> <value>矮冬瓜</value> </setting> <setting name="AppUsage" serializeAs="String"> <value>用于忽悠未成年人</value> </setting> <setting name="AppTitle" serializeAs="String"> <value>天国第一假货</value> </setting> </DemoApp.AppSettings> </userSettings> </configuration>
另外,ApplicationSettingsBase 类有几个事件比较有用,必要时可以用上。当设置数加载后会发生 SettingsLoaded 事件,从名字中也能知道其用途;在设置项被修改之前,会发生 SettingChanging 事件,修改之后会发生 PropertyChanged 事件(实现了INotifyPropertyChanged接口);当调用 Save 方法保存之前,会引发 SettingsSaving 事件,事件参数会包含一个 Cancel 属性,如果想取消保存,可以将该属性设置为 true。
示例源代码下载地址:请点击▶这里◀