最近研究 Ext.Net 的 Demo,看到 SimpleTask 例子,该例子利用 Ext.Net 自己封装了三个控件来使用,感觉不错。
其实,这种方式在 .NET 里很常见,但是由于 Ext.Net 自定义的 Ajax 控件,综合使用了 DOM、CSS 和 JavaScript,还是有一定难度。但是从另外一个角度来看,却比 .NET 简单很多。是不是很奇怪,因为 Ext.Net 有现成的操作 DOM、CSS 和 JavaScript 的框架,你只需要将它们集成到一起。
本节演示利用 Ext.Net 自定义简单的 GridPanel 控件。
首先看看,在不封装的情况下,使用 GridPanel 控件。用于改变某行、或某列的的式样。代码如下:
<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ext.Net GridPanel 改变某行、或某列的的式样</title>
<style type="text/css">
.indoor-t
{
color: #fb223a;
}
.indoor-f
{
text-decoration: line-through;
color: gray;
}
</style>
<script type="text/javascript">1:
2: // 改变行式样3: var getRowClass = function(r) {4: var d = r.data;5:
6: if (d.Indoor) {7: return "indoor-t";8: }
9: else {10: return "indoor-f";11: }
12: };
13: // 改变列式样14: var template = '<span style="color:{0};">{1}</span>';15: var changePriceStyle = function(value) {16: return String.format(template, (value >= 5) ? "green" : "red", value);17: };
18:
</script>1:
2:
3: <script runat="server">4: protected void Chg_Click(object sender, DirectEventArgs e)5: {
6: string Id = e.ExtraParams["Id"];7: string common = e.ExtraParams["Common"];8: bool indoor = Convert.ToBoolean(e.ExtraParams["Indoor"].ToString());9:
10: // TODO11: // Server-Side12:
13: this.Store1.SuspendEvents(false);14:
15: this.Store1.UpdateRecordField((object)Id, "Indoor", !indoor);16: this.Store1.ResumeEvents();17: this.Store1.FireEvent("datachanged", new JRawValue(this.Store1.ClientID));18: }
19:
</script>
</head>
<body>
<form id="form1" runat="server">
<ext:ResourceManager ID="ResourceManager1" runat="server" />
<ext:GridPanel ID="GridPanel1" runat="server" Width="600" Height="500" AutoExpandColumn="Common"
Title="植物" Frame="true">
<Store>
<ext:Store ID="Store1" runat="server" GroupField="Light">
<Proxy>
<ext:HttpProxy Method="GET" Url="Data/PlantService.asmx/Plants" />
</Proxy>
<Reader>
<ext:XmlReader Record="Plant" IDPath="Id">
<Fields>
<ext:RecordField Name="Id" />
<ext:RecordField Name="Common" />
<ext:RecordField Name="Botanical" />
<ext:RecordField Name="Zone" Type="Int" />
<ext:RecordField Name="ColorCode" />
<ext:RecordField Name="Light" />
<ext:RecordField Name="Price" Type="Float" />
<ext:RecordField Name="Availability" Type="Date" />
<ext:RecordField Name="Indoor" Type="Boolean" />
</Fields>
</ext:XmlReader>
</Reader>
<SortInfo Field="Common" Direction="ASC" />
</ext:Store>
</Store>
<ColumnModel ID="ColumnModel1" runat="server">
<Columns>
<ext:Column ColumnID="Id" Header="Key" DataIndex="Id" Width="10" />
<ext:Column ColumnID="Common" Header="Common Name" DataIndex="Common" Width="220" />
<ext:Column Header="Light" DataIndex="Light" Width="130" />
<ext:Column Header="Price" DataIndex="Price" Width="70" Align="right" Groupable="false">
<Renderer Format="UsMoney" />
</ext:Column>
<ext:Column Header="Price" DataIndex="Price" Width="70" Align="right" Groupable="false">
<Renderer Fn="changePriceStyle" />
</ext:Column>
<ext:DateColumn Header="Available" DataIndex="Availability" Width="95" Groupable="false"
Format="yyyy-MM-dd" />
<ext:Column Header="Indoor?" DataIndex="Indoor" Width="55" />
<ext:ImageCommandColumn Width="110">
<Commands>
<ext:ImageCommand CommandName="Change" Icon="TableEdit" Text="改变">
<ToolTip Text="改变" />
</ext:ImageCommand>
</Commands>
</ext:ImageCommandColumn>
</Columns>
</ColumnModel>
<DirectEvents>
<Command OnEvent="Chg_Click">
<ExtraParams>
<ext:Parameter Name="Id" Value="record.id" Mode="Raw">
</ext:Parameter>
<ext:Parameter Name="Common" Value="record.data.Common" Mode="Raw">
</ext:Parameter>
<ext:Parameter Name="Indoor" Value="record.data.Indoor" Mode="Raw">
</ext:Parameter>
</ExtraParams>
</Command>
</DirectEvents>
<LoadMask ShowMask="true" />
<SelectionModel>
<ext:RowSelectionModel ID="RowSelectionModel1" runat="server" />
</SelectionModel>
<View>
<ext:GroupingView ID="GroupingView1" HideGroupedColumn="true" runat="server" ForceFit="true"
StartCollapsed="true">
<GetRowClass Fn="getRowClass" />
</ext:GroupingView>
</View>
<Buttons>
<ext:Button ID="btnToggleGroups" runat="server" Text="展开/收起分组" Icon="TableSort" Style="margin-left: 6px;"
AutoPostBack="false">
<Listeners>
<Click Handler="#{GridPanel1}.getView().toggleAllGroups();" />
</Listeners>
</ext:Button>
</Buttons>
</ext:GridPanel>
</form>
</body>
</html>
运行界面:
图1 不封装的 GridPanel
说明:
1,首先看下 HTML 标记,值得注意的有以下几点:
<webServices>
<protocols>
<add name="HttpGet"/>
<add name="HttpPost"/>
</protocols>
</webServices>
2,服务器端事件。为了改变某行的式样,需要根据行ID,更新 Store 的记录,并触发 Store 的 datachanged 事件。
接下来,看看如何封装这个功能。先看界面,然后再说明如何实现。
<%@ Page Language="C#" %>
<%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
<%@ Register Assembly="ExtNetCustomControl" Namespace="ExtNetCustomControl.MyCustom"
TagPrefix="MyControl" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Ext.Net GridPanel 改变某行、或某列的的式样</title>
<ext:ResourcePlaceHolder ID="ResourcePlaceHolder1" runat="server" Mode="ScriptFiles" />
<link href="resources/main.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.indoor-t
{
color: #fb223a;
}
.indoor-f
{
text-decoration: line-through;
color: gray;
}
</style>
<script src="resources/myGrid.js" type="text/javascript"></script>
</head>
<body>
<form id="form1" runat="server">
<ext:ResourceManager ID="ResourceManager1" runat="server" InitScriptMode="Linked"
DirectEventUrl="MyCustomGrid.aspx" RemoveViewState="true" IDMode="Explicit" />
<ext:Panel ID="Panel1" runat="server" Width="600" Height="600" AutoScroll="true">
<Items>
<MyControl:MyGrid ID="MyGrid1" runat="server" AutoHeight="true" />
</Items>
</ext:Panel>
</form>
</body>
</html>
说明:
1,这个界面比之前简单多了。
定义 MyGrid 分部类,分别实现控件的 UI 和逻辑代码,如下所示:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Ext.Net;
using Newtonsoft.Json;
namespace ExtNetCustomControl.MyCustom
{
public partial class MyGrid : GridPanel
{
private Store store;
public MyGrid()
{
this.Title = "植物";
this.Width = System.Web.UI.WebControls.Unit.Pixel(600);
this.Height = System.Web.UI.WebControls.Unit.Pixel(500);
this.Frame = true;
this.AutoExpandColumn = "Common";
this.LoadMask.ShowMask = true;
this.SelectionModel.Add(new RowSelectionModel());
this.View.Add(new GroupingView
{
HideGroupedColumn = true,
ForceFit = true,
StartCollapsed = true,
GetRowClass = { Fn = MyGrid.SCOPE + ".getRowClass" }
});
this.BuildStore();
this.BuildColumnModel();
this.InitLogic();
}
private void BuildColumnModel()
{
ColumnModel cm = this.ColumnModel;
cm.Columns.Add(new Column
{
ColumnID = "Id",
Header = "Key",
Width = 10,
DataIndex = "Id"
});
cm.Columns.Add(new Column
{
ColumnID = "Common",
Header = "Common Name",
Width = 220,
DataIndex = "Common"
});
cm.Columns.Add(new Column
{
Header = "Light",
Width = 130,
DataIndex = "Light"
});
cm.Columns.Add(new Column
{
Header = "Price",
Width = 70,
DataIndex = "Price",
Align = Ext.Net.Alignment.Right,
Groupable = false,
Renderer = new Renderer { Format = Ext.Net.RendererFormat.UsMoney }
});
cm.Columns.Add(new Column
{
Header = "Price",
Width = 70,
DataIndex = "Price",
Align = Ext.Net.Alignment.Right,
Groupable = false,
Renderer = new Renderer { Fn = MyGrid.SCOPE + ".changePriceStyle" }
});
cm.Columns.Add(new DateColumn
{
Header = "Available",
Width = 95,
DataIndex = "Availability",
Format = "yyyy-MM-dd",
Groupable = false
});
cm.Columns.Add(new Column
{
Header = "Indoor?",
Width = 55,
DataIndex = "Indoor"
});
cm.Columns.Add(new ImageCommandColumn
{
Commands =
{
new ImageCommand
{
CommandName = "Change",
Icon = Icon.TableEdit,
Text = "改变"
}
}
});
}
private void BuildStore()
{
this.store = new Store
{
Reader =
{
new Ext.Net.XmlReader
{
Record="Plant",
IDPath="Id",
Fields =
{
new RecordField("Id"),
new RecordField("Common"),
new RecordField("Botanical"),
new RecordField("Zone",RecordFieldType.Int),
new RecordField("ColorCode"),
new RecordField("Light"),
new RecordField("Price", RecordFieldType.Float),
new RecordField("Availability", RecordFieldType.Date),
new RecordField("Indoor", RecordFieldType.Boolean)
}
}
},
SortInfo =
{
Field = "Common",
Direction = SortDirection.ASC
},
GroupField = "Light",
Proxy =
{
new HttpProxy { Method=Ext.Net.HttpMethod.GET,Url="Data/PlantService.asmx/Plants"}
}
};
this.Store.Add(this.store);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using Ext.Net.Utilities;
using Ext.Net;
namespace ExtNetCustomControl.MyCustom
{
[DirectMethodProxyID(IDMode = DirectMethodProxyIDMode.None)]
public partial class MyGrid
{
public const string SCOPE = "MyCustom.MyGrid";
private void InitLogic()
{
this.Listeners.Render.Fn = MyGrid.SCOPE + ".init";
this.Listeners.Render.Scope = MyGrid.SCOPE;
ComponentDirectEvent command = this.DirectEvents.Command;
command.Event += Command_Event;
command.ExtraParams.Add(new Parameter("Id", "record.id", ParameterMode.Raw));
command.ExtraParams.Add(new Parameter("Common", "record.data.Common", ParameterMode.Raw));
command.ExtraParams.Add(new Parameter("Indoor", "record.data.Indoor", ParameterMode.Raw));
Ext.Net.Button button = new Button();
button.ID = "btnToggleGroups";
button.Text = "展开/收起分组";
button.Icon = Icon.TableSort;
button.AutoPostBack = false;
button.Listeners.Click.Handler = MyGrid.SCOPE + ".toggleAllGroups();";
this.Buttons.Add(button);
}
protected void Command_Event(object sender, DirectEventArgs e)
{
string Id = e.ExtraParams["Id"];
string common = e.ExtraParams["Common"];
bool indoor = Convert.ToBoolean(e.ExtraParams["Indoor"].ToString());
// TODO
// Server-Side
this.store.SuspendEvents(false);
this.store.UpdateRecordField((object)Id, "Indoor", !indoor);
this.store.ResumeEvents();
this.store.FireEvent("datachanged", new JRawValue(this.store.ClientID));
}
protected override void OnLoad(EventArgs e)
{
if (!Ext.Net.X.IsAjaxRequest)
{
this.ResourceManager.AddDirectMethodControl(this);
}
}
}
}
说明:
但光有这个类还不够,还要跟它配合的客户端脚本,如下所示:
Ext.ns("MyCustom");
// ------------------MyGrid----------------------------------
MyCustom.MyGrid = {
init: function(grid) {
this.grid = grid;
},
getRowClass: function(r) {
var d = r.data;
if (d.Indoor) {
return "indoor-t";
}
else {
return "indoor-f";
}
return "";
},
changePriceStyle: function(value) {
var template = '<span style="color:{0};">{1}</span>';
return String.format(template, (value >= 5) ? "green" : "red", value);
},
toggleAllGroups: function() {
this.grid.getView().toggleAllGroups();
}
};
说明: