Silverlight MVVM 贴近实战(五)

Silverlight MVVM 贴近实战(五),第1张

概述我不像有些大牛能出书做MVP,有些大牛能卖组件开公司,我只是个小程序员。 今天主要是展示Silverlight的验证和OOB(Out Of Browser)模式,对了,我平时最恨那些写博客只写简写而不注明全称的人,所以这里OOB就是Out Of Browser。顾名思义,浏览器外,也就是Silverlight运行在浏览器外。OK,废话不多说,先上一张图,档案信息的修改界面。 点击修改按钮,d出档案

我不像有些大牛能出书做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 { getset; }                   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 { getset; }              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(thisnullnull);                      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(thisnull);                      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(thisnew 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 贴近实战(五)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1066952.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存