Winform 如何移植到 WebForm。
这个问题其实也就是要找一个好的方案,最大程度的复用现有的资源。
同时,WebForm的话,不一定要走ASPNET这条路,可以自己搞定HTTP协议的。
下图是Winform版
下图是Web版本
1。如何自己搞定HTTP协议
WebForm,可以重头老老实实的用ASPNET来做,不过,现在Oracle,MongoDB这样的数据库都支持本地的浏览器方式管理了,这样的应用,不用安装ASP,PHP这样的服务器,完全自己实现一个简单的HTTP服务器。
OK,我们也实现一个吧,侦听13000(这里随便什么都可以的,不过推荐使用10000以上的端口号)端口,开启多线程,做HTTP服务吧。
public
void Start()
{
TcpListener server =
null;
try
{
//
Set the TcpListener on port 13000.
Int32 port =
13000;
IPAddress localAddr = IPAddress.Parse(
"
127.0.0.1
");
//
TcpListener server = new TcpListener(port);
server =
new TcpListener(localAddr, port);
//
Start listening for client requests.
server.Start();
//
Enter the listening loop.
while (
true)
{
///
对于每个请求创建一个线程,线程的参数是TcpClient对象
TcpClient client = server.AcceptTcpClient();
OutputLog(
"
[Init]
" + DateTime.Now +
"
Connected!
",
0);
ParameterizedThreadStart ParStart =
new ParameterizedThreadStart(ProcessFun);
Thread t =
new Thread(ParStart);
t.Start(client);
}
}
catch (SocketException e)
{
OutputLog(
"
SocketException:
" + e,
0);
}
finally
{
//
Stop listening for new clients.
server.Stop();
server =
null;
}
}
这里就是不停的启动ProcessFun来响应客户端(写客户端),同时用一个无限循环来侦听请求。
private
void ProcessFun(
object clientObj)
{
TcpClient client = clientObj
as TcpClient;
//
Buffer for reading data
Byte[] bytes =
new Byte[
512];
OutputLog(
"
[Init]Waiting for a connection...
",
0);
//
Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
int i;
//
Loop to receive all the data sent by the client.
while ((client.Available !=
0) && (i = stream.Read(bytes,
0, bytes.Length)) !=
0)
{
//
Translate data bytes to a ASCII string.
String data = System.Text.Encoding.ASCII.GetString(bytes,
0, i);
//
OutputLog("Received:" + data);
string[] OrgRequest = data.Split(Environment.NewLine.ToCharArray());
if (OrgRequest[
0].StartsWith(
"
GET
"))
{
String[] Request = OrgRequest[
0].Split(
"
".ToCharArray());
String RequestItem = HttpUtility.UrlDecode(Request[
1], System.Text.Encoding.UTF8);
OutputLog(
"
[RequestItem]Received :
" + RequestItem,
0);
String[] RequestPath = RequestItem.Split(
"
?
".ToCharArray());
switch (RequestPath[
0])
{
case
"
/
":
//
根节点
GETPage(stream, GetPage.ConnectionList());
break;
case
"
/Connection
":
GETPage(stream, GetPage.Connection(RequestPath[
1]));
break;
default:
GETFile(stream, RequestItem.Replace(
"
/
",
"
\\
"));
break;
}
}
else
{
if (OrgRequest[
0].StartsWith(
"
POST
"))
{
}
}
}
//
Shutdown and end connection
client.Close();
}
这里响应请求,我只写完了GET,POST还没有开始动手呢。这里我们通过Stream.Read来获取请求,然后根据请求内容来寻找资源,将资源返还给客户端。
///
<summary>
///
///
</summary>
///
<param name="stream"></param>
///
<param name="FileName"></param>
private
void GETFile(NetworkStream stream, String FileName)
{
byte[] msg =
null;
byte[] bFile =
new
byte[
0];
String data = String.Empty;
Boolean IsFound =
false;
if (File.Exists(ServerPath + FileName))
{
IsFound =
true;
bFile = ReadFile(ServerPath + FileName);
}
else
{
//
资源文件里面获得
if (FileName.StartsWith(
"
\\MainTreeImage
"))
{
//
MainTreeImage00.png -- 从MainTreeImage 里面获得
int MainTreeImageIndex = Convert.ToInt32(FileName.Substring(
"
\\MainTreeImage
".Length,
2));
Image img = GetSystemIcon.MainTreeImage.Images[MainTreeImageIndex];
bFile = GetSystemIcon.imageToByteArray(img,ImageFormat.Png);
IsFound =
true;
}
}
if (IsFound)
{
//
Process the data sent by the client.
data =
"
HTTP/1.1 200 OK
" + Environment.NewLine;
//
if content-type is wrong,FF can;t render it,but IE can
string filetype = String.Empty;
switch (
new FileInfo(FileName).Extension)
{
case
"
.css
":
filetype =
"
text/css
";
break;
case
"
.js
":
filetype =
"
text/javascript
";
break;
case
"
.png
":
filetype =
"
image
";
break;
default:
break;
}
data +=
"
Content-Type: @filetype; charset=utf-8
".Replace(
"
@filetype
", filetype) + Environment.NewLine;
data +=
"
Content-Length:
";
data += (bFile.Length).ToString();
data += Environment.NewLine + Environment.NewLine;
msg = System.Text.Encoding.ASCII.GetBytes(data);
//
Send back a response.
stream.Write(msg,
0, msg.Length);
stream.Write(bFile,
0, bFile.Length);
OutputLog(
"
[System]Sent HTML OK
",
0);
}
else
{
data =
"
HTTP/1.1 404 Not Found
" + Environment.NewLine;
msg = System.Text.Encoding.ASCII.GetBytes(data);
//
Send back a response.
stream.Write(msg,
0, msg.Length);
OutputLog(
"
[System]FileName Not Found:
" + FileName,
0);
}
stream.Flush();
}
}
将资源(文件)给客户端,就是将资源转换为Bytes字节流,然后写入NetStream里面去。
当然,为了“欺骗”浏览器,我们还要在成功找到资源的时候,给个200 OK的标记,如果没有资源的话,给个404的标记。
以后,还要考虑 缓存的问题,已经请求过的资源,就给个3XX的,不用再重复取得资源了。
对于有些资源内容,这里干脆不从实体文件里面取了,直接从资源里面取了。客户才不知道这个文件到底怎么来的。
2。最大程度的复用代码
为了展示这个树形结构,原来的Windows代码非常的冗长(不是冗余)。
如何将这个树形Winform转为WebForm?如果最大程度的复用代码,这是必须要考虑的。树形展示,我使用了zTree这个Jquery插件。只要能够给他一个JSON的节点信息,就可以帮你自动完成树形了。
原来的代码的目标是生成一个Treeview,现在的目标是做个JSON。当然,可以将原来的代码里面的TreeNode的构成逻辑化为BsonDocument(MongoDB的概念,类似JSON)。不过,最最正确的做法是,直接将Treeview的结果转换为JSON。不用修改任何原来的代码,最大限度的使用原来的代码。
#region"展示数据库结构 WebForm"
///
<summary>
///
///
</summary>
///
<param name="ConnectionName"></param>
///
<returns></returns>
public
static String FillConnectionToJSON(String ConnectionName)
{
String strJSON = String.Empty;
TreeView tree =
new TreeView();
FillConnectionToTreeView(tree);
//
Transform Treeview To JSON
//
必须这样做,防止二重管理的问题。如果这里的逻辑有两套的话,维护起来比较麻烦。
//
一套逻辑,来控制树的内容。然后将TreeView的内容转换为JSON。
//
递归GetSubTreeNode
strJSON = GetSubTreeNode(tree.Nodes[
0]).ToJson(SystemManager.JsonWriterSettings);
return strJSON;
}
///
<summary>
///
///
</summary>
///
<param name="SubNode"></param>
///
<returns></returns>
private
static BsonDocument GetSubTreeNode(TreeNode SubNode)
{
if (SubNode.Nodes.Count ==
0)
{
BsonDocument SingleNode =
new BsonDocument();
SingleNode.Add(
"
name
", SubNode.Text);
SingleNode.Add(
"
icon
",
"
MainTreeImage
" + String.Format(
"
{0:00}
",SubNode.ImageIndex) +
"
.png
");
return SingleNode;
}
else
{
BsonDocument MultiNode =
new BsonDocument();
MultiNode.Add(
"
name
", SubNode.Text);
BsonArray ChildrenList =
new BsonArray();
foreach (TreeNode item
in SubNode.Nodes)
{
ChildrenList.Add(GetSubTreeNode(item));
}
MultiNode.Add(
"
children
", ChildrenList);
MultiNode.Add(
"
icon
",
"
MainTreeImage
" + String.Format(
"
{0:00}
", SubNode.ImageIndex) +
"
.png
");
return MultiNode;
}
}
#endregion
原来的FillConnectionToTreeView(tree);是一个很庞大的方法,用来构建一个Treeview,里面存放着数据库的完整结构。
这里我们将这个Treeview ,通过GetSubTreeNode 这个方法转换为了BsonDocument,然后直接使用内置方法转为JSON,一切搞定了。10来行代码,非常优雅。
通过这个例子,我想说,作为程序员,一定要时刻注意,不要出现同样的代码,不要双重管理代码,不要改了一个地方,另一个地方也必须改动。
明天去 埃森哲 入职,上海埃森哲的朋友,多多关照阿。。。。