Winform 如何移植到 WebForm。
這個問題其實也就是要找一個好的方案,最大程度的復用現有的資源。
同時,WebForm的話,不一定要走ASPNET這條路,可以自己搞定HTTP協議的。
下圖是Winform版
下圖是Web版本
1。如何自己搞定HTTP協議
WebForm,可以重頭老老實實的用ASPNET來做,不過,現在Oracle,MongoDB這樣的數據庫都支持本地的瀏覽器方式管理了,這樣的應用,不用安裝ASP,PHP這樣的服務器,完全自己實現一個簡單的HTTP服務器。
OK,我們也實現一個吧,偵聽13000(這里隨便什么都可以的,不過推薦使用10000以上的端口號)端口,開啟多線程,做HTTP服務吧。
{
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來響應客戶端(寫客戶端),同時用一個無限循環來偵聽請求。
{
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>
/// <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。不用修改任何原來的代碼,最大限度的使用原來的代碼。
/// <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來行代碼,非常優雅。
通過這個例子,我想說,作為程序員,一定要時刻注意,不要出現同樣的代碼,不要雙重管理代碼,不要改了一個地方,另一個地方也必須改動。
明天去 埃森哲 入職,上海埃森哲的朋友,多多關照阿。。。。
