wenmo8 发布的文章

在我们开发一些项目的时候,一般需要一些外围的设备进行数据处理,如ID/IC读卡器获取卡号、激光条码扫描枪、USB摄像头、USB方式的小票据打印机(POS打印机)、USB来电录音盒、普通打印机等一系列附属设备。借助这些设备,可以使我们的业务流程更严谨,输入数据更方便,或者能够一些特殊的数据等功能。本文主要介绍其中的ID读卡器(IC读卡器)快速读取卡号,以及实用激光条码枪的条码扫描录入功能,后面的一些硬件设备的处理,后续文章在继续介绍。

1、设备介绍

前面介绍的设备,在很多场合上都可能用到,如我的会员管理系统里面,就需要用到下面的设备处理。

本文主要针对性了解ID读卡器和条码枪的设备数据处理,这两种设备虽然不同,但是它们相似的地方就是都支持在光标处录入数据的,就有点类似我们的键盘快速录入一样,当然激光条码枪也支持很多种方式的事件处理操作,这是后话。

2、ID读卡器数据读取界面和条码扫描枪读取界面的分析介绍

在我的会员管理系统里面,录入卡号一般是通过ID读卡器获取的,在界面上设置一个可以弹出录入的文本框,也方便手工录入卡号,如下面的界面功能所示。

当然,有时候,我们可能不需要提供手工录入,那么就不能通过光标录入方式获取扫描的内容,因为我们把输入框设置为只读的了,所以这种情况,就就应该通过事件来获取设备的输入内容。

在条码枪处理读取条形码或者二维码的时候,我们一般都是和商品相关的地方使用条形码,二维码也可以使用,条形码可能一般带有数据供阅读,二维码则没有,但是都可以通过设备读取出来到文本框里面,一般如果录入,就停放光标在文本框就可以了,如商品的信息的录入。在我们需要输入条码的地方点一下,然后操作条码枪录入条码即可,这种不需要额外的代码处理。

但是对于一些我们需要快速录入商品信息的界面,如客户消费界面,那么就需要对条码的事件进行处理了。

例如下面的界面,在消费确认前的产品录入,我们都是通过条码枪的快速扫描产品进行录入的,这时候条码枪就代替了手工的录入,我们可以每次扫描一次,就在列表里面自动增加一个对应商品的记录,非常方便的了。

3、通用的读卡操作和条码扫描枪操作实现

在前面小节介绍了一些利用ID读卡器录入数据和使用条码枪的场景,对于如果是在可输入文本框里面获得内容,不用任何编码,如果是在只读界面或者窗体上获得设备的数据,那么就可以通过事件进行处理了,那么读卡器和扫描枪的事件应该如何处理的呢。

我的做法,是统一在我的Winform开发框架的界面层基类模块里面,增加一些硬件相关的处理类和界面,这样在各个框架派生出来的项目就可以很方便使用了。

其中Device里面的CardReader就是IC、ID读卡器获取操作的处理,一般来说,这些卡都是以00开始的,所以我们的处理类,通过一个Time来控制连续获取数据的处理就可以了,主要就是监听KeyUp事件。

以CardReader为例,它的完整代码如下所示。

    /// <summary>
    ///读卡器封装类/// </summary>
    public classCardReader
{
privateControl _hostCtrl;private string_cardCode;privateTimer _timer;private const int CARD_CODE_LEN = 10;private const string CARD_CODE_START = "00";/// <summary> ///读卡器读到一张卡的事件/// </summary> public eventCardReadEventHandler CardRead;/// <summary> ///默认读卡器(挂在主窗体上,会被主窗体初始化,在模块里用肯定是安全的)/// </summary> public static CardReader Default { get; set; }/// <summary> ///构造器/// </summary> /// <param name="hostCtrl">接受键盘事件的宿主控件</param> publicCardReader(Control hostCtrl)
{
_hostCtrl
=hostCtrl;if (_hostCtrl isForm)
{
(_hostCtrl
as Form).KeyPreview = true;
}
_hostCtrl.KeyUp
+= newKeyEventHandler(hostCtrl_KeyUp);
_cardCode
= "";
_timer
= newTimer();
_timer.Interval
= 20;
_timer.Tick
+= newEventHandler(timer_Tick);
_timer.Start();
}
/// <summary> ///判断是否卡号/// </summary> /// <param name="code"></param> /// <returns></returns> public static bool IsCardCode(stringcode)
{
return code.Length == CARD_CODE_LEN &&code.StartsWith(CARD_CODE_START);
}
/// <summary> ///定时器到期的事件/// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void timer_Tick(objectsender, EventArgs e)
{
//达到一定的位数才开始判断 if (_cardCode.Length >=CARD_CODE_LEN)
{
_cardCode
= _cardCode.Trim((char)13);if(IsCardCode(_cardCode))
{
_timer.Stop();
OnCardRead(_cardCode);
}
}
_cardCode
= "";
_timer.Start();
}
/// <summary> ///监听按键弹起的事件/// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void hostCtrl_KeyUp(objectsender, KeyEventArgs e)
{
_timer.Stop();
_cardCode
= _cardCode + (char)e.KeyValue;
_timer.Start();
}
private void OnCardRead(stringscanCode)
{
if (CardRead != null)
{
CardRead(scanCode);
}
}

}
/// <summary> ///读卡器读到一张卡的事件处理委托/// </summary> /// <param name="cardCode"></param> public delegate void CardReadEventHandler(string cardCode);

CardReader封装类,

