恬谧

学习包容。容入团队,以自己之功劳为团队之功劳,以他人之过失为自己之过失
(坚绝反对南水北调西线工程,保卫长江源,保卫四川,保卫长江,坚绝反对在川渝建设核电站)
随笔 - 17, 文章 - 2, 评论 - 112, 引用 - 15
数据加载中……

2008年5月11日

ExtJs ExtenderControl 勾选列处理

 在ExtJs ExtenderControl 中添加勾选列后,不能正常勾选,因为每一次单击勾选框的操作都会都单击行的覆盖。
这时,我们需要在选择时,监听选择事件,并处理,即可。代码如下:

     sm = new Ext.grid.CheckboxSelectionModel({
        listeners: {
          beforerowselect : function (sm, rowIndex, keep, rec) {
            if (this.deselectingFlag && this.grid.enableDragDrop){
              this.deselectingFlag = false;
              this.deselectRow(rowIndex);
              return this.deselectingFlag;
            }

            return keep;
          }
        },
        onMouseDown : function(e, t){
            if(e.button === 0 ){
                e.stopEvent();
                var row = e.getTarget('.x-grid3-row');
                if(row){
                    var index = row.rowIndex;
                    if(this.isSelected(index)){
                        if (!this.grid.enableDragDrop)
                          this.deselectRow(index);
                        else
                          this.deselectingFlag = true;
                    }else{
                        this.selectRow(index, true);
                    }
                }
            }
        }
    });

posted @ 2008-05-11 18:50 舒密 阅读(245) | 评论 (0)编辑

ExtJs ExtenderControl 日期格式化处理

最近喜欢上了EXTJS,最后决定把它用在项目中,在网上找到了ExtJsExtenderControl,可按照例子把日期列写为:

<cc1:ColModel allowBlank="true" dataIndex="ApplyDate" DataType="Date" header="申请日期"
                            sortable="true"
/> 发现出现的是\Date(120654802547)\的时间戳形式

把其改为
                        <cc1:ColModel allowBlank="true" dataIndex="ApplyDate" DataType="Date" header="申请日期"
                            sortable="true" renderer="formatDate" />


  function formatDate(val){
    if(val!=null && val!="")
    {
        var timemap = val.replace("/Date(","").replace(")/","");
        var dateN = new Date(parseInt(timemap));
        var m;
        var d;
        if(dateN.getMonth()<9){
            m="0"+(dateN.getMonth()+1);
        }else{
            m=dateN.getMonth()+1;
        }
        if(dateN.getDate()<10){
            d="0"+dateN.getDate();
            }else{
            d=dateN.getDate();
            }
        return dateN.getFullYear()+"-"+m+"-"+d;
    }
    return "";
}



显示正常日期,大功告成!

posted @ 2008-05-11 18:30 舒密 阅读(190) | 评论 (0)编辑

2006年10月16日

自定义DataGrid控件开源 (V1.1)

     摘要: 在使用中又增加了以下功能:1、修改选择,采用异步调用的方式,防止大数量数据时,假死
2、任意字符进行转意 3、自定义合并字段的连接字符,修改了代码生成器  阅读全文

posted @ 2006-10-16 20:02 舒密 阅读(2143) | 评论 (9)编辑

2006年10月10日

看9月2日央视“决策者说”(转自四川新闻网)

     摘要:   阅读全文

posted @ 2006-10-10 12:46 舒密 阅读(355) | 评论 (0)编辑

要鲧?还是要禹?中华民族生存大问题!(转自四川新闻网)

     摘要:   阅读全文

posted @ 2006-10-10 12:44 舒密 阅读(519) | 评论 (6)编辑

2006年10月9日

自定义DataGrid控件开源

     摘要: 支持分页,全选,列合并,指定条件的行显示为指定的颜色,指定条件的行可以被勾选 等常用功能集合,并附代码生成工具,提高开发速度  阅读全文

posted @ 2006-10-09 13:01 舒密 阅读(4242) | 评论 (23)编辑

2006年9月25日

