在 ASP.NET MVC 中充分利用 WebGrid
https://msdn.microsoft.com/zh-cn/magazine/hh288075.aspx
今年早些时候,Microsoft 发布了 ASP.NET MVC 版本 3 (asp.net/mvc) 以及一款名为 WebMatrix 的新产品 (asp.net/webmatrix)。 该 WebMatrix 版本中提供了几个工作效率帮助组件,可以简化诸如图表和表格数据呈现等任务。 其中一个帮助组件是 WebGrid,该组件支持通过 AJAX 自定义列的格式、分页、排序和异步更新,使表格呈现变得非常简单。
本文介绍 WebGrid 及其在 ASP.NET MVC 3 中的使用方式,然后讨论如何在 ASP.NET MVC 解决方案中充分利用 WebGrid 的功能。 有关 WebMatrix 的概述以及本文所用 Razor 语法的相关信息,请参见 Clark Sell 在 2011 年 4 月期刊中的文章“WebMatrix 简介”(msdn.microsoft.com/magazine/gg983489)。
本文介绍如何在 ASP.NET MVC 环境中安装 WebGrid 组件,以提高表格数据的呈现效率。 我将从 ASP.NET MVC 角度重点介绍以下与 WebGrid 有关的功能:创建具有完全 IntelliSense 支持的强类型版 WebGrid;利用 WebGrid 支持实现服务器端分页;以及添加可在禁用脚本编写时从容降级的 AJAX 功能。 本文所用示例以一个现成的服务为基础进行构建,该服务通过实体框架提供对 AdventureWorksLT 数据库的访问。 如果您对数据访问代码感兴趣,可在代码下载部分下载这些代码,也可查阅 Julie Lerman 在 2011 年 3 月期刊中的文章“使用实体框架和 ASP.NET MVC 3 实现服务器端分页”(msdn.microsoft.com/magazine/gg650669)。
WebGrid 入门
为了提供一个简单的 WebGrid 示例,我设置了一个 ASP.NET MVC 操作,它执行向视图传递 Ienumerable<Product> 的简单功能。 本文中我大多使用 Razor 视图引擎,但后面我也会讨论如何使用 WebForms 视图引擎。 我的 ProductController 类有如下操作:
public ActionResult List()
{
IEnumerable<Product> model =
_productService.GetProducts(); return View(model);
}
List 视图中包含如下 Razor 代码,用于呈现图 1 所示的网格:
@model IEnumerable<MsdnMvcWebGrid.Domain.Product>
@{
ViewBag.Title = "Basic Web Grid";
}
<h2>Basic Web Grid</h2>
<div>
@{
var grid = new WebGrid(Model, defaultSort:"Name");
}
@grid.GetHtml()
</div>
图 1 呈现的基本 Web 网格
该视图中的第一行指定型号(例如我们在视图中访问的 Model 属性的类型)为 IEnumerable<Product>。然后,我在 div 元素内通过传入型号数据实例化一个 WebGrid,我将代码放入 @{...} 代码块中是要告诉 Razor 不要试图呈现结果。 我还在构造函数中将 defaultSort 参数设置为“Name”,告知 WebGrid 传给它的数据已按 Name 排序。 最后,我用 @grid.GetHtml() 生成网格的 HTML 并在响应中呈现网格。
这段代码虽然不多,却提供了丰富的网格功能。 该网格限制了显示的数据量,并包含翻阅数据所需的分页器链接,而且列标题呈现为链接以支持分页。 如果需要自定义该行为,可在 WebGrid 构造函数和 GetHtml 方法中指定一些选项。 通过这些选项可以禁用分页和排序、更改每页显示的行数、更改分页器链接中的文本等等。 图 2 显示了 WebGrid 构造函数参数,图 3 显示了 GetHtml 参数。
图 2 WebGrid 构造函数参数
名称 | 类型 | 备注 |
source | IEnumerable<dynamic> | 要呈现的数据。 |
columnNames | IEnumerable<string> | 筛选呈现的列。 |
defaultSort | string | 指定作为排序依据的默认列。 |
rowsPerPage | int | 控制每页显示的行数(默认值为 10)。 |
canPage | bool | 启用或禁用数据分页。 |
canSort | bool | 启用或禁用数据排序。 |
ajaxUpdateContainerId | string | 网格中包含元素的 ID,用来启用 AJAX 支持。 |
ajaxUpdateCallback | string | 完成 AJAX 更新后调用的客户端函数。 |
fieldNamePrefix | string | 支持多个网格时查询字符串字段使用的前缀。 |
pageFieldName | string | 页码的查询字符串字段名称。 |
selectionFieldName | string | 所选行号的查询字符串字段名称。 |
sortFieldName | string | 排序列的查询字符串字段名称。 |
sortDirectionFieldName | string | 排序方向的查询字符串字段名称。 |
图 3 WebGrid.GetHtml 参数
名称 | 类型 | 备注 |
tableStyle | string | 样式使用的表类。 |
headerStyle | string | 样式使用的标题行类。 |
footerStyle | string | 样式使用的页脚行类。 |
rowStyle | string | 样式使用的行类(仅限奇数行)。 |
alternatingRowStyle | string | 样式使用的行类(仅限偶数行)。 |
selectedRowStyle | string | 所选的样式行类。 |
caption | string | 显示为表标题的字符串。 |
displayHeader | bool | 指示是否应显示标题行。 |
fillEmptyRows | bool | 指示表中是否可以通过添加空行来保证 rowsPerPage 的行数。 |
emptyRowCellValue | string | 空行内填充的值,仅在设置了 fillEmptyRows 时使用。 |
columns | IEnumerable<WebGridColumn> | 用于自定义列呈现的列模型。 |
exclusions | IEnumerable<string> | 自动填充列时要排除的列。 |
mode | WebGridPagerModes | 分页器呈现模式(默认值为 NextPrevious 和 Numeric)。 |
firstText | string | 第一页链接的文本。 |
previousText | string | 上一页链接的文本。 |
nextText | string | 下一页链接的文本。 |
lastText | string | 最后一页链接的文本。 |
numericLinksCount | int | 要显示的数字链接的数量(默认值为 5)。 |
htmlAttributes | object | 包含为元素设置的 HTML 属性。 |
前一段 Razor 代码将呈现每一行的所有属性,但您也可能希望对显示哪些列作出限制。 有多种方法可以实现这一目的。 第一种方法(也是最简单的方法)是将这一组列传递到 WebGrid 构造函数。 例如,以下代码只呈现 Name 和 ListPrice 属性:
var grid = new WebGrid(Model, columnNames: new[] {"Name", "ListPrice"});
也可在 GetHtml 调用而不是在构造函数中指定这些列。 这种方法虽然要编写稍多的代码,但好处是可以指定更多关于如何呈现列的信息。 在下面的示例中,我指定了 header 属性,以使 ListPrice 列更便于阅读:
@grid.GetHtml(columns: grid.Columns(
grid.Column("Name"),
grid.Column("ListPrice", header:"List Price")
)
)
在呈现一组项目时,我们通常希望让用户通过点击一个项目来导航到详细信息视图。 通过 Column 方法的 format 参数可以自定义数据项的呈现。 以下代码演示如何更改名称的呈现方式,以输出指向某个项目详细信息视图的链接。这段代码输出带两位小数的“List Price”(货币值惯用的小数位数),得到的输出如图 4 所示。
@grid.GetHtml(columns: grid.Columns(
grid.Column("Name", format: @<text>@Html.ActionLink((string)item.Name,
"Details", "Product", new {id=item.ProductId}, null)</text>),
grid.Column("ListPrice", header:"List Price",
format: @<text>@item.ListPrice.ToString("0.00")</text>)
)
)
图 4 采用自定义列的基本网格
虽然我指定格式时发生的情况看似有些神秘,但 format 参数实际就是一个 Func<dynamic,object>,即一个利用动态参数返回对象的委托函数。 Razor 引擎采用为 format 参数指定的代码段,并将其转变为一个委托。该委托采用一个名为 item 的动态参数,format 代码段中正是使用了这个 item 变量。 有关这些委托的工作方式的更多信息,请参见 Phil Haack 在以下地址发表的博客文章:bit.ly/h0Q0Oz。
由于 item 参数属于动态类型,所以在编写代码时无法获得 IntelliSense 支持和编译器检查(请参见 Alexandra Rusina 在 2011 年 2 月期刊中发表的关于动态类型的文章msdn.microsoft.com/magazine/gg598922)。 而且,也不支持用动态参数调用扩展方法。 也就是说,当调用扩展方法时,一定要使用静态类型。正因为如此,我在前面的代码中调用 Html.ActionLink 扩展方法时,item.Name 转换成了 string。 由于 ASP.NET MVC 中对扩展方法的使用较为普遍,动态和扩展方法之间的这种冲突可能会让人疲于应付(在使用 T4MVC 等其他组件时情况甚至更糟:bit.ly/9GMoup)。
添加强类型化
虽然动态类型化可能很适合 WebMatrix,但强类型化视图也有其优点。 实现强类型化的一种办法是创建一个派生类型 WebGrid<T>,如图 5 所示。 如您所见,这是个非常轻型的包装!
图 5 创建派生 WebGrid
public class WebGrid<T> : WebGrid
{
public WebGrid(
IEnumerable<T> source = null,
...
parameter list omitted for brevity)
: base(
source.SafeCast<object>(),
...
parameter list omitted for brevity)
{ }
public WebGridColumn Column(
string columnName = null,
string header = null,
Func<T, object> format = null,
string style = null,
bool canSort = true)
{
Func<dynamic, object> wrappedFormat = null;
if (format != null)
{
wrappedFormat = o => format((T)o.Value);
}
WebGridColumn column = base.Column(
columnName, header,
wrappedFormat, style, canSort);
return column;
}
public WebGrid<T> Bind(
IEnumerable<T> source,
IEnumerable<string> columnNames = null,
bool autoSortAndPage = true,
int rowCount = -1)
{
base.Bind(
source.SafeCast<object>(),
columnNames,
autoSortAndPage,
rowCount);
return this;
}
} public static class WebGridExtensions
{
public static WebGrid<T> Grid<T>(
this HtmlHelper htmlHelper,
...
parameter list omitted for brevity)
{
return new WebGrid<T>(
source,
...
parameter list omitted for brevity);
}
}
这样做有什么好处呢? 通过实现这个新的 WebGrid<T>,我添加了一个新的 Column 方法,该方法以 Func<T, object> 作为 format 参数,这意味着在调用扩展方法时不必再进行转换。 不仅如此,现在还能够获得 IntelliSense 支持和编译器检查(假定项目文件中已经打开 MvcBuildViews,它默认处于关闭状态)。
通过这种 Grid 扩展方法,您能够利用编译器针对范型参数的类型推断功能。 因此,本例中我们只需要编写 Html.Grid(Model),而不必编写新的 WebGrid<Product>(Model)。 无论采用哪种方式,返回的类型都是 WebGrid<Product>。
添加分页和排序
如您所见,WebGrid 能让我们毫不费力的获得分页和排序功能。 您还了解到如何通过 rowsPerPage 参数(位于构造函数中,或通过 Html.Grid 帮助程序实现)配置页面大小,使网格自动显示单页数据并呈现页面导航所使用的分页控件。 但是,这种默认行为可能满足不了您的需求。 为了说明这一点,我添加了一行代码,用于在呈现网格后显示数据源中包含的项数,如图 6 所示。
图 6 数据源中的项数
可以看到,我们传递的数据中包含完整的产品列表(本例中为 295 个产品,但检索更多数据的情形想来并不少见)。 随着返回数据量的增加,虽然依旧是呈现单页数据,但服务和数据库所承受的负荷会越来越大。 但是有一种更好的办法:服务器端分页。 采用这种方式,只需要取回需要在当前页面中显示的数据(例如只显示五行数据)。
实现 WebGrid 服务器端分页的第一步是限制从数据源检索的数据量。 为此,需要知道请求的是哪一页数据,以便检索正确的数据页。 WebGrid 在呈现分页链接时,会重复使用页面的 URL,并在页码中附加一个查询字符串参数,例如 http://localhost:27617/Product/DefaultPagingAndSorting?page=3(该查询字符串参数的名称可通过帮助程序参数进行配置,这在支持同一页面中多个网格的分页时非常有用)。 也就是说,您可以在自己的操作方法中采用一个名为 page 的参数,然后使用查询字符串值填充该参数。
如果只是通过修改现有代码向 WebGrid 传递单页数据,则 WebGrid 只会看到单页数据。 由于它不知道还有别的页面,因而不再呈现分页器控件。 幸运的是,WebGrid 还有一种名为 Bind 的方法,可用来指定数据。Bind 不仅能够接受数据,而且有一个表示总行数的参数,从而据此计算页数。 为了使用此方法,需要更新 List 操作以检索更多信息并将其传入视图,如图 7 所示。
图 7 更新 List 操作
public ActionResult List(int page = 1)
{
const int pageSize = 5; int totalRecords;
IEnumerable<Product> products = productService.GetProducts(
out totalRecords, pageSize:pageSize, pageIndex:page-1); PagedProductsModel model = new PagedProductsModel
{
PageSize= pageSize,
PageNumber = page,
Products = products,
TotalRows = totalRecords
};
return View(model);
}
利用这些附加信息,即可更新视图以使用 WebGrid 的 Bind 方法。 通过调用 Bind 可提供要呈现的数据和总行数,并将 autoSortAndPage 参数设置为 false。 autoSortAndPage 参数告知 WebGrid 不需要应用分页,因为这由 List 方法负责。 对此可用下面代码说明:
<div>
@{
var grid = new WebGrid<Product>(null, rowsPerPage: Model.PageSize,
defaultSort:"Name");
grid.Bind(Model.Products, rowCount: Model.TotalRows, autoSortAndPage: false);
}
@grid.GetHtml(columns: grid.Columns(
grid.Column("Name", format: @<text>@Html.ActionLink(item.Name,
"Details", "Product", new { id = item.ProductId }, null)</text>),
grid.Column("ListPrice", header: "List Price",
format: @<text>@item.ListPrice.ToString("0.00")</text>)
)
) </div>
经过如此改造,WebGrid 又恢复了生机,重新呈现分页控件,但分页发生在服务中而不是视图中! 但是,由于关闭了 autoSortAndPage,排序功能遭到破坏。 WebGrid 利用查询字符串参数来传递排序列和方向,但我们已命令它不执行排序。 解决办法是在操作方法中添加 sort 和 sortDir 参数,然后将它们传入服务,让服务执行必要的排序,如图 8 所示。
图 8 在操作方法中添加排序参数
public ActionResult List(
int page = 1,
string sort = "Name",
string sortDir = "Ascending" )
{
const int pageSize = 5; int totalRecords;
IEnumerable<Product> products =
_productService.GetProducts(out totalRecords,
pageSize: pageSize,
pageIndex: page - 1,
sort:sort,
sortOrder:GetSortDirection(sortDir)
); PagedProductsModel model = new PagedProductsModel
{
PageSize = pageSize,
PageNumber = page,
Products = products,
TotalRows = totalRecords
};
return View(model);
}
AJAX:客户端改动
WebGrid 支持通过 AJAX 异步更新网格内容。 为了利用此功能,应确保包含网格的 div 有一个 id,然后通过 ajaxUpdateContainerId 参数将该 id 传入网格的构造函数。 还需要对 jQuery 的引用,但这已经包括在布局视图中。 指定 ajaxUpdateContainerId 以后,WebGrid 会修改自己的行为,使分页和排序链接能够利用 AJAX 进行更新:
<div id="grid"> @{
var grid = new WebGrid<Product>(null, rowsPerPage: Model.PageSize,
defaultSort: "Name", ajaxUpdateContainerId: "grid");
grid.Bind(Model.Products, autoSortAndPage: false, rowCount: Model.TotalRows);
}
@grid.GetHtml(columns: grid.Columns(
grid.Column("Name", format: @<text>@Html.ActionLink(item.Name,
"Details", "Product", new { id = item.ProductId }, null)</text>),
grid.Column("ListPrice", header: "List Price",
format: @<text>@item.ListPrice.ToString("0.00")</text>)
)
) </div>
尽管内置的使用 AJAX 的功能很不错,但如果脚本编写被禁用,生成的输出将不起作用。 其原因在于,在 AJAX 模式下,WebGrid 在呈现定位标记时将 href 设置为“#”,并通过 onclick 处理程序注入 AJAX 行为。
我一直热衷于创建能在禁用脚本编写时从容降级的页面,最后往往发现做到这一点最好的办法是渐进式增强(基本原理是提供一个无需脚本即可正常工作的页面,然后通过脚本对该页面加以丰富)。 为达到此目的,可恢复为非 AJAX 的 WebGrid,然后创建图 9 所示的脚本以重新应用 AJAX 行为:
图 9 重新应用 AJAX 行为
$(document).ready(function () { function updateGrid(e) {
e.preventDefault();
var url = $(this).attr('href');
var grid = $(this).parents('.ajaxGrid');
var id = grid.attr('id');
grid.load(url + ' #' + id);
};
$('.ajaxGrid table thead tr a').live('click', updateGrid);
$('.ajaxGrid table tfoot tr a').live('click', updateGrid);
});
为使脚本只应用到一个 WebGrid 中,它利用 jQuery 选择器标识出设置了 ajaxGrid 类的元素。 脚本通过 jQuery live 方法 (api.jquery.com/live) 建立排序和分页链接的 click 处理程序(通过网格容器内的表标题和页脚进行标识)。 这将为符合选择器要求的现有和未来元素设置事件处理程序,由于脚本将取代内容,因此这样做非常方便。
updateGrid 方法被设置为事件处理程序,它首先要做的是调用 preventDefault 以抑制默认行为。 在此之后,该方法获取要使用的 URL(通过定位标记的 href 属性获取),然后通过调用 AJAX 将更新的内容加载到容器元素之中。 为了采用这种做法,一定要禁用默认的 WebGrid AJAX 行为,将 ajaxGrid 类添加到容器 div,然后加入图 9 所示的脚本。
AJAX:服务器端改动
还有一点需要指出,就是脚本使用 jQuery load 方法中的功能从返回的文档中分离出一个片段。 只需调用 load(‘http://example.com/someurl’) 就能加载 URL 的内容。 但是,load(‘http://example.com/someurl #someId’) 将从指定 URL 加载内容,然后返回 id 为“someId”的片段。这反映了 WebGrid 的默认 AJAX 行为,意味着不必通过更新服务器代码添加部分呈现行为。WebGrid 首先加载整个页面,然后从中剥离出新的网格。
尽管这样在快速获得 AJAX 功能方面非常有效,但也意味着需要通过网络发送不必要的数据,而且可能在服务器中也要查询不必要的数据。 幸运的是,ASP.NET MVC 能够轻松解决这个问题。 基本做法是将要在 AJAX 及非 AJAX 请求*享的呈现内容提取到一个部分视图中。 随后,控制器中的 List 操作既可以为 AJAX 调用仅呈现部分视图,也可以为非 AJAX 调用呈现完整视图(该完整视图又使用该部分视图)。
这种做法非常简单,只需在操作方法内部测试 Request.IsAjaxRequest 扩展方法的结果即可。 当 AJAX 与非 AJAX 代码途径之间的差别非常小时,这种方法十分适用。 然而,两者之间的差别往往比较大(例如,完全呈现需要的数据比部分呈现多)。 在这种情况下,可能需要编写一个 AjaxAttribute,以便单独编写相应的方法,然后让 MVC 框架根据请求是否为 AJAX 请求来选择合适的方法(与 HttpGet 和 HttpPost 属性的工作方式相同)。 关于这方面的例子,请参阅我在bit.ly/eMlIxU 的博客文章。
WebGrid 和 WebForms 视图引擎
到目前为止,所有举例都使用了 Razor 视图引擎。 在最简单的情况下,我们不必执行任何修改即可将 WebGrid 用于 WebForms 视图(暂不论视图引擎的语法差别)。 在前面的示例中,我演示了如何使用 format 参数自定义行数据的呈现:
grid.Column("Name",
format: @<text>@Html.ActionLink((string)item.Name,
"Details", "Product", new { id = item.ProductId }, null)</text>),
format 参数实际上是一个 Func,但 Razor 视图引擎对我们隐藏了这一点。 不过,您还是可以传递 Func,例如用 lambda 表达式:
grid.Column("Name",
format: item => Html.ActionLink((string)item.Name,
"Details", "Product", new { id = item.ProductId }, null)),
借助于这种简单的转换,现在我们可以轻松地在 WebForms 视图引擎中使用 WebGrid!
总结
本文介绍了如何通过几项简单的调整,在不牺牲强类型化、IntelliSense 和高效服务器端分页的情况下利用 WebGrid 为我们提供的功能。 WebGrid 有一些非常棒的功能,可帮助我们提高表格数据的呈现效率。 希望本文能为您在 ASP.NET MVC 应用程序中充分利用 WebGrid 提供有益的提示。
Stuart Leeks 是英国高级开发支持团队的应用程序开发经理,他对于键盘快捷方式有着超乎寻常的热爱。他的博客站点在 blogs.msdn.com/b/stuartleeks,他在那里讨论自己感兴趣的技术主题(包括但不限于 ASP.NET MVC、实体框架和 LINQ)。
衷心感谢以下技术专家对本文的审阅:Simon Ince 和 Carl Nolan
在 ASP.NET MVC 中充分利用 WebGrid (microsoft 官方示例)的更多相关文章
-
ASP.NET MVC中使用Ninject
ASP.NET MVC中使用Ninject 在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事 ...
-
在 ASP.NET MVC 中使用异步控制器
线程池 一直想把项目改写成异步,但是ASP.NETMVC3下写的过于繁琐,.NET 4.5与ASP.NET MVC下代码写起来就比较简单了, MS好像也一直喜欢这样搞,每一个成熟的东西,都要演变好几个 ...
-
在Asp.Net MVC 中配置 Serilog
Serilog 是一种非常简便记录log 的处理方式,使用Serilog可以生成本地的text文件, 也可以通过 Seq 来在Web界面中查看具体的log内容. 接下来就简单的介绍一下在Asp.Net ...
-
如何在 ASP.NET MVC 中集成 AngularJS(3)
今天来为大家介绍如何在 ASP.NET MVC 中集成 AngularJS 的最后一部分内容. 调试路由表 - HTML 缓存清除 就在我以为示例应用程序完成之后,我意识到,我必须提供两个版本的路由表 ...
-
如何在 ASP.NET MVC 中集成 AngularJS(1)
介绍 当涉及到计算机软件的开发时,我想运用所有的最新技术.例如,前端使用最新的 JavaScript 技术,服务器端使用最新的基于 REST 的 Web API 服务.另外,还有最新的数据库技术.最新 ...
-
ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器
ASP.NET MVC 中应用Windows服务以及Webservice服务开发分布式定时器一:闲谈一下:1.现在任务跟踪管理系统已经开发快要结束了,抽一点时间来写一下,想一想自己就有成就感啊!! ...
-
ASP.NET MVC中使用ASP.NET AJAX异步访问WebService
使用过ASP.NET AJAX的朋友都知道,怎么通过ASP.NET AJAX在客户端访问WebService,其实在ASP.NET MVC中使用ASP.NET AJAX异步访问WebService 也 ...
-
NPOI以及在ASP.NET MVC中的使用
NPOI以及在ASP.NET MVC中的使用 1.前言 相信大家在工作中经常要遇到一些导入导出Execl操作.学习贵在分享,分享使人快乐,园子里的前辈已经有很多好的文章,鄙人也是能力有限,在这里把这些 ...
-
Ninject 在 Winform、 Asp.net MVC中连络EntityFramework的应用
Ninject 在 Winform. Asp.net MVC中连络EntityFramework的应用( 注入同一个DbContext的应用名人名言:科学是老老实实的东西,它要靠许许多多人民的劳动和智 ...
随机推荐
-
ELF Format 笔记(十三)—— 段权限
ilocker:关注 Android 安全(新手) QQ: 2597294287 一个可被系统加载的程序至少拥有一个可加载段.当系统创建可加载段的内存映像时,会根据 p_flags 赋予一定的访问权限 ...
-
CentOS 7 配置静态IP
1.查看MAC地址 2.修改/etc/sysconfig/network-scripts/ifcfg-[第一步中红框内的文字] 3.添加和修改内容如下: 4.修改/etc/resolv.conf 5. ...
-
32位计时器极端情况下产生的bug
用每毫秒更新的32位变量用来计时, 使用这个变量计算离上次操作是否间隔10秒.两种写法: f - lastF <10, 和 f
-
Android成长日记-ProgressBar的设计
ProgressBar的关键属性 Android:max=”100” - ---最大显示进度 Android:progress=”50”----第一显示进度 Android:secondaryProg ...
-
c# tcp备忘及networkstream.length此流不支持查找解决
服务端 bool isRunning = true; MouseKeyBoard mk = new MouseKeyBoard(); void InitTcpServer(int port) { T ...
-
非常有用!eclipse与myeclipse恢复已删除的文件和代码
eclipse与myeclipse恢复已删除的文件和代码 今天写了1300多行代码,被不小心删除了顿时感觉手足无措,后来用myeclipse的历史文件恢复功能,找回来了,虚惊一场!!!MyEclip ...
-
【转】shell脚本处理字符串的常用方法
转自:http://blog.csdn.net/linfeng999/article/details/6661233 1. 构造字符串 直接构造 STR_ZERO=hello #shell中等号左右的 ...
-
大数据测试之hadoop系统生态
poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标,也是国内最早探索大数据测试培训的机构,开发了独有的课程体系.如果对课程感兴趣,请大 ...
-
ES6字符串扩展
前面的话 字符串是编程中重要的数据类型,只有熟练掌握字符串操作才能更高效地开发程序.JS字符串的特性总是落后于其它语言,例如,直到 ES5 中字符串才获得了 trim() 方法.而 ES6 则继续添加 ...
-
洛谷P4363 一双木棋chess [九省联考2018] 搜索+hash
正解:记搜+hash 解题报告: 传送门! 因为看到nm范围特别小,,,所以直接考虑爆搜(bushi 先考虑爆搜之后再想优化什么的嘛QwQ 首先对这种都要最优的,就可以直接把答案设为针对某一方,然后题 ...