的使用操作如下所示。我们通过事件就可以获取到完整的输入内容,然后进行数据的绑定或处理即可,代码如下所示。

    public partial classFrmProcessConsumption : BaseDock
{
/// <summary> ///会员信息/// </summary> private MemberInfo memberInfo { get; set; }/// <summary> ///读卡器接口/// </summary> privateCardReader cardReader;publicFrmProcessConsumption()
{
InitializeComponent();

................................

cardReader
= new CardReader(this);
cardReader.CardRead
+= newCardReadEventHandler(cardReader_CardRead);
}
void cardReader_CardRead(stringcardCode)
{
this.txtMember_CardNo.Text =cardCode;

BindMemberData();
}

然后我们为了方便使用,还可以定义一个统一的处理读卡器和扫描枪的接收数据的小窗口。

这个弹出的小窗口用来处理读卡器,扫描枪等信息的录入就可以了,当然上述的如CardReader/USBScanner还是可以独立使用,如我们在一个只读控件或者窗口里面,一样可以监听到对应的设备数据读取操作,但设备有数据读取完成过后,就会触发相应的事件了。

下面代码就是上面设备信息读取的代码

    /// <summary>
    ///读卡器、USB条码扫描器、串口条码扫描器数据读取及显示窗体/// </summary>
    public partial classDeviceReaderDialog : BaseForm
{
privateCardReader _cardReader;privateUSBScanner _usbScanner;public DeviceReaderDialog(DeviceType type =DeviceType.Card)
{
InitializeComponent();
//能手填 this.Readonly = false;if (type ==DeviceType.Card)
{
this._cardReader = new CardReader(this);this._cardReader.CardRead += newCardReadEventHandler(_cardReader_CardRead);
}
else if (type ==DeviceType.UsbScanner)
{
this._usbScanner = new USBScanner(this);this._usbScanner.ScannerRead += newScannerReadEventHandler(Scanner_ScannerRead);
}
}void Scanner_ScannerRead(stringscanCode)
{
this.txtCode.Text =scanCode;
DialogResult
=DialogResult.OK;
}
void _cardReader_CardRead(stringcardCode)
{
this.txtCode.Text =cardCode;
DialogResult
=DialogResult.OK;
}
public stringCode
{
get { returntxtCode.Text; }
}
public boolReadonly
{
get { returntxtCode.Properties.ReadOnly; }set{
txtCode.Properties.ReadOnly
=value;this.btnOK.Enabled = !value;this.btnOK.Visible = !value;
}
}
private void DeviceReaderDialog_Load(objectsender, EventArgs e)
{
if (!this.Readonly)
{
this.KeyDown += newKeyEventHandler(DeviceReaderDialog_KeyDown);
}
}
void DeviceReaderDialog_KeyDown(objectsender, KeyEventArgs e)
{
if (e.KeyCode ==Keys.Enter)
{
this.DialogResult =DialogResult.OK;
}
}
}

在我的博客里面,很多Winform程序里面都用到了分页处理,这样可以不管是在直接访问数据库的场景还是使用网络方式访问WCF服务获取数据,都能获得较好的效率,因此WInform程序里面的分页控件的使用是很好的一个解决方式,它能够快速获取数据,并将可能减少我们增加太多的代码逻辑,实现简单、高效、统一的理念。本篇主要介绍如何在分页的GridControl里面如何实现数据的统计功能。

一般情况下,分页控件呈现分为两种方式,一种需要有分页码的真正分页处理,一种是不需要分页处理,但是列表和功能基本保持一致的界面,其实这两种情况都是对列表控件的进一步封装,以方便使用的目的。

有分页底栏的控件界面:

无需分页的,保持一致界面的控件效果。

这两种方式,都是对GridControl控件的封装,而且他们的使用步骤和属性基本上保持一致。

使用代码如下所示。

            this.winGridView1.BestFitColumnWith = false;this.winGridView1.AppendedMenu = this.contextMenuStrip1;this.winGridView1.gridView1.DataSourceChanged += newEventHandler(gridView1_DataSourceChanged);this.winGridView1.OnRefresh += newEventHandler(winGridView1_OnRefresh);this.winGridView1.gridView1.CustomColumnDisplayText += newDevExpress.XtraGrid.Views.Base.CustomColumnDisplayTextEventHandler(gridView1_CustomColumnDisplayText);this.winGridView1.gridView1.RowCellStyle += new DevExpress.XtraGrid.Views.Grid.RowCellStyleEventHandler(gridView1_RowCellStyle);

那么我们如何在分页控件的基础上增加统计功能呢,由于分页控件的字段是根据配置自动创建的,我们需要在创建后,数据绑定的时候,把它增加上去,这样我们就可以实现数据的统计处理了,我们最终希望的统计效果是在底部统计栏目里面增加一些特殊的值的统计处理,界面如下所示,这个是我Winform开发框架里面,对于库存查询统计的一个案例,供参考。

上面的统计实现代码如下所示。

        /// <summary>
        ///常见汇总信息/// </summary>
        private voidCreateSummary()
{
GridView gridView1
= this.winGridView1.gridView1;if (gridView1 != null && gridView1.Columns.Count > 0)
{
gridView1.GroupSummary.Clear();

gridView1.OptionsView.ColumnAutoWidth
= false;
gridView1.OptionsView.GroupFooterShowMode
=DevExpress.XtraGrid.Views.Grid.GroupFooterShowMode.VisibleAlways;
gridView1.OptionsView.ShowFooter
= true;
gridView1.OptionsView.ShowGroupedColumns
= true;
gridView1.OptionsView.ShowGroupPanel
= false;

DevExpress.XtraGrid.Columns.GridColumn IDColumn
= gridView1.Columns["ID"];
IDColumn.Summary.AddRange(
newDevExpress.XtraGrid.GridSummaryItem[] {new DevExpress.XtraGrid.GridColumnSummaryItem(DevExpress.Data.SummaryItemType.Count, "ID", "记录数:{0}")});

DevExpress.XtraGrid.Columns.GridColumn StockQuantityColumn
= gridView1.Columns["STOCKQUANTITY"];
StockQuantityColumn.Summary.AddRange(
newDevExpress.XtraGrid.GridSummaryItem[] {new DevExpress.XtraGrid.GridColumnSummaryItem(DevExpress.Data.SummaryItemType.Sum, "STOCKQUANTITY", "库存数量:{0}")});

DevExpress.XtraGrid.Columns.GridColumn StockAmountColumn
= gridView1.Columns["STOCKAMOUNT"];
StockAmountColumn.Summary.AddRange(
newDevExpress.XtraGrid.GridSummaryItem[] {new DevExpress.XtraGrid.GridColumnSummaryItem(DevExpress.Data.SummaryItemType.Sum, "STOCKAMOUNT", "库存金额:{0:C2}")});
}
}

这个函数,我们可以在数据变化的时候,重新调用函数进行统计设置即可,如下代码所示。

        private void gridView1_DataSourceChanged(objectsender, EventArgs e)
{
if (this.winGridView1.gridView1.Columns.Count > 0 && this.winGridView1.gridView1.RowCount > 0)
{
//统一设置100宽度 foreach (DevExpress.XtraGrid.Columns.GridColumn column in this.winGridView1.gridView1.Columns)
{
column.Width
= 100;
}
//可特殊设置特别的宽度 SetGridColumWidth("Note", 200);
SetGridColumWidth(
"ItemNo", 120);
SetGridColumWidth(
"ItemBigType", 120);
SetGridColumWidth(
"WareHouse", 120);

SetGridColumWidth(
"ID", 100);
SetGridColumWidth(
"StockQuantity", 120);
SetGridColumWidth(
"StockAmount", 160);//ID,StockQuantity,Unit,Price SetGridColumWidth("Unit", 80);
SetGridColumWidth(
"Price", 80);
}

CreateSummary();
}

以上就是基于DevExpress样式的Winform分页控件的基础上实现的统计数据效果,希望对你使用有帮助。

我们在开发一些Winform程序的时候,除了常规的显示普通数据外,有的时候需要显示一些人员肖像或者一些车辆等物体的图片,一般这些内容较小,所以以二进制存储在数据库是一个不错的方案。但由于它们虽然很常用,设计数据库保存的逻辑又会使得整个控件的封装显得麻烦很多。本文介绍的肖像显示保存控件,通过事件的封装处理,让数据的保存不在依赖于数据库存储模块,实现更加通用的特性。

1、肖像显示保存控件的需求

我们在一些程序了里面,可能需要显示一些人员头像,车辆图片,物件图片等,这些图片可以从电脑上选取,也可以拍照,当然最重要的是,方便使用,并能存储到数据库里面,这个就是我们一般的需求了。

1)人员肖像显示效果:

2)车辆图片展示效果

控件的工具条上,提供了编辑图片,保存图片,恢复默认图片,拍照等功能,当然我们希望控件和整个界面一起,实现图片数据的方便加载和保存操作。

2、肖像显示保存控件的设计

前面我们看了一些常用的场景,对我们设计这个通用性的肖像显示保存控件有很好的指导意义,我们只需要把图片显示部分作为一个控件,然后公布一些事件给外部实现,从而实现我们需要的数据加载、数据保存等操作,至于其他的功能,我们可以集成到里面去。

我们设计一个用户自定义控件,上面放一个图片显示框,然后增加一些按钮在上面,并设置好工具栏的图片显示,效果如下所示。

由于我们需要给外部处理数据的绑定(需要从数据库加载)和数据的保存(保存到数据库里面),因此抛出两个委托代理定义。

    /// <summary>
    ///保存图片的处理代理定义/// </summary>
    /// <param name="id">记录ID</param>
    /// <param name="imageBytes">图片数据</param>
    /// <param name="imageType">图片类型</param>
    /// <returns></returns>
    public delegate bool SavePortraitHandler(string id, byte[] imageBytes, stringimageType);/// <summary>
    ///绑定图片数据的代理定义/// </summary>
    /// <param name="id">记录ID</param>
    /// <param name="imageType">图片类型</param>
    /// <returns></returns>
    public delegate byte[] BindPortraitHandler(string id, string imageType);