基于XMPP协议的即时通讯工具的客户端实现原理

        前面两篇,我们主要学习了基于XMPP协议的即时通讯工具的服务器端实现,下面我们将要来共同学习一下基于XMPP协议的即时通讯工具的客户端实现原理,本篇将主要分析agsXMPPP客户端用户登录认证的实现原理(关于XMPP协议与agsXMPP的相关知识请认真看第一篇中的附件)
1、主窗体初始化时将订阅一大堆的事件

  // 读取接收到的XML流信息
            XmppCon.OnReadXml            += new XmlHandler(XmppCon_OnReadXml);
            
//向服务器发送XML流信息
            XmppCon.OnWriteXml            += new XmlHandler(XmppCon_OnWriteXml);
            
            XmppCon.OnRosterStart        
+= new ObjectHandler(XmppCon_OnRosterStart);
            XmppCon.OnRosterEnd            
+= new ObjectHandler(XmppCon_OnRosterEnd);
            XmppCon.OnRosterItem        
+= new agsXMPP.XmppClientConnection.RosterHandler(XmppCon_OnRosterItem);

            XmppCon.OnAgentStart        
+= new ObjectHandler(XmppCon_OnAgentStart);
            XmppCon.OnAgentEnd            
+= new ObjectHandler(XmppCon_OnAgentEnd);
            XmppCon.OnAgentItem            
+= new agsXMPP.XmppClientConnection.AgentHandler(XmppCon_OnAgentItem);

            
//用户登录事件
            XmppCon.OnLogin                += new ObjectHandler(XmppCon_OnLogin);
            
//关闭连接事件
            XmppCon.OnClose                += new ObjectHandler(XmppCon_OnClose);
            
//连接错误事件
            XmppCon.OnError                += new ErrorHandler(XmppCon_OnError);
            
//收到Presence节时引发的事件
            XmppCon.OnPresence            += new agsXMPP.XmppClientConnection.PresenceHandler(XmppCon_OnPresence);
            
//收到Message节时引发的事件
            XmppCon.OnMessage            += new agsXMPP.XmppClientConnection.MessageHandler(XmppCon_OnMessage);
            
//收到IQ节时引发的事件
            XmppCon.OnIq                += new StreamHandler(XmppCon_OnIq);
            
//认证错误事件
            XmppCon.OnAuthError            += new OnXmppErrorHandler(XmppCon_OnAuthError);

            
//读取Socket事件
            XmppConSocket.OnReadSocketData    += new agsXMPP.net.BaseSocket.OnSocketDataHandler(ClientSocket_OnReceive);
            
//写入Socket事件
            XmppCon.OnWriteSocketData   += new agsXMPP.net.BaseSocket.OnSocketDataHandler(ClientSocket_OnSend);

            XmppCon.ClientSocket.OnValidateCertificate 
+= new System.Net.Security.RemoteCertificateValidationCallback(ClientSocket_OnValidateCertificate);

            
//连接状态改变事件            
            XmppCon.OnXmppConnectionStateChanged        += new agsXMPP.XmppClientConnection.XmppConnectionStateHandler(XmppCon_OnXmppConnectionStateChanged);
            XmppCon.OnSaslStart 
+= new SaslEventHandler(XmppCon_OnSaslStart);

窗体接收,和发送XML流时都不在UI线程中进行,这里都是采用异步调用的方式发送和接收XML数据流,所以对发送和接收XML流数据时所引发的事件,如果需要更新到用户界面,一定要将线程切换在UI线程,不然会出现一些莫名其妙的错误.

用户输入用户名,密码后将打开连接
 private void connectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            frmLogin f 
= new frmLogin(XmppCon);

            
if (f.ShowDialog() == DialogResult.OK)
            {               
                XmppCon.Open();
            }
        }

在这里的 XmppCon.Open(); 将引发一系列的事件,并最终引发frmMain的XmppCon_OnWriteXml事件,向服务器端发送XML数据流,下面将重点说明这一系列的事件的引发过程:
我们先来看一下引发这一系引事件的流程图


首先将高用OpenSocket()初发始化Socket连接并打开,在调用SocketConnect时将引发OnXmppConnectionStateChanged事件,初发始化Socket连接完成后将回调 EndConnect(IAsyncResult ar)并先引发OnConnect事件并发送XML数据流,在接行EndConnect时,如果抛出异常将引发OnError事件.调用Receive()完成后将回调EndReceive(IAsyncResult ar),然后引发OnReceive事件

