Knockout JS实现任务管理应用程序
1.1.1 摘要
在博文《Ember.js实现单页面应用程序》中,我们介绍了使用Ember JS实现一个单页应用程序 (SPA),这使我想起了几年前写过一个任务管理程序,通过选择日期,然后编辑时间来增加任务信息。
当时,我们是使用ASP.NET和jQuery实现了任务管理程序的,通过ajax调用ASP.NET的Webservice方法来访问数据库。
今天,我们将通过任务管理程序的实现,来介绍使用ASP.NET Web API和Knockout JS的结合使用,想必许多人都有使用过任务管理程序,其中我觉得Google日历是一个不错的任务管理器
图1 Google日历
目录
1.1.2 正文
通过图1Google日历,我们发现它使用一个Date Picker,让用户选择编辑的日期、还有一个24小时的表格,当用户点击表上的一个时间区域就显示一个弹出式窗口,让用户编辑任务的内容,现在大概了解了基本的界面设计了,接下来我们将通过ASP.NET Web API作为服务端,开放API让Knockout JS调用接口获取数据。
创建ASP.NET MVC 项目
首先,我们在VS2012中创建一个ASP.NET MVC 4 Web项目。
然后,我们打开Package Manager Console,添加Package引用,要使用的库如下:
- PM> install-package jQuery
- PM> install-package KnockoutJS
- PM> install-package Microsoft.AspNet.Web.Optimization
- PM> update-package Micrsoft.AspNet.WebApi
- PM> install-package EntityFramework
图2 ASP.NET MVC 4 Web Application
创建数据表
接着,我们在数据库中添加表TaskDays和TaskDetails,TaskDays保存所有任务的日期,那么一个日期只有一行记录保存该表中,它包含了Id(自增)和Day字段,TaskDetails保存不同时间短任务信息,它包含Id(自增)、Title、Details、Starts、Ends和ParentTaskId等字段,其中ParentTaskId保存任务的日期的Id值。
图3 表TaskDays和TaskDetails
数据传输对象
前面,我们已经定义了数据表TaskDays和TaskDetails并且通过ParentTaskId建立了表之间的关系,接下来,我们将根表定义数据传输对象,具体定义如下:
/// <summary>
/// Defines a DTO TaskCalendar.
/// </summary>
public class TaskDay
{
public TaskDay()
{
Tasks = new List<TaskDetail>();
}
public int Id { get; set; }
public DateTime Day { get; set; }
public List<TaskDetail> Tasks { get; set; }
} /// <summary>
/// Defines a DTO TaskDetail.
/// </summary>
public class TaskDetail
{
public int Id { get; set; }
public string Title { get; set; }
public string Details { get; set; }
public DateTime Starts { get; set; }
public DateTime Ends { get; set; } [ForeignKey("ParentTaskId")]
[ScriptIgnore]
public TaskDay ParentTask { get; set; }
public int ParentTaskId { get; set; }
}
上面,我们定义了数据传输对象TaskDays和TaskDetails,在TaskDays类中,我们定义了一个List<TaskDetail>类型的字段并且在构造函数中实例化该字段,通过保持TaskDetail类型的强对象引用,从而建立起TaskDays和TaskDetails之间的聚合关系,也就是TaskDay和TaskDetails是一对多的关系。
创建控制器
这里我们的ASP.NET MVC程序作为服务端向客户端开放API接口,所以我们创建控制器CalendarController并且提供数据库操作方法,具体实现如下:
/// <summary>
/// The server api controller.
/// </summary>
public class CalendarController : ApiController
{ /// <summary>
/// Gets the task details.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>A list of task detail.</returns>
/// /api/Calendar/GetTaskDetails?id
[HttpGet]
public List<TaskDetail> GetTaskDetails(DateTime id)
{ } /// <summary>
/// Saves the task.
/// </summary>
/// <param name="taskDetail">The task detail.</param>
/// <returns></returns>
/// /api/Calendar/SaveTask?taskDetail
[HttpPost]
public bool SaveTask(TaskDetail taskDetail)
{
} /// <summary>
/// Deletes the task.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
/// /api/Calendar/DeleteTask?id
[HttpDelete]
public bool DeleteTask(int id)
{ }
}
在控制器CalendarController中我们定义了三个方法分别是SaveTask()、DeleteTask()和GetTaskDetails(),想必大家一看都知道这三个方法的作用,没错就是传统的增删查API,但我们这里并没有给出具体数据库操作代码,因为我们将使用Entity Framework替代传统ADO.NET操作。
Entity Framework数据库操作
接下来,我们定义类TaskDayRepository和TaskDetailRepository,它们使用Entity Framework对数据库进行操作,具体定义如下:
/// <summary>
/// Task day repository
/// </summary>
public class TaskDayRepository : ITaskDayRepository
{
readonly TaskCalendarContext _context = new TaskCalendarContext(); /// <summary>
/// Gets all tasks.
/// </summary>
public IQueryable<TaskDay> All
{
get { return _context.TaskDays.Include("Tasks"); }
} /// <summary>
/// Alls the including tasks.
/// </summary>
/// <param name="includeProperties">The include properties.</param>
/// <returns></returns>
public IQueryable<TaskDay> AllIncluding(params Expression<Func<TaskDay, object>>[] includeProperties)
{
IQueryable<TaskDay> query = _context.TaskDays;
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
} /// <summary>
/// Finds the specified identifier.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
public TaskDay Find(int id)
{
return _context.TaskDays.Find(id);
} /// <summary>
/// Inserts the or update.
/// </summary>
/// <param name="taskday">The taskday.</param>
public void InsertOrUpdate(TaskDay taskday)
{
if (taskday.Id == default(int))
{
_context.TaskDays.Add(taskday);
}
else
{
_context.Entry(taskday).State = EntityState.Modified;
}
} /// <summary>
/// Saves this instance.
/// </summary>
public void Save()
{
_context.SaveChanges();
} /// <summary>
/// Deletes the specified identifier.
/// </summary>
/// <param name="id">The identifier.</param>
public void Delete(int id)
{
var taskDay = _context.TaskDays.Find(id);
_context.TaskDays.Remove(taskDay);
} public void Dispose()
{
_context.Dispose();
}
} public interface ITaskDayRepository : IDisposable
{
IQueryable<TaskDay> All { get; }
IQueryable<TaskDay> AllIncluding(params Expression<Func<TaskDay, object>>[] includeProperties);
TaskDay Find(int id);
void InsertOrUpdate(TaskDay taskday);
void Delete(int id);
void Save();
}
上面,我们定义类TaskDayRepository,它包含具体的数据库操作方法:Save()、Delete()和Find(),TaskDetailRepository的实现和TaskDayRepository基本相似,所以我们很快就可以使用TaskDetailRepository,具体定义如下:
/// <summary>
/// Task detail repository
/// </summary>
public class TaskDetailRepository : ITaskDetailRepository
{
readonly TaskCalendarContext _context = new TaskCalendarContext(); /// <summary>
/// Gets all.
/// </summary>
public IQueryable<TaskDetail> All
{
get { return _context.TaskDetails; }
} /// <summary>
/// Alls the including task details.
/// </summary>
/// <param name="includeProperties">The include properties.</param>
/// <returns></returns>
public IQueryable<TaskDetail> AllIncluding(params Expression<Func<TaskDetail, object>>[] includeProperties)
{
IQueryable<TaskDetail> query = _context.TaskDetails;
foreach (var includeProperty in includeProperties)
{
query = query.Include(includeProperty);
}
return query;
} /// <summary>
/// Finds the specified identifier.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
public TaskDetail Find(int id)
{
return _context.TaskDetails.Find(id);
} /// <summary>
/// Saves this instance.
/// </summary>
public void Save()
{
_context.SaveChanges();
} /// <summary>
/// Inserts the or update.
/// </summary>
/// <param name="taskdetail">The taskdetail.</param>
public void InsertOrUpdate(TaskDetail taskdetail)
{
if (default(int) == taskdetail.Id)
{
_context.TaskDetails.Add(taskdetail);
}
else
{
_context.Entry(taskdetail).State = EntityState.Modified;
}
} /// <summary>
/// Deletes the specified identifier.
/// </summary>
/// <param name="id">The identifier.</param>
public void Delete(int id)
{
var taskDetail = _context.TaskDetails.Find(id);
_context.TaskDetails.Remove(taskDetail);
} public void Dispose()
{
_context.Dispose();
}
} public interface ITaskDetailRepository : IDisposable
{
IQueryable<TaskDetail> All { get; }
IQueryable<TaskDetail> AllIncluding(params Expression<Func<TaskDetail, object>>[] includeProperties);
TaskDetail Find(int id);
void InsertOrUpdate(TaskDetail taskdetail);
void Delete(int id);
void Save();
}
上面我们通过Entity Framework实现了数据的操作,接下来,让我们控制器CalendarController的API的方法吧!
/// <summary>
/// The server api controller.
/// </summary>
public class CalendarController : ApiController
{
readonly ITaskDayRepository _taskDayRepository = new TaskDayRepository();
readonly TaskDetailRepository _taskDetailRepository = new TaskDetailRepository(); /// <summary>
/// Gets the task details.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>A list of task detail.</returns>
/// /api/Calendar/GetTaskDetails?id
[HttpGet]
public List<TaskDetail> GetTaskDetails(DateTime id)
{
var taskDay = _taskDayRepository.All.FirstOrDefault<TaskDay>(_ => _.Day == id);
return taskDay != null ? taskDay.Tasks : new List<TaskDetail>();
} /// <summary>
/// Saves the task.
/// </summary>
/// <param name="taskDetail">The task detail.</param>
/// <returns></returns>
/// /api/Calendar/SaveTask?taskDetail
[HttpPost]
public bool SaveTask(TaskDetail taskDetail)
{
var targetDay = new DateTime(
taskDetail.Starts.Year,
taskDetail.Starts.Month,
taskDetail.Starts.Day); // Check new task or not.
var day = _taskDayRepository.All.FirstOrDefault<TaskDay>(_ => _.Day == targetDay); if (null == day)
{
day = new TaskDay
{
Day = targetDay,
Tasks = new List<TaskDetail>()
};
_taskDayRepository.InsertOrUpdate(day);
_taskDayRepository.Save();
taskDetail.ParentTaskId = day.Id;
}
else
{
taskDetail.ParentTaskId = day.Id;
taskDetail.ParentTask = null;
}
_taskDetailRepository.InsertOrUpdate(taskDetail);
_taskDetailRepository.Save();
return true;
} /// <summary>
/// Deletes the task.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns></returns>
/// /api/Calendar/DeleteTask?id
[HttpDelete]
public bool DeleteTask(int id)
{
try
{
_taskDetailRepository.Delete(id);
_taskDetailRepository.Save();
return true;
}
catch (Exception)
{
return false;
} }
}
Knockout JS
上面,我们通过APS.NET MVC实现了服务端,接下来,我们通过Knockout JS实现客户端访问服务端,首先,我们在Script文件中创建day-calendar.js和day-calendar.knockout.bindinghandlers.js文件。
// The viem model type.
var ViewModel = function () {
var $this = this,
d = new Date(); // Defines observable object, when the selectedDate value changed, will
// change data bind in the view.
$this.selectedDate = ko.observable(new Date(d.getFullYear(), d.getMonth(), d.getDate()));
$this.selectedTaskDetails = ko.observable(new TaskDetails(d)); // A serial of observable object observableArray.
$this.dateDetails = ko.observableArray();
$this.appointments = ko.observableArray(); // Init date details list.
$this.initializeDateDetails = function () {
$this.dateDetails.removeAll();
for (var i = 0; i < 24; i++) {
var dt = $this.selectedDate();
$this.dateDetails.push({
count: i,
TaskDetails: new GetTaskHolder(i, dt)
});
}
}; // Call api to get task details.
$this.getTaskDetails = function (date) {
var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate()),
uri = "/api/Calendar/GetTaskDetails"; // Deprecation Notice: The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks are deprecated as of jQuery 1.8.
// To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
// Reference: https://api.jquery.com/jQuery.ajax/
$.get(uri, 'id=' + dt.getFullYear() + '-' + (dt.getMonth() + 1) + '-' + dt.getDate()
).done(function(data) {
$this.appointments.removeAll();
$(data).each(function(i, item) {
$this.appointments.push(new Appointment(item, i));
});
}).error(function(data) {
alert("Failed to retrieve tasks from server.");
});
};
};
上面,我们定义了ViewModel类型,并且在其中定义了一系列的方法。
- selectedDate:获取用户在日历控件中选择的日期。
- selectedTaskDetails:获取用户选择中TaskDetail对象。
- dateDetails:定义监控数组,它保存了24个时间对象。
- appointments:定义监控数组保存每个TaskDetail对象。
接下来,需要获取用户点击日历控件的操作,我们通过方法ko.bindingHandlers()自定义事件处理方法,具体定义如下:
// Binding event handler with date picker.
ko.bindingHandlers.datepicker = {
init: function (element, valueAccessor, allBindingsAccessor) { // initialize datepicker with some optional options
var options = allBindingsAccessor().datepickerOptions || {};
$(element).datepicker(options); // when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeDate", function (event) {
var value = valueAccessor(); // Determine if an object property is ko.observable
if (ko.isObservable(value)) {
value(event.date);
}
});
},
update: function (element, valueAccessor) {
var widget = $(element).data("datepicker");
//when the view model is updated, update the widget
if (widget) {
widget.date = ko.utils.unwrapObservable(valueAccessor());
widget.setValue();
}
} };
上面,我们定义了日历控件的事件处理方法,当用户选择日历中的日期时,我们获取当前选择的日期绑定到界面上,具体定义如下:
<!-- Selected time control -->
<input id="selectStartDate" data-bind="datepicker: Starts" type="text" class="span12" />
上面,我们在Html元素中绑定了datepicker事件处理方法并且把Starts值显示到input元素中。
图4 日历控件
接下来,我们定义Time picker事件处理方法,当用户时间时获取当前选择的时间绑定到界面上,具体定义如下:
// Binding event handler with time picker.
ko.bindingHandlers.timepicker = {
init: function (element, valueAccessor, allBindingsAccessor) {
//initialize timepicker
var options = $(element).timepicker(); //when a user changes the date, update the view model
ko.utils.registerEventHandler(element, "changeTime.timepicker", function (event) {
var value = valueAccessor();
if (ko.isObservable(value)) {
value(event.time.value);
}
});
},
update: function (element, valueAccessor) {
var widget = $(element).data("timepicker");
//when the view model is updated, update the widget
if (widget) {
var time = ko.utils.unwrapObservable(valueAccessor());
widget.setTime(time);
}
}
};
同样,我们把时间值绑定页面元素中,具体定义如下:
<!-- Time picker value-->
<input id="selectStartTime" data-bind="timepicker: StartTime" class="span8" type="text" />
现在,我们已经实现获取用户的输入,接下来需要把用户输入的任务信息数据保存到数据库中,那么我们将通过$.ajax()方法调用API接口,首先我们在day-calendar.js文件中定义类型TaskDetails,具体定义如下:
// TaskDetails type.
var TaskDetails = function (date) {
var $this = this;
$this.Id = ko.observable();
$this.ParentTask = ko.observable();
$this.Title = ko.observable("New Task");
$this.Details = ko.observable();
$this.Starts = ko.observable(new Date(new Date(date).setMinutes(0)));
$this.Ends = ko.observable(new Date(new Date(date).setMinutes(59))); // Gets start time when starts changed.
$this.StartTime = ko.computed({
read: function () {
return $this.Starts().toLocaleTimeString("en-US");
},
write: function (value) {
if (value) {
var dt = new Date($this.Starts().toDateString() + " " + value);
$this.Starts(new Date($this.Starts().getFullYear(), $this.Starts().getMonth(), $this.Starts().getDate(), dt.getHours(), dt.getMinutes()));
}
}
}); // Gets end time when ends changed.
$this.EndTime = ko.computed({
read: function () {
return $this.Ends().toLocaleTimeString("en-US");
},
write: function (value) {
if (value) {
var dt = new Date($this.Ends().toDateString() + " " + value);
$this.Ends(new Date($this.Ends().getFullYear(), $this.Ends().getMonth(), $this.Ends().getDate(), dt.getHours(), dt.getMinutes()));
}
}
}); $this.btnVisibility = ko.computed(function () {
if ($this.Id() > 0) {
return "visible";
}
else {
return "hidden";
}
}); $this.Save = function (data) { // http://knockoutjs.com/documentation/plugins-mapping.html
var taskDetails = ko.mapping.toJS(data);
taskDetails.Starts = taskDetails.Starts.toDateString();
taskDetails.Ends = taskDetails.Ends.toDateString();
$.ajax({
url: "/api/Calendar/SaveTask",
type: "POST",
contentType: "text/json",
data: JSON.stringify(taskDetails) }).done(function () {
$("#currentTaskModal").modal("toggle");
vm.getTaskDetails(vm.selectedDate());
}).error(function () {
alert("Failed to Save Task");
});
}; $this.Delete = function (data) {
$.ajax({
url: "/api/Calendar/" + data.Id(),
type: "DELETE", }).done(function () {
$("#currentTaskModal").modal("toggle");
vm.getTaskDetails(vm.selectedDate());
}).error(function () {
alert("Failed to Delete Task");
});
}; $this.Cancel = function (data) {
$("#currentTaskModal").modal("toggle");
};
};
我们在TaskDetails类型中定义方法Save()和Delete(),我们看到Save()方法通过$.ajax()调用接口“/api/Calendar/SaveTask” 保存数据,这里要注意的是我们把TaskDetails对象序列化成JSON格式数据,然后调用SaveTask()保存数据。
现在,我们已经实现了页面绑定用户的输入,然后由Knockout JS访问Web API接口,对数据库进行操作;接着需要对程序界面进行调整,我们在项目中添加bootstrap-responsive.css和bootstrap.css文件引用,接下来,我们在的BundleConfig中指定需要加载的Javascript和CSS文件,具体定义如下:
/// <summary>
/// Compress JS and CSS file.
/// </summary>
public class BundleConfig
{
/// <summary>
/// Registers the bundles.
/// </summary>
/// <param name="bundles">The bundles.</param>
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js")); bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js",
"~/Scripts/html5shiv.js")); bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*")); bundles.Add(new ScriptBundle("~/bundles/knockout").Include(
"~/Scripts/knockout-{version}.js")); bundles.Add(new StyleBundle("~/Styles/bootstrap/css").Include(
"~/Content/bootstrap-responsive.css",
"~/Content/bootstrap.css")); bundles.Add(new ScriptBundle("~/bundles/jquerydate").Include(
"~/Scripts/datepicker/bootstrap-datepicker.js",
//"~/Scripts/datepicker/locales/bootstrap-datepicker.zh-CN.js",
"~/Scripts/timepicker/bootstrap-timepicker.min.js",
"~/Scripts/moment.js")); bundles.Add(new ScriptBundle("~/bundles/app").Include(
"~/Scripts/app/day-calendar*")); bundles.Add(new StyleBundle("~/Styles/jquerydate").Include(
"~/Content/datepicker/datepicker.css",
"~/Content/timepicker/bootstrap-timepicker.css"));
}
}
图5 任务管理器Demo
1.1.3 总结
我们通过一个任务管理程序的实现介绍了Knockout JS和Web API的结合使用,服务端我们通过Entity Framework对数据进行操作,然后使用API控制器开放接口让客户端访问,然后我们使用Knockout JS的数据绑定和事件处理方法实现了动态的页面显示,最后,我们使用了BootStrap CSS美化了一下程序界面。
本文仅仅是介绍了Knockout JS和Web API的结合使用,如果大家想进一步学习,参考如下链接。
参考
- https://github.com/dotnetcurry/ko-calendar-dncmag-08
- http://blogs.msdn.com/b/webdev/archive/2013/11/18/announcing-release-of-asp-net-and-web-tools-2013-1-for-visual-studio-2012.aspx
- http://code.tutsplus.com/tutorials/into-the-ring-with-knockout-js--net-21239
- http://code.tutsplus.com/series/into-the-ring-with-knockoutjs--net-22038
- http://www.asp.net/web-api/overview/creating-web-apis/using-web-api-with-entity-framework/using-web-api-with-entity-framework,-part-5
Knockout JS实现任务管理应用程序的更多相关文章
- MVC、MVP、MVVM、Angular.js、Knockout.js、Backbone.js、React.js、Ember.js、Avalon.js、Vue.js 概念摘录
注:文章内容都是摘录性文字,自己阅读的一些笔记,方便日后查看. MVC MVC(Model-View-Controller),M 是指业务模型,V 是指用户界面,C 则是控制器,使用 MVC 的目的是 ...
- Knockout.js随手记(2)
计算属性 konckout.js的API文档,写的极为详细和生动,透过MVVM的运作原理,开发时只需专注于定义ViewModel逻辑,不需耗费心力处理TextBox.Select的onchange.o ...
- Knockout.js随手记(1)
新的开始,knockout.js 1.首先去http://knockoutjs.com/index.html下载knockout.js,最新的版本是2.3 2.知道什么是Knockout?它是个Jav ...
- Knockout.Js案例一Introduction
</strong></p> <p>Last name: <strong data-bind="text:lastName ">tod ...
- Knockout.js是什么?
从本节开始介绍关于KnockoutJs相关的内容,本节主要介绍knockoutjs一些重要特性与优点,以及它与Jquery等框架库之间的区别. 1.Knockout.js是什么? Knockout是一 ...
- 一个简单的 ASP.NET MVC 例子演示如何在 Knockout JS 的配合下,使用 TypeScript 。
前言 TypeScript 是一种由微软开发的自由和开源的编程语言.它是JavaScript的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程.安德斯·海尔斯伯格,C#的首席架 ...
- knockout.js的简介和简单使用
1.knockout简介knockout是一个轻量级的UI类库,通过MVVM模式使JavaScript前端UI简单化knockout有四大重要概念:1)声明式绑定:使用简明移读的语法很容易地将模型(m ...
- Knockout.Js官网学习(简介)
前言 最近一段时间在网上经常看到关于Knockout.js文章,于是自己就到官网看了下,不过是英文的,自己果断搞不来,借用google翻译了一下.然后刚刚发现在建立asp.net mvc4.0的应用程 ...
- 1.Knockout.Js(简介)
前言 最近一段时间在网上经常看到关于Knockout.js文章,于是自己就到官网看了下,不过是英文的,自己果断搞不来,借用google翻译了一下.然后刚刚发现在建立asp.net mvc4.0的应用程 ...
随机推荐
- 关于SharePoint 2010中不能使用AjaxControlToolkit的解决办法
因为项目中有一个需求需要使用calendar控件,而且样式要和Reporting Service中的尽量一致,搜索了很久发现还是微软的AjaxControlToolkit提供的CalendarExte ...
- HttpWebResponse远程服务器返回错误: (500) 内部服务器错误。
现象 我们编码实现请求一个页面时,请求的代码类似如下代码: HttpWebRequest req = (HttpWebRequest)WebRequest.Create(strUrl); req.Us ...
- XML增、删、改
今天有个需求需要操作xml节点.突然见遗忘了许多.上网看了些资料.才整出来.脑袋真不够用.在这里把我找到的资料共享一下.方便以后使用.本文属于网摘/ 一.简单介绍 using System.Xml; ...
- 【洛谷P2889】Milking Time
很容易想到以结束时间加上R从小到大排序 之后怎样呢? 我们按层考虑,f[i]表示前i个时间段嫩得到的最大价值 每次枚举其之前的状态,如果其ed<当前i的st,那么取max即可 #include& ...
- BeanUtils: 威力和代价(转载)
转自:http://blog.sina.com.cn/s/blog_ab3fbf1b0101jbxz.html Apache Jakarta Commons项目非常有用.我曾在许多不同的项目上或直接或 ...
- STM32正交编码器驱动电机
1.编码器原理 什么是正交?如果两个信号相位相差90度,则这两个信号称为正交.由于两个信号相差90度,因此可以根据两个信号哪个先哪个后来判断方向. 这里使用了TI12模式,例如当T1上升 ...
- 关于display: box 和 box-flex
这两天做手机项目,使用到这个css3新属性.现在还不为所有浏览器支持,所以使用的时候要加上前缀.使用方法见下面: html代码: <div class="s-indLine" ...
- C#自旋的艺术
CODE1: while (status == ServerStatus.Started) { if (*TaskLocker > 0) { for (int i = 0; i != *Leve ...
- 位图图像处理控件ImageCapture Suite更新至v9.1
概述:Dynamsoft公司旗下非常出名的位图图像处理控件ImageCapture Suite更新至了v9.1,这次新版本为Mac版本和IE 9新增了不少功能,同时还对其他组件的性能进行了质的提高! ...
- windows 7下qtcreator里QWT文件的pro配置
http://blog.chinaunix.net/uid-20717410-id-272331.html 把编译好的qwt的include文件夹下面 所有的.h文件 复制到qt目录下 然后在pro里 ...