然后我们在用户控件里面增加一些属性和事件定义,部分代码如下所示。

    /// <summary>
    ///图片数据显示和采集控件/// </summary>
    public partial classPortraitControl : XtraUserControl
{
#region 事件及属性定义 /// <summary> ///保存图片操作的事件/// </summary> public eventSavePortraitHandler OnSavePortrait;/// <summary> ///绑定图片数据的事件/// </summary> public eventBindPortraitHandler OnBindPortait;/// <summary> ///记录ID/// </summary> public string ID { get; set; }/// <summary> ///图片是否被修改过/// </summary> public bool ImageIsDirty { get; set; }

然后我们需要实现的就是,公布两个公共方法,分别是数据的绑定,绑定到界面控件的操作函数,我们主要注意的是,这里调用了事件
OnBindPortait
,以实现数据库的解耦,代码如下所示。

        /// <summary>
        ///绑定图片的操作,触发绑定事件处理/// </summary>
        /// <param name="id">记录ID</param>
        public void BindPicture(stringid)
{
try{this.ID = id; //设置控件的ID值 #region 更新图片显示操作 if (OnBindPortait == null)
{
MessageDxUtil.ShowTips(
"控件未指定OnBindPortait事件处理");return;
}
byte[] imageBytes = OnBindPortait(id, ImageType);if (imageBytes != null)
{
this.picPortrait.Image =ImageHelper.ImageFromBytes(imageBytes);
}
else{
ResetDefaultImage(ImageType);
}
#endregion}catch(Exception ex)
{
MessageDxUtil.ShowError(ex.Message);
LogTextHelper.Error(ex);
}
}

数据库保存的实现,和上面思路差不多,也是实现事件的处理即可,通过调用事件
OnSavePortrait
,实现数据库的解耦。

        /// <summary>
        ///保存图片到服务器/// </summary>
        public bool SavePicture(stringid)
{
this.ID = id;//设置控件的ID值 if (string.IsNullOrEmpty(id))
{
MessageDxUtil.ShowTips(
"记录ID未指定,无法保存,请先保存数据!");return false;
}
if (OnSavePortrait == null)
{
MessageDxUtil.ShowTips(
"控件未指定OnSavePortrait处理事件!");return false;
}
if (picPortrait.Image != null)
{
try{byte[] imageBytes = ImageHelper.ImageToBytes(this.picPortrait.Image);bool sucess = false;if (OnSavePortrait != null)
{
sucess
= OnSavePortrait(this.ID, imageBytes, ImageType);
}
returnsucess;

}
catch(Exception ex)
{
MessageDxUtil.ShowError(ex.Message);
LogTextHelper.Error(ex);
}
}
return false;
}

3、肖像显示保存控件的使用

完成以上控件的设计和处理后,编译后,模块将增加一个用户控件,这样我们就可以在需要的界面模块里面,把这个控件拖动到设计界面里面去了,设计效果和下图类似。

这个时候,肖像显示保存控件就作为一个整体进行使用了。

由于我们设计控件的时候,我们把它和外部数据库存储和加载进行了隔离,因此这里需要进行整合,通过事件进行整合处理。调用代码如下所示。

    public partial classFrmEditDriver : BaseEditForm
{
publicFrmEditDriver()
{
InitializeComponent();
this.portraitControl1.ImageType = "个人肖像";this.portraitControl1.OnBindPortait += new BindPortraitHandler(portraitControl1_OnBindPortait);
this.portraitControl1.OnSavePortrait += new
SavePortraitHandler(portraitControl1_OnSavePortrait);
}

然后实现其中自动增加的事件函数即可,主要就是调用业务类实现数据的存储和加载处理逻辑,代码如下所示。

        bool portraitControl1_OnSavePortrait(string id, byte[] imageBytes, stringimageType)
{
return BLLFactory<Driver>.Instance.UpdateImageBytes(id, imageBytes, imageType);
}
byte[] portraitControl1_OnBindPortait(string id, stringimageType)
{
return BLLFactory<Driver>.Instance.GetImageBytes(id, imageType);
}

在数据的显示函数里面,我们主动调用控件的函数实现界面数据的绑定显示。

        /// <summary>
        ///数据显示的函数/// </summary>
        public override voidDisplayData()
{
InitDictItem();
//数据字典加载(公用) if (!string.IsNullOrEmpty(ID))
{
#region 显示信息DriverInfo info= BLLFactory<Driver>.Instance.FindByID(ID);if (info != null)
{
tempInfo
= info;//重新给临时对象赋值,使之指向存在的记录对象 txtHandNo.Text=info.HandNo;
txtName.Text
=info.Name;
txtMobile.Text
=info.Mobile;
txtDept.Text
=info.Dept;
txtStartDriveDate.SetDateTime(info.StartDriveDate);
txtSex.Text
=info.Sex;
txtBirthday.SetDateTime(info.Birthday);
txtNote.Text
=info.Note;
}
#endregion }else{ }//绑定图片 this.portraitControl1.BindPicture(tempInfo.ID);
}

而在保存按钮实现数据保存的时候,我们也可以调用控件自身的保存操作函数,实现数据的存储。

        /// <summary>
        ///新增状态下的数据保存/// </summary>
        /// <returns></returns>
        public override boolSaveAddNew()
{
DriverInfo info
= tempInfo;//必须使用存在的局部变量,因为部分信息可能被附件使用 SetInfo(info);try{#region 新增数据 bool succeed = BLLFactory<Driver>.Instance.Insert(info);if(succeed)
{
//可添加其他关联操作 this.portraitControl1.SavePicture(tempInfo.ID);return true;
}
#endregion}catch(Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
return false;
}

通过和数据库操作实现解耦,我们可以对这个控件进行更方便的重用,而代码也很简单,这样也就实现了我们统一化、简单化、可复用性的目标了。

在Web开发的时候,我们很多时候,需要引用很多CSS文件、JS文件,随着使用更多的插件或者独立样式文件,可能我们的Web界面代码会越来越臃肿,看起来也很累赘,在MVC里面提供了一个Bundle的对象,用来简化页面代码非常方便,本文主要介绍在我的MVC框架里面,如何使用bundles来简化页面的代码的。

1、常规的页面代码

我们知道,随着使用更多的一些效果,我们可能不断引入一些新的JS和CSS文件,已达到Web界面更好的表现效果。这样也就逐步增加了文件代码的行数,造成相对比较臃肿的场景,如下面的我正常使用的Web界面,头部需要引入很多JS和CSS文件。

@*添加Jquery EasyUI的样式*@<linkhref="~/Content/JqueryEasyUI/themes/default/easyui.css"rel="stylesheet"type="text/css" />
    <linkhref="~/Content/JqueryEasyUI/themes/icon.css"rel="stylesheet"type="text/css" />

    <linkhref="~/Content/themes/Default/style.css"rel="stylesheet"type="text/css" />
    <linkhref="~/Content/themes/Default/default.css"rel="stylesheet"type="text/css" />@*添加Jquery,EasyUI和easyUI的语言包的JS文件*@<scripttype="text/javascript"src="~/Content/JqueryEasyUI/jquery.min.js"></script>
    <scripttype="text/javascript"src="~/Content/JqueryEasyUI/jquery.easyui.min.js"></script>
    <scripttype="text/javascript"src="~/Content/JqueryEasyUI/locale/easyui-lang-zh_CN.js"></script>@*日期格式的引用*@<scriptsrc="~/Content/datapattern.js"></script>
    
    <!--引用EasyUI扩展-->
    <linkhref="~/Content/JQueryTools/jQuery.easyui-extend/extend/themes/easyui.extend.css"rel="stylesheet" />
    <linkhref="~/Content/JQueryTools/jQuery.easyui-extend/extend/themes/icon.css"rel="stylesheet" />
    <scriptsrc="~/Content/JQueryTools/jQuery.easyui-extend/jquery.easyui.extend.min.js"></script>@*引用提示控件*@<linkrel="stylesheet"type="text/css"href="~/Content/JQueryTools/jNotify/jquery/jNotify.jquery.css"media="screen" />
    <scripttype="text/javascript"src="~/Content/JQueryTools/jNotify/jquery/jNotify.jquery.js"></script>@*常用的一些组件业务脚本函数*@<scripttype="text/javascript"src="~/Scripts/ComponentUtil.js"></script>

然后这样的文件总是在不断的复制做,非常不雅观,维护也不方便。

在ASP.NET MVC出来之后,引入了一个叫做Bundle的东西,它用来将js和css文件捆绑为一个块进行输出,能够极大简化界面代码,并默认对这些内容进行压缩处理,提高效率。

最终简化的界面代码如下所示。

@using System.Web.Optimization;
@Scripts.Render("~/bundles/jquery")
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquerytools")
@Styles.Render("~/Content/jquerytools")

2、使用bundles优化的界面操作

为了实现上面的效果,我们需要进行几步的操作处理。

在App_Start里面的BundleConfig里面增加几行处理代码,如下所示。

    public classBundleConfig
{
//有关 Bundling 的详细信息,请访问http://go.microsoft.com/fwlink/?LinkId=254725 public static voidRegisterBundles(BundleCollection bundles)
{
//为了减少太多的Bundles命名,定义的CSS的Bundle为:"~/Content/css"、"~/Content/jquerytools"//定义的Script的Bundles为:"~/bundles/jquery"、"~/bundles/jquerytools"//Jquery必备的StyleBundle和ScriptBundle StyleBundle css = new StyleBundle("~/Content/css");
ScriptBundle jquery
= new ScriptBundle("~/bundles/jquery");//添加Jquery EasyUI的样式 css.Include("~/Content/JqueryEasyUI/themes/default/easyui.css","~/Content/JqueryEasyUI/themes/icon.css","~/Content/themes/Default/style.css","~/Content/themes/Default/default.css");//添加Jquery,EasyUI和easyUI的语言包的JS文件,日期格式的引用 jquery.Include("~/Content/JqueryEasyUI/jquery.min.js","~/Content/JqueryEasyUI/jquery.easyui.min.js","~/Content/JqueryEasyUI/locale/easyui-lang-zh_CN.js","~/Content/datapattern.js");//常用的一些组件业务脚本函数(建议放到最后) jquery.Include("~/Scripts/ComponentUtil.js");//扩展的StyleBundle和ScriptBundle StyleBundle cssExtend = new StyleBundle("~/Content/jquerytools");
ScriptBundle jqueryExtend
= new ScriptBundle("~/bundles/jquerytools");//引用EasyUI扩展 cssExtend.Include("~/Content/JQueryTools/jQuery.easyui-extend/extend/themes/easyui.extend.css","~/Content/JQueryTools/jQuery.easyui-extend/extend/themes/icon.css");
jqueryExtend.Include(
"~/Content/JQueryTools/jQuery.easyui-extend/jquery.easyui.extend.min.js");//引用消息提示控件 cssExtend.Include("~/Content/JQueryTools/jNotify/jquery/jNotify.jquery.css");
jqueryExtend.Include(
"~/Content/JQueryTools/jNotify/jquery/jNotify.jquery.js");//其他一些辅助脚本和样式//全部增加到集合里面去 bundles.Add(css);
bundles.Add(jquery);
bundles.Add(cssExtend);
bundles.Add(jqueryExtend);
}
}

上面代码,我们增加一些必要的Jquery和一些扩展给的JqueryTool的脚本和样式,方便统一化管理。

默认的情况下,Bundle是按照字母顺序进行排序的,如果需要按照增加的次序进行排序,这需要写一个自定义的排序规则进行处理,如下所示

    /// <summary>
    ///自定义Bundles排序/// </summary>
    internal classAsIsBundleOrderer : IBundleOrderer
{
public virtual IEnumerable<BundleFile> OrderFiles(BundleContext context, IEnumerable<BundleFile>files)
{
returnfiles;
}
}

然后在调用的时候,修改对象的排序规则即可。

            ScriptBundle jqueryExtend = new ScriptBundle("~/bundles/jquerytools");
jqueryExtend.Orderer
= new AsIsBundleOrderer();

接着在Global.asa.cs里面,增加对Bundle的注册,如下所示。

        protected voidApplication_Start()
{
AreaRegistration.RegisterAllAreas();
BundleConfig.RegisterBundles(BundleTable.Bundles);

WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}

最后在MVC的视图里面,就可以使用Bundle来简化界面代码了。简化后的界面代码如下所示。

<!DOCTYPE html>
<html>
    <head>
    <title>用户管理</title>
    <metaname="viewport"content="width=device-width" />@using System.Web.Optimization;
@Scripts.Render("~/bundles/jquery")
@Styles.Render("~/Content/css")
@Scripts.Render("~/bundles/jquerytools")
@Styles.Render("~/Content/jquerytools")

...............

运行界面,虽然使用了简化版本的代码,依旧正常运行

页面代码输出则还是和原先未优化的一致。

<!DOCTYPE html>
<html>
    <head>
    <title>用户管理</title>
    <metaname="viewport"content="width=device-width" />
    <scriptsrc="/Content/JqueryEasyUI/jquery.min.js"></script>
    <scriptsrc="/Content/JqueryEasyUI/jquery.easyui.min.js"></script>
    <scriptsrc="/Content/JqueryEasyUI/locale/easyui-lang-zh_CN.js"></script>
    <scriptsrc="/Content/datapattern.js"></script>
    <scriptsrc="/Scripts/ComponentUtil.js"></script>

    <linkhref="/Content/JqueryEasyUI/themes/default/easyui.css"rel="stylesheet"/>
    <linkhref="/Content/JqueryEasyUI/themes/icon.css"rel="stylesheet"/>
    <linkhref="/Content/themes/Default/style.css"rel="stylesheet"/>
<linkhref="/Content/themes/Default/default.css"rel="stylesheet"/>

<scriptsrc="/Content/JQueryTools/jQuery.easyui-extend/jquery.easyui.extend.min.js"></script>
    <scriptsrc="/Content/JQueryTools/jNotify/jquery/jNotify.jquery.js"></script>

    <linkhref="/Content/JQueryTools/jQuery.easyui-extend/extend/themes/easyui.extend.css"rel="stylesheet"/>
    <linkhref="/Content/JQueryTools/jQuery.easyui-extend/extend/themes/icon.css"rel="stylesheet"/>
    <linkhref="/Content/JQueryTools/jNotify/jquery/jNotify.jquery.css"rel="stylesheet"/>

基于MVC4+EasyUI的Web开发框架的系列文章:

基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍


基于MVC4+EasyUI的Web开发框架形成之旅--MVC控制器的设计

基于MVC4+EasyUI的Web开发框架形成之旅--界面控件的使用

基于MVC4+EasyUI的Web开发框架形成之旅--附件上传组件uploadify的使用

基于MVC4+EasyUI的Web开发框架形成之旅--框架总体界面介绍

基于MVC4+EasyUI的Web开发框架形成之旅--基类控制器CRUD的操作

基于MVC4+EasyUI的Web开发框架形成之旅--权限控制

基于MVC4+EasyUI的Web开发框架经验总结(1)-利用jQuery Tags Input 插件显示选择记录

基于MVC4+EasyUI的Web开发框架经验总结(2)- 使用EasyUI的树控件构建Web界面

基于MVC4+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据

基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts

基于MVC4+EasyUI的Web开发框架经验总结(5)--使用HTML编辑控件CKEditor和CKFinder

基于MVC4+EasyUI的Web开发框架经验总结(6)--在页面中应用下拉列表的处理

基于MVC4+EasyUI的Web开发框架经验总结(7)--实现省份、城市、行政区三者联动

基于MVC4+EasyUI的Web开发框架经验总结(8)--实现Office文档的预览

基于MVC4+EasyUI的Web开发框架经验总结(9)--在Datagrid里面实现外键字段的转义操作

基于MVC4+EasyUI的Web开发框架经验总结(10)--在Web界面上实现数据的导入和导出

基于MVC4+EasyUI的Web开发框架经验总结(11)--使用Bundles处理简化页面代码

基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

基于MVC4+EasyUI的Web开发框架经验总结(13)--DataGrid控件实现自动适应宽带高度

基于MVC4+EasyUI的Web开发框架经验总结(14)--自动生成图标样式文件和图标的选择操作

在基于MVC4+EasyUI的Web开发框架里面,大量采用了Jquery的方法,对数据进行请求或者提交,方便页面和服务器后端进行数据的交互处理。本文主要介绍利用Jquery处理数据交互的几种方式,包括获取数据并显示,插入新数据到服务器,更新数据,删除数据等操作。

1、利用Jquery获取数据并显示

为了顺利获取数据,我们需要保持页面端调用和服务器端保持一致,并相应的把数据转换或者封装为对象实体进行处理。

下面我们以一个简单的全国省份、全国城市、全国城市行政区的案例进行Demo代码的介绍。


总体的操作包括了,分页查询,添加数据的保存,编辑显示和保存,查看信息的数据显示等等,而利用Jquery获取数据并绑定到界面控件上的代码操作如下所示,主要就是利用getJson方法进行处理。

        //绑定编辑详细信息的方法
function BindEditInfo(ID) {//使用同步方式,使得联动的控件正常显示
            $.ajaxSettings.async = false;//首先用户发送一个异步请求去后台实现方法
            $.getJSON("/Province/FindByID?id=" +ID, function (info) {//赋值有几种方式:.datebox('setValue', info.Birthday);.combobox('setValue', info.Status);.val(info.Name);.combotree('setValue', info.PID);.numberbox('setValue', info.Number);
                $("#ID").val(info.ID);
$(
"#ProvinceName").val(info.ProvinceName);

isAddOrEdit
= 'edit';//新增对话框标识 });
}
//绑定查看详细信息的方法 function BindViewInfo(ID) {//发送请求 $.getJSON("/Province/FindByID?id=" +ID, function (info) {
$(
"#ID2").text(info.ID);
$(
"#ProvinceName2").text(info.ProvinceName);
});
}

getJson方法主要就是调用MVC里面控制器的方法,获取数据,并把它转换为Json的对象实体,这样我们就能方便获取到对应的属性,从而绑定到界面控件。而FindByID的接口是控制器里面的方法定义,我们可以通过下面的控制器基类代码了解具体的逻辑。

        /// <summary>
        ///查询数据库,检查是否存在指定ID的对象/// </summary>
        /// <param name="id">对象的ID值</param>
        /// <returns>存在则返回指定的对象,否则返回Null</returns>
        public virtual ActionResult FindByID(stringid)
{
//检查用户是否有权限,否则抛出MyDenyAccessException异常 base.CheckAuthorized(AuthorizeKey.ViewKey);

ActionResult result
= Content("");
T info
=baseBLL.FindByID(id);if (info != null)
{
result
=ToJsonContentDate(info);
}
returnresult;
}
        /// <summary>
        ///返回处理过的时间的Json字符串/// </summary>
        /// <param name="date"></param>
        /// <returns></returns>
        public ContentResult ToJsonContentDate(objectdate)
{
var timeConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd HH:mm:ss"};returnContent(JsonConvert.SerializeObject(date, Formatting.Indented, timeConverter));
}

2、利用Jquery保存数据到服务器

上面的操作是从服务端获取数据并显示在页面上,下面我们来介绍如何把数据通过通过Jquery调用,保存到服务器上。

在Web框架里面,我们把添加数据和编辑数据的界面,共享了一个层的界面代码,这样可以减少主界面视图Index.cshtml的代码数量(因为我们把各种界面的代码放在一个文件里面,方便操作管理)。

    <!--------------------------添加/修改信息的弹出层---------------------------->
    <divid="DivAdd"class="easyui-dialog"style="width:680px;height:200px;padding:10px 20px"closed="true"resizable="true"modal="true"data-options="iconCls: 'icon-add',buttons: '#dlg-buttons'">
        <formid="ffAdd"method="post"novalidate="novalidate">
            <divid="tabAdd"class="easyui-tabs" >
                <divtitle="基本信息"data-options="iconCls:'icon-view'"style="padding:5px 5px">
                    <table>
                        <tr>
                            <td>                    
                                <tableid="tblAdd1"class="view">
                                    <tr> 
                                        <th>
                                            <labelfor="ProvinceName">省份名称:</label>
                                        </th>
                                        <tdcolspan="3">
                                            <inputclass="easyui-validatebox"type="text"id="ProvinceName"name="ProvinceName"style="width:280px;"  />
                                        </td>
 
                                    </tr>
                                 </table>
                            </td>
                        </tr>
                    </table>
                </div>
            </div>
            <divstyle="text-align:right; padding-top:10px">
                <inputtype="hidden"id="ID"name="ID" />
                <ahref="javascript:void(0)"class="easyui-linkbutton"id="btnAddOK"iconcls="icon-ok"onclick="SaveEntity()">确定</a>
                <ahref="javascript:void(0)"class="easyui-linkbutton"iconcls="icon-cancel"onclick="javascript:$('#DivAdd').dialog('close')">关闭</a>
            </div>
         </form>
    </div>

而数据的保存,我们也用同一个函数,这样也很方便,同时减少代码数量,保存操作的javascript脚本函数如下所示。

        //绑定添加按钮的事件
        functionSaveEntity() {//判断表单的信息是否通过验证
            var validate = $("#ffAdd").form('validate');if (validate == false) {return false;
}
var postData = $("#ffAdd").serializeArray();
$.post(url, postData,
function(json) {var data =$.parseJSON(json);if(data.Success) {//添加成功 1.关闭弹出层,2.刷新DataGird showTips("保存成功");
$(
"#DivAdd").dialog("close");
$(
"#grid").datagrid("reload");
$(
"#ffAdd").form("clear");
}
else{
showError(
"保存失败:" + data.ErrorMessage, 3000);
}
}).error(
function() {
$.messager.alert(
"提示", "您未被授权使用该功能,请联系管理员进行处理。", 'warning');
});
}

由于每个层都定义了Name和ID,因此我们可以很容易通过下面方式获取到对应的对象数据,方便提交。

var postData = $("#ffAdd").serializeArray();

插入数据的时候,调用的路径如下所示:

url = '/Province/Insert';

更新数据的时候,调用的路径如下数艘:

url = '/Province/Update?ID=' + ID;

插入对象到数据库里面的控制器方法,主要还是调用BLL层的对象方法进行处理,不过事先会进行一定的权限控制和信息补充,如下代码所示。

        /// <summary>
        ///插入指定对象到数据库中/// </summary>
        /// <param name="info">指定的对象</param>
        /// <returns>执行操作是否成功。</returns>
        public virtualActionResult Insert(T info)
{
//检查用户是否有权限,否则抛出MyDenyAccessException异常 base.CheckAuthorized(AuthorizeKey.InsertKey);

CommonResult result
= newCommonResult();if (info != null)
{
try{
OnBeforeInsert(info);
result.Success
=baseBLL.Insert(info);
}
catch(Exception ex)
{
LogTextHelper.Error(ex);
//错误记录 result.ErrorMessage =ex.Message;
}
}
returnToJsonContent(result);
}

我们注意到,插入和更新操作,返回的对象都是
CommonResult
对象,这个对象,包含了一个Success的布尔属性,用来表示是否成功,还有一个ErrorMessage属性,用来标识错误信息的,所以我们利用Javascript脚本处理保存操作的时候,也需要使用这两个属性,用来区分和提示信息的显示。

                var data =$.parseJSON(json);if(data.Success) {//添加成功  1.关闭弹出层,2.刷新DataGird
                    showTips("保存成功");
$(
"#DivAdd").dialog("close");
$(
"#grid").datagrid("reload");
$(
"#ffAdd").form("clear");
}
else{
showError(
"保存失败:" + data.ErrorMessage, 3000);
}

操作完成后,提示成功的信息如下所示。

3、利用Jquery删除列表数据操作

利用Jquery函数,可以POST删除的请求到服务器上,在此之前我们需要了解我们需要删除那些记录,并确认提示是否删除,删除成功后,更新列表,并提示用户,大致的页面代码函数如下所示。

        //实现删除数据的方法
        functionDelete() {//得到用户选择的数据的ID
            var rows = $("#grid").datagrid("getSelections");if (rows.length >= 1) {//遍历出用户选择的数据的信息,这就是用户用户选择删除的用户ID的信息
                var ids = "";   //1,2,3,4,5
                for (var i = 0; i < rows.length; i++) {
ids
+= rows[i].ID + ",";
}
//最后去掉最后的那一个, ids = ids.substring(0, ids.length - 1);var postData = { Ids: ids };//然后确认发送异步请求的信息到后台删除数据 $.messager.confirm("删除确认", "您确认删除选定的记录吗?", function(action) {if(action) {
$.ajax({
type:
'POST',
url:
'/District/DeletebyIds',
dataType:
'json',
data: postData,
success:
function(data) {if(data.Success) {
showTips(
"删除选定的记录成功");

$(
"#grid").datagrid("reload");//当删除完成之后,第二次删除的时候还记得上次的信息,这样是不可以的,所以我们需要清除第一次的信息 rows.length = "";//第一种方法 $("#grid").datagrid("clearSelections");//第二种方法 }else{
showError(
"操作失败:" + data.ErrorMessage, 3000);
}
}
});
}
});
}
else{
$.messager.alert(
"提示", "请选择你要删除的数据");
}
}

