我不像有些大牛能出书做MVP,有些大牛能卖组件开公司,我只是个小程序员。
今天主要是展示Silverlight的验证和OOB(Out Of browser)模式,对了,我平时最恨那些写博客只写简写而不注明全称的人,所以这里OOB就是Out Of browser。顾名思义,浏览器外,也就是Silverlight运行在浏览器外。OK,废话不多说,先上一张图,档案信息的修改界面。
点击修改按钮,d出档案信息修改界面。我们首先看看修改界面的UI代码。
<controls:ChilDWindow x:Class="MISInfoManage.ArchiveInfoModify" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:System.windows.Controls;assembly=System.windows.Controls" WIDth="480" Height="260" xmlns:LocalResource="clr-namespace:MISInfoManage.Resources"> <controls:ChilDWindow.Resources> <LocalResource:ArchiveInfoModifyResource x:Key="LocalResource"/> <Style x:Key="TitleColumnStyle" targettype="TextBlock"> <Setter Property="FontSize" Value="12"/> <Setter Property="HorizontalAlignment" Value="Right"/> </Style> </controls:ChilDWindow.Resources> <GrID x:name="LayoutRoot" margin="5"> <GrID.RowDeFinitions> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> <RowDeFinition Height="auto"/> </GrID.RowDeFinitions> <GrID.ColumnDeFinitions> <ColumnDeFinition WIDth="auto"/> <ColumnDeFinition WIDth="auto"/> <ColumnDeFinition WIDth="auto"/> <ColumnDeFinition WIDth="auto"/> </GrID.ColumnDeFinitions> <TextBlock Text="{Binding Tb_ArchiveNo,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" GrID.Row="0" GrID.Column="0"/> <TextBox Text="{Binding ArchiveNo,Mode=TwoWay}" GrID.Row="0" GrID.Column="1" FontSize="12" WIDth="150"/> <TextBlock Text="{Binding Tb_name,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" GrID.Row="0" GrID.Column="2" margin="20,0"/> <TextBox Text="{Binding name,Mode=TwoWay,NotifyOnValIDationError=True,ValIDatesOnExceptions=True,UpdateSourceTrigger=Explicit}" GrID.Row="0" GrID.Column="3" FontSize="12" WIDth="170"/> <TextBlock Text="{Binding Tb_IDCardNo,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" GrID.Row="1" GrID.Column="0" margin="0,5,0"/> <TextBox Text="{Binding IDCardNo, ValIDatesOnExceptions=True}" GrID.Row="1" GrID.Column="1" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_Sex,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" GrID.Row="1" GrID.Column="2" margin="20,0"/> <ComboBox GrID.Row="1" GrID.Column="3" SelectedValuePath="Tag" SelectedValue="{Binding Sex,Mode=TwoWay}" margin="0,0" FontSize="12"> <ComboBoxItem Content="男" Tag="1"></ComboBoxItem> <ComboBoxItem Content="女" Tag="0"></ComboBoxItem> </ComboBox> <TextBlock Text="{Binding Tb_Birth,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" margin="0,0" GrID.Row="2" GrID.Column="0"/> <sdk:DatePicker GrID.Row="2" GrID.Column="1" SelectedDate="{Binding BirthDay,ValIDatesOnExceptions=True}" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_TelNo,Source={StaticResource LocalResource}}" Style="{StaticResource TitleColumnStyle}" margin="20,0" GrID.Row="2" GrID.Column="2"/> <TextBox Text="{Binding TelNumber,Mode=TwoWay}" GrID.Row="2" GrID.Column="3" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_Professional,0" GrID.Row="3" GrID.Column="0"/> <TextBox Text="{Binding Professional,Mode=TwoWay}" GrID.Row="3" GrID.Column="1" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_Education,0" GrID.Row="3" GrID.Column="2"/> <ComboBox ItemsSource="{Binding EducationList,Mode=OneWay}" displayMemberPath="display_content" SelectedValuePath="data" SelectedValue="{Binding Education,Mode=TwoWay}" FontSize="12" margin="0,0" GrID.Row="3" GrID.Column="3"/> <TextBlock Text="{Binding Tb_GraduateSchool,0" GrID.Row="4" GrID.Column="0"/> <TextBox Text="{Binding GraduateSchool,Mode=TwoWay}" GrID.Row="4" GrID.Column="1" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_GraduateYear,0" GrID.Row="4" GrID.Column="2"/> <TextBox Text="{Binding GraduateYear,ValIDatesOnExceptions=True}" GrID.Row="4" GrID.Column="3" margin="0,0" FontSize="12"/> <TextBlock Text="{Binding Tb_ArchiveState,0" GrID.Row="5" GrID.Column="0"/> <ComboBox GrID.Row="5" GrID.Column="1" SelectedValuePath="Tag" SelectedValue="{Binding ArchiveState,0" FontSize="12"> <ComboBoxItem Content="已提" Tag="0"/> <ComboBoxItem Content="在库" Tag="1"/> </ComboBox> <StackPanel GrID.Row="6" GrID.Column="0" GrID.ColumnSpan="4" OrIEntation="Horizontal" HorizontalAlignment="Center" margin="0,10,0"> <button Content="{Binding Btn_Modify,Source={StaticResource LocalResource}}" WIDth="80" Height="25" margin="0,0" Click="button_Click" Tag="m"/> <button Content="{Binding Btn_Cancel,Source={StaticResource LocalResource}}" WIDth="80" Height="25" Click="button_Click" Tag="c"/> </StackPanel> </GrID> </controls:ChilDWindow>
大家看到了,也是GrID和StackPanel布局。我们看到里面有这样一段代码
<ComboBox GrID.Row="1" GrID.Column="3" SelectedValuePath="Tag" SelectedValue="{Binding Sex,0" FontSize="12"> <ComboBoxItem Content="男" Tag="1"></ComboBoxItem> <ComboBoxItem Content="女" Tag="0"></ComboBoxItem> </ComboBox>
注意,这里SelectedValuePath="Tag",这里指定了这个下拉列表的SelectedValue值绑定的是自己的ComboBoxItem 的Tag属性。如果选择了男,下拉列表的SelectedValue值就是1,如果选择女,则SelectedValue值是0。也就是这里绑定的SelectedValue="{Binding Sex,Mode=TwoWay}" ,即viewmodel的Sex属性。
再往下看有这么一段代码
<TextBox Text="{Binding name,ValIDatesOnExceptions=True}" GrID.Row="0" GrID.Column="3" FontSize="12" WIDth="170"/>
在这里绑定的是viewmodel的name属性,NotifyOnValIDationError和ValIDatesOnExceptions用来捕获验证出现的异常。我们来看看viewmodel的代码
namespace viewmodel { public class ArchiveInfoModifyModel { public string ArchiveNo { get; set; } private string archiveState; public string ArchiveState { get { return archiveState; } set { archiveState = value; NotifyPropertyChange("ArchiveState"); } } private string sex; public string Sex { get { return sex; } set { sex = value; NotifyPropertyChange("Sex"); } } private string education { get; set; } public string Education { get { return education; } set { education = value; NotifyPropertyChange("Education"); } } private DateTime? birthDay; public DateTime? BirthDay { get { return birthDay; } set { birthDay = value; if (value > DateTime.Now.AddYears(-15) || value < DateTime.Now.AddYears(-50)) { throw new ValIDationException("出生日期不正确!"); } NotifyPropertyChange("BirthDay"); } } private string professional; public string Professional { get { return professional; } set { professional = value; NotifyPropertyChange("Professional"); } } private int? graduateYear; [Range(1950,2012,ErrorMessage="毕业年份不正确!")] public int? GraduateYear { get { return graduateYear; } set { var valIDatorContext = new ValIDationContext(this, null, null); valIDatorContext.Membername = "GraduateYear"; ValIDator.ValIDateProperty(value, valIDatorContext); graduateYear = value; NotifyPropertyChange("GraduateYear"); } } private string name; [required(ErrorMessage="姓名不能为空!")] [StringLength(8,ErrorMessage="姓名长度不能超过8!")] public string name { get { return name; } set { var valIDatorContext = new ValIDationContext(this, null); valIDatorContext.Membername = "name"; ValIDator.ValIDateProperty(value, valIDatorContext); name = value; NotifyPropertyChange("name"); } } private string IDCardNo; public string IDCardNo { get { return IDCardNo; } set { IDCardNo = value; NotifyPropertyChange("IDCardNo"); } } private string graduateSchool; public string GraduateSchool { get { return graduateSchool; } set { graduateSchool = value; NotifyPropertyChange("GraduateSchool"); } } private string telNumber; public string TelNumber { get { return telNumber; } set { telNumber = value; NotifyPropertyChange("TelNumber"); } } private ObservableCollection<viewmodel.ArchiveInfoService.Codes> educationList; public ObservableCollection<viewmodel.ArchiveInfoService.Codes> EducationList { get { return educationList; } set { educationList = value; NotifyPropertyChange("EducationList"); } } public event PropertyChangedEventHandler PropertyChanged; private voID NotifyPropertyChange(string property) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(property)); } } } }
我们看到了name和GraduateYear等加入了验证。name验证了非空和长度,采用的是标注Attribute的格式,和MVC的验证方式一样,需要引入System.ComponentModel.DataAnnotations命名空间。其中的Birthday属性采用了抛出异常的方式,当抛出异常后,页面UI会接收到异常信息,并显示红色的提示信息,因为Birthday设置了ValIDatesOnExceptions=True。我们来看看验证的效果。
UI会提示出生日期不正确。如果毕业年份输入不正确,也会给出提示信息。
因为毕业年份设置了NotifyOnValIDationError=True。所以在这里NotifyOnValIDationError对应于设置ValIDationAttribute的方式,而ValIDatesOnExceptions对应于Set访问器中的抛出异常方式。在这里我特别要注意说明的是
<TextBox Text="{Binding name,UpdateSourceTrigger=Explicit}" GrID.Row="0" GrID.Column="3" FontSize="12" WIDth="170"/>
在这里有一个UpdateSourceTrigger,这个属性有两个值,一个是默认的Default,一个是Explicit,有什么区别呢?我们在用MVVM的时候,经常会碰到这样一种场景,在文本框输入一个值,然后直接按回车键查数据或者别的什么 *** 作,但是你会发现回车后取得的文本框得值仍然是文本框上次的值(没有失去焦点前的值)。为什么呢?这就是这里要说的UpdateSourceTrigger,在双向绑定下,大多数控件默认是在PropertyChanged以后就会更改viewmodel的值,但是TextBox则默认是失去焦点以后才会更改viewmodel的值。所以在这里我也不默认了,直接改成UpdateSourceTrigger=Explicit,显式的调用BindingExpression的UpdateSource方法获取页面上的值,将其反映到viewmodel上。这就意味着,只有在在调用了UpdateSource方法以后才会启用验证。上述讲的验证只是在失去焦点以后的验证,大多数情况下我们是要点击提交按钮以后来验证,并且验证不通过,不能再执行下面的逻辑。这个在我们web开发中很容易实现,但是在Silverlight中怎么实现呢?我们看看后台
using System; using System.Collections.Generic; using System.Globalization; using System.linq; using System.Net; using System.Threading; using System.windows; using System.windows.Data; using System.windows.Controls; using System.windows.documents; using System.windows.input; using System.windows.Media; using System.windows.Media.Animation; using System.windows.Shapes; using ClIEnt.Common; using viewmodel; namespace MISInfoManage { public partial class ArchiveInfoModify : ChilDWindow { string archiveNo; ArchiveInfoModifyModel viewmodel; viewmodel.ArchiveInfoService.ArchiveInfoServiceClIEnt clIEnt; public ArchiveInfoModify() { InitializeComponent(); this.Title = "档案信息修改"; } public ArchiveInfoModify(string archiveNo) : this() { this.archiveNo = archiveNo; viewmodel = new ArchiveInfoModifyModel(); this.Loaded += delegate(object sender, RoutedEventArgs e) { clIEnt = new viewmodel.ArchiveInfoService.ArchiveInfoServiceClIEnt(); clIEnt.GetEducationCompleted += delegate(object sender1, viewmodel.ArchiveInfoService.GetEducationCompletedEventArgs e1) { viewmodel.EducationList = new System.Collections.ObjectModel.ObservableCollection<viewmodel.ArchiveInfoService.Codes>(e1.Result); this.GetArchiveInfoByNo(); }; clIEnt.GetEducationAsync(); }; } private voID button_Click(object sender, RoutedEventArgs e) { button button = sender as button; if (button.Tag.ToString().Equals("m")) { if (!this.ValIDateData()) { return; } viewmodel.ArchiveInfoService.Person_Info personInfo = new viewmodel.ArchiveInfoService.Person_Info() { birth = viewmodel.BirthDay.Value, contact_tel = viewmodel.TelNumber, education_level = viewmodel.Education, graduate_school = viewmodel.GraduateSchool, graduate_year = viewmodel.GraduateYear.Value, ID_card = viewmodel.IDCardNo, name = viewmodel.name, no = viewmodel.ArchiveNo, professional = viewmodel.Professional, sex = viewmodel.Sex, state = viewmodel.ArchiveState }; clIEnt.ModifyArchiveInfoCompleted += delegate(object modifySender, viewmodel.ArchiveInfoService.ModifyArchiveInfoCompletedEventArgs modifyArgs) { new Thread(() => { UisynchronizationContext.Context.Post((state) => { MessageBox.Show("修改成功!"); }, null); }).Start(); }; clIEnt.ModifyArchiveInfoAsync(personInfo); } if (button.Tag.ToString().Equals("c")) { this.Close(); } } private voID GetArchiveInfoByNo() { clIEnt.GetPersonInfoByIDCompleted += delegate(object sender, viewmodel.ArchiveInfoService.GetPersonInfoByIDCompletedEventArgs e) { viewmodel.ArchiveInfoService.Person_Info personInfo = e.Result; this.viewmodel.ArchiveNo = personInfo.no; this.viewmodel.ArchiveState = personInfo.state; this.viewmodel.BirthDay = personInfo.birth; this.viewmodel.Education = personInfo.education_level; this.viewmodel.GraduateSchool = personInfo.graduate_school; this.viewmodel.GraduateYear = personInfo.graduate_year; this.viewmodel.IDCardNo = personInfo.ID_card; this.viewmodel.name = personInfo.name; this.viewmodel.Professional = personInfo.professional; this.viewmodel.Sex = personInfo.sex; this.viewmodel.TelNumber = personInfo.contact_tel; this.LayoutRoot.DataContext = viewmodel; }; clIEnt.GetPersonInfoByIDAsync(archiveNo); } private bool ValIDateData() { UIElementCollection UIElments = LayoutRoot.Children; foreach (var element in UIElments) { if (element.GetType() == typeof(TextBox)) { TextBox textBox = element as TextBox; BindingExpression Expression = textBox.GetBindingExpression(TextBox.TextProperty); if (Expression.ParentBinding.NotifyOnValIDationError == true || Expression.ParentBinding.ValIDatesOnExceptions == true) { Expression.UpdateSource(); if (ValIDation.GetHasError(textBox)) { return false; } } } if (element.GetType() == typeof(ComboBox)) { ComboBox comboBox = element as ComboBox; BindingExpression Expression = comboBox.GetBindingExpression(ComboBox.SelectedValueProperty); if (Expression.ParentBinding.NotifyOnValIDationError == true || Expression.ParentBinding.ValIDatesOnExceptions == true) { Expression.UpdateSource(); if (ValIDation.GetHasError(comboBox)) { return false; } } } if (element.GetType() == typeof(DatePicker)) { DatePicker datepicker = element as DatePicker; BindingExpression Expression = datepicker.GetBindingExpression(DatePicker.SelectedDateProperty); if (Expression.ParentBinding.NotifyOnValIDationError == true || Expression.ParentBinding.ValIDatesOnExceptions == true) { Expression.UpdateSource(); if (ValIDation.GetHasError(datepicker)) { return false; } } } } return true; } } }
我们看看这个button_Click事件,在修改方法中,我们调用了ValIDateData方法,该方法循环遍历页面UIElment,并强制更新viewmodel中的值。如果发现验证不通过,直接返回False,在button_Click事件中,如果没有通过验证,就不再执行修改 *** 作。这样就实现了验证不通过,就不再往下走的逻辑。关于验证,我就说到这里,如有不懂,可以加入.net群205217091,我可以把源代码共享给大家。我们看看服务端代码
public int ModifyArchiveInfo(Person_Info personInfoModel) { Person_Info personInfo = misInfoEntitIEs.person_info.SingleOrDefault(p => p.no.Equals(personInfoModel.no)); Type type = personInfo.GetType(); PropertyInfo[] propertyInfos = type.GetPropertIEs().Where(p=>!p.name.Equals("ID")).ToArray(); foreach (var propertyInfo in propertyInfos) { propertyInfo.SetValue(personInfo, propertyInfo.GetValue(personInfoModel, null)); } return misInfoEntitIEs.SaveChanges(); }
这个后台代码也没什么,循环遍历利用反射赋值,最后调用DBContext的SaveChanges方法完成更新。好了,最后我们看看OOB模式。
在Silverlight 项目上点击右键,打开属性设置界面,如下所示
我们勾选Enable running application out of browser,然后点击Out-of-browser Settings按钮,d出设置界面,如下
我们设置了高度和宽度,以及window style等信息。OK,我们再次将程序运行起来。我们发现点击右键多了一项“将MISinformation Application 安装到此计算机...”。我们点击安装后,出现下面的界面
勾选开始和桌面后,点击确定,我们发现桌面上多了一个图标,如下所示
我们双击它,d出如下界面,即我们的OOB运行模式
OK,看到了吧,Title是我们上面设置的档案信息管理。怎么样,这样的Silverlight运行方式是不是很不错。它同样能够实现在浏览器中的功能,如下
好了,今天就讲这么多,如果大家需要源代码,直接找我要,或者加入.net群205217091。
总结以上是内存溢出为你收集整理的Silverlight MVVM 贴近实战(五)全部内容,希望文章能够帮你解决Silverlight MVVM 贴近实战(五)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)