这里以后的交互过程,请参看<<.net平台 基于 XMPP协议的即时消息服务端简单实现>>一文中的"客户端与服务器端的交互过程"

posted @ 2006-09-25 08:07 舒密 阅读(2894) | 评论 (7)编辑

2006年9月22日

基于 XMPP协议的服务器端 文件互传的简单实现

         昨天我们讲了客户端实现用户互发消息的原理,今天我们继续学习一下基于XMPP协议的文件传输,首先我们假设用户已经登录,这里我们假设Clinet Send对Clinet Receive 发送文件,这里记录了Clinet Send的发送和接收XML流记录

1、发送请求表示要向Clinet test发送文件,并发送文件大小和文件名
SEND: <iq xmlns="jabber:client" id="agsXMPP_6" to="Receive @localhost/" type="set"><si xmlns="http://jabber.org/protocol/si" profile="http://jabber.org/protocol/si/profile/file-transfer" id="b3b5f49c-8a94-49e2-908a-3de694b98af0"><file xmlns="http://jabber.org/protocol/si/profile/file-transfer" name="MiniClient.exe.config" size="382"><range /></file><feature xmlns="http://jabber.org/protocol/feature-neg"><xmlns="jabber:x:data" type="form"><field type="list-single" var="stream-method"><option><value>http://jabber.org/protocol/byReceive reams</value></option></field></x></feature></si></iq>

2、Receive 收到Send发实来的请求后,发送出响应流
RECV: <iq xmlns="jabber:client" to="Send@localhost" type="result" id="agsXMPP_6"><si xmlns="http://jabber.org/protocol/si" id="b3b5f49c-8a94-49e2-908a-3de694b98af0"><feature xmlns="http://jabber.org/protocol/feature-neg"><xmlns="jabber:x:data" type="submit"><field var="stream-method"><value>http://jabber.org/protocol/byReceive reams</value></field></x></feature></si></iq>

3、Send收到Receive 愿意接收的响应后,发出如下XML流
SEND: <iq xmlns="jabber:client" id="agsXMPP_7" to="Receive@localhost" type="set"><query xmlns="http://jabber.org/protocol/bySendreams" sid="71f1b875-4d84-429c-b39d-7dabcd400f3d"><streamhost jid="Send@localhost/MiniClient" host="localhost" port="1000" /><streamhost jid="proxy.ag-software.de" host="proxy.ag-software.de" port="7777" /></query></iq>


注意这里注要是代理的设置,agsXMPP 自带的客户端是使用代理传输数据的,所以必须对其做相应的修改,使其支持局域网内支持发送文件.

下面我们来看一下服务器端是如何路由消息的呢?
从上面所发送的XML流我们可以看出,实际上用户传送文件所发送的都是IQ节,我们必须对IQ节进行路由才能使其找到相应的接收者 ,所以我们需要在XmppServerConnection类中添加如下代码:
 

private void ProcessIQ(IQ iq)
        {
//传送文件得理
  if (iq.Query == null)
            {
                
if(iq.Type == IqType.result)
                    
this.Send(iq);
                ProcessOobIQ(iq);
            }
            
else
            
if (iq.Query.GetType() == typeof(Auth))
            {
                Auth auth 
= iq.Query as Auth;
                
this.Username = auth.Username.ToString();

                
switch (iq.Type)
                {
                    
case IqType.get:
                        iq.SwitchDirection();
                        iq.Type 
= IqType.result;
                        auth.AddChild(
new Element("password"));
                        auth.AddChild(
new Element("digest"));
                        Send(iq);
                        
break;
                    
case IqType.set:
                        
// Here we should verify the authentication credentials
                        if (AccountBus.CheckLogin(auth.Username, auth.Digest, this.SessionId))
                        {
                            iq.SwitchDirection();
                            iq.Type 
= IqType.result;
                            iq.Query 
= null;
                            Send(iq);
                            Console.WriteLine(auth.Username 
+ "登录了" + "   登录时间:" + System.DateTime.Now.ToString());
                        }
                        
else
                        {
                          
//  iq.SwitchDirection();
                            iq.Type = IqType.error;
                            iq.Query 
= null;
                            Send(iq);
                        }
                        
break;
                }

            }
            
else if (iq.Query.GetType() == typeof(Roster))
            {
                ProcessRosterIQ(iq);
            }
//用代理传送文件处理
            else if(iq.Query.FirstChild !=null)
            {
               
// iq.SwitchDirection();
               
// //iq.Type = IqType.result;
               
// //Send(iq);
               
// iq.Type = IqType.set;
                
//this.Send(iq);
                ProcessOobIQ(iq);
            }

}
传送文件载图:

这里我们讲解完了服务器是如何处理用户互传文件,以后我将继续讲解客户端的实现原理.
         这篇文章本来是昨晚上写好的,但由于源码留在公司了,所以今早上才贴出来。

posted @ 2006-09-22 08:04 舒密 阅读(2750) | 评论 (9)编辑

2006年9月20日

.net平台 基于 XMPP协议的即时消息服务端简单实现

        昨天抽空学习了一下XMPP,在网上找了好久,中文的资料太少了所以做这个简单的例子,今天才完成。公司也正在准备开发基于XMPP协议的即时通讯工具所以也算是打一个基础吧!如果你还没有了解过XMPP请先阅读附录中链接的文章,本实例是基agsXMPP上开发的,agsXMPP是C#写的支持开源XMPP协议软件,我们可以在agsXMPP上快速构建自已的即时通讯平台,我的这个例子只是修改了服务器端,因为agsXMPP本身自带的服务器端没有实现聊天功能、签名和登录密码认证。

服务器端XmppSeverConnection类事件

//在流开始时触发,一般是最初的响应流
streamParser.OnStreamStart += new StreamHandler(streamParser_OnStreamStart);

//在流结束时触发,一般是发送</stream:stream>并关闭套接字连接streamParser.OnStreamEnd += new StreamHandler(streamParser_OnStreamEnd);

//在接收到流结点时触发,这是用得最多的,常用的<message>消息,<Presence>出席消息,< IQ>请求应答消息都在这里处理
streamParser.OnStreamElement += new StreamHandler(streamParser_OnStreamElement);

 

//此处处理大部份的消息,包括消息路由
private void streamParser_OnStreamElement(object sender, Node e)
        {
            Console.WriteLine(
"OnStreamElement: " + e.ToString());
            
if (e.GetType() == typeof(Presence))
            {
             
// 路由presences节
            }
            
else if (e.GetType() == typeof(Message))
            {
                
// 路由messages节
            }
            
else if (e.GetType() == typeof(IQ))
            {
                
//处理IQ节
            }
        }

 

 /// <summary>
        
/// IQ节处理函数
        
/// </summary>
        
/// <param name="iq">.</param>
        private void ProcessIQ(IQ iq)
        {
            
if (iq.Query.GetType() == typeof(Auth))
            {
               Auth auth 
= iq.Query as Auth;
                
this.Username = auth.Username.ToString();

                
switch (iq.Type)
                {
                    
case IqType.get:
                        iq.SwitchDirection();
                        iq.Type 
= IqType.result;
                        auth.AddChild(
new Element("password"));
                        auth.AddChild(
new Element("digest"));
                        Send(iq);
                        
break;

                    
case IqType.set:
                        
// 进行登录认证
                        if (AccountBus.CheckLogin(auth.Username, auth.Digest, this.SessionId))
                        {
                            iq.SwitchDirection();
                            iq.Type 
= IqType.result;
                            iq.Query 
= null;
                            Send(iq);

                            Console.WriteLine(auth.Username 
+ "登录了" + "   登录时间:" + System.DateTime.Now.ToString());

                        }
                       
else
                        {
//登录失败返回错误信息
                            iq.SwitchDirection();
                            iq.Type 
= IqType.error;
                            iq.Query 
= null;
                            Send(iq);
                        }
                        
break;
                }
            }
            
else if (iq.Query.GetType() == typeof(Roster))
            {
                ProcessRosterIQ(iq);
            }
        }

 