而服务器的MVC控制器类,我们也只需要调用基类控制器方法就可以了,基本上不需要额外的处理代码。

MVC控制器基类的方法定义如下所示,注意最后返回的是一个常见类CommonResult ,承载这个是否操作成功和错误信息(如果存在的话)。

        /// <summary>
        ///删除多个ID的记录/// </summary>
        /// <param name="ids">多个id组合,逗号分开(1,2,3,4,5)</param>
        /// <returns></returns>
        public virtual ActionResult DeleteByIds(stringids)
{
//检查用户是否有权限,否则抛出MyDenyAccessException异常 base.CheckAuthorized(AuthorizeKey.DeleteKey);

CommonResult result
= newCommonResult();try{if (!string.IsNullOrEmpty(ids))
{
List
<string> idArray = ids.ToDelimitedList<string>(",");foreach (string strId inidArray)
{
if (!string.IsNullOrEmpty(strId))
{
baseBLL.Delete(strId);
}
}
result.Success
= true;
}
}
catch(Exception ex)
{
LogTextHelper.Error(ex);
//错误记录 result.ErrorMessage =ex.Message;
}
returnToJsonContent(result);
}

城市信息界面如下所示。

行政区管理界面如下所示。

基于MVC4+EasyUI的Web开发框架的系列文章:

基于MVC4+EasyUI的Web开发框架形成之旅--总体介绍


基于MVC4+EasyUI的Web开发框架形成之旅--MVC控制器的设计

基于MVC4+EasyUI的Web开发框架形成之旅--界面控件的使用

基于MVC4+EasyUI的Web开发框架形成之旅--附件上传组件uploadify的使用

基于MVC4+EasyUI的Web开发框架形成之旅--框架总体界面介绍

基于MVC4+EasyUI的Web开发框架形成之旅--基类控制器CRUD的操作

基于MVC4+EasyUI的Web开发框架形成之旅--权限控制

基于MVC4+EasyUI的Web开发框架经验总结(1)-利用jQuery Tags Input 插件显示选择记录

基于MVC4+EasyUI的Web开发框架经验总结(2)- 使用EasyUI的树控件构建Web界面

基于MVC4+EasyUI的Web开发框架经验总结(3)- 使用Json实体类构建菜单数据

基于MVC4+EasyUI的Web开发框架经验总结(4)--使用图表控件Highcharts

基于MVC4+EasyUI的Web开发框架经验总结(5)--使用HTML编辑控件CKEditor和CKFinder

基于MVC4+EasyUI的Web开发框架经验总结(6)--在页面中应用下拉列表的处理

基于MVC4+EasyUI的Web开发框架经验总结(7)--实现省份、城市、行政区三者联动

基于MVC4+EasyUI的Web开发框架经验总结(8)--实现Office文档的预览

基于MVC4+EasyUI的Web开发框架经验总结(9)--在Datagrid里面实现外键字段的转义操作

基于MVC4+EasyUI的Web开发框架经验总结(10)--在Web界面上实现数据的导入和导出

基于MVC4+EasyUI的Web开发框架经验总结(11)--使用Bundles处理简化页面代码

基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

基于MVC4+EasyUI的Web开发框架经验总结(13)--DataGrid控件实现自动适应宽带高度

基于MVC4+EasyUI的Web开发框架经验总结(14)--自动生成图标样式文件和图标的选择操作