/// <summary>
        
/// 处理IQ节的杂项数据.
        
/// </summary>
        
/// <param name="iq">The iq.</param>
        private void ProcessRosterIQ(IQ iq)
        {
            
if (iq.Type == IqType.get)
            {
                
// 发送IQ节的杂项数据
              
//这里我用来下载好友列表
                iq.SwitchDirection();
                iq.Type 
= IqType.result;
                List
<string> friendList = new List<string>();
                friendList 
= AccountBus.GetFriendName(this.username);
                
foreach (string str in friendList)
                {
                    RosterItem ri 
= new RosterItem();
                    ri.Name 
= str.Trim();
                    ri.Subscription 
= SubscriptionType.both;
                    ri.Jid 
= new agsXMPP.Jid(str.Trim() + "@localhost");
                    ri.AddGroup(
"localhost");
                    iq.Query.AddChild(ri);
                }
                Send(iq);
            }
        }


 

服务器端开启监听5222端口

 

 while (running)
                {
                    
////
                    allDone.Reset();
                    
// Start an asynchronous socket to listen for connections.
                    Console.WriteLine("等待连接");

                    listener.BeginAccept(
new AsyncCallback(AcceptCallback), null);

                    
//// 等待客户端连接                    
                     allDone.WaitOne();

                }


如果收到客户端请求就异步调用AcceptCallback初始化套接字连接
,并为客户端建立一个通信线程,新建初始化套接字连接采用异步调
用读取套接字信息

 public XmppSeverConnection(Socket sock)
            : 
this()
        {
            m_Sock 
= sock;
            m_Sock.BeginReceive(buffer, 
0, BUFFERSIZE, 0new AsyncCallback(ReadCallback), null);
            m_Sock.SendTimeout 
= 100;
        }



客户端与服务器端的交互过程


  
1客户端异步向服务器端发送连接请求

<stream:stream to='localhost' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en'>

2服务器端收到请求,初始化回应流,并随机生成一相SessionID

 <stream:stream xmlns:stream="http://etherx.jabber.org/streams" from="localhost" id="30e3b8c0" >

3等待服务器返回消息后客户端发送用户名(由于在客户端采用了异步调用
方式,所以UI界面感觉不到等待)

<iq xmlns="jabber:client" id="agsXMPP_1" type="get" to="localhost">
<query xmlns="jabber:iq:auth"><username>test</username></query></iq>

4服务器端收到用户名等待用户提供密码

<iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_1">
<query xmlns="jabber:iq:auth"><username>test</username><password />
<digest /></query></iq>

5客户端提供加密后的密码

<iq xmlns="jabber:client" id="agsXMPP_2" to="localhost" type="set">
<query xmlns="jabber:iq:auth"><username>test</username>
<digest>e66557d2b67256bf7e9b317a51b6101674a56b5e</digest>
<resource>MiniClient</resource></query></iq>

6服务器端从数据库验证用户名和密码,并返回结果

iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_2" />

7如果返回错误,客户端提示并终断连接,否则客户端发送响应数据

8 服务器端返回数据

9 客户端发送状态,

10服务器收到状态,发送IQ节并通知其它用户.

项目解决方案和类图


附录:
推荐使用Pandion作为客户端
服务器端下载      客户端下载
agsXMPP 源码下载 
XMPP RFC 3920 可扩展消息出席协议 
jabber官方网站

posted @ 2006-09-20 23:29 舒密 阅读(4617) | 评论 (18)编辑

2006年9月3日

WinForm DataGrid的功能扩展及实现 (已写完)

     摘要: 你是否在开发WinForm应用程序时,通常使用DataGrid来作为数据的层现,是否会用到DataGrid分页,添加序号,添加全选,反选功能,有时还会用到不同数据显示不同的颜色.DataGridGost为我们做了这一切,你只需要简单的设置,几乎不用写一行代码就能轻松完成.下面我们一起来进入DataGridGost神奇的世界[关键词]DataGrid,DataGrid全选,自动添加序号.1.系统概...  阅读全文

posted @ 2006-09-03 09:40 舒密 阅读(4428) | 评论 (15)编辑