你的位置:首页 > ASP.net教程

[ASP.net教程]1、WPF一步步开发XMPP IM客户端:入门

[起因&目标]

因为工作原因接触openfire服务端和spark客户端开发2年,主要是基于openfire扩展开发了针对企业用途的服务器插件,还开发了各个平台上的客户端,详情可搜索微信公众号:CVTalk 

在开发过程中,发现基于Spark开发Java Swing客户端(公司内部命名CVTalk)比较重,用户体验很难做到和微信客户端看齐,皮肤的开发也比较费力,公司里绝大部分是windows的客户端,而且高清分辨率屏幕的用户越来越多, 那蚂蚁一样的小字和拥挤的布局改造很费劲,内存占用过多的问题也解决不了,swing客户端越发力不从心。

于是,我打算利用业余时间做一个 轻量级、易用、稳定、美观的Windows客户端。并抛砖引玉放开源:https://lightchat.codeplex.com/

规划自己未来的职业生涯,将持续做企业基础领域的开发,IM、Voip、Email、Portal等极少领域,持续稳定的前行......做精一件事没有几年是不行的,会一直坚持。

至于做IM开源日臻成熟以后的商业化、vip版本、服务、商务合作等事情,应该是水到渠成的,目前仅考虑把产品做好。 

坚持轻客户端,体验第一。 目标功能只有7个

1、  单聊

2、  群聊

3、  人员搜索

4、  消息搜索

5、  截屏传输

6、  文件传输

7、  插件扩展平台和企业业务集成接口,及xmpp少量扩展和服务器插件

界面采用Modern UI。布局采用微信PC的方式,即搜索+联系人+聊天窗一体,不开新窗。

第一版的界面,单聊界面

消息搜索:

 

[项目介绍]

项目目录介绍:

 

 

主要类介绍

 

1、Config.cs:全局设置类

//是否显示即时调试窗口    public static bool IsDebug = false;    public static String Version = "0.1";[email protected]    public static String Domain = "@im";//注意:这个是域名,一般为该服务器的机器名    public static String Server = "im";//外网或内网IP,目前AutoResolveConnectServer设置为true也不起作用,只能先改hosts文件    //public static readonly String ServerIP = "111.63.127.83";     public static readonly String ResourceName = "LightChat";    public static readonly bool IsCheckPresence = false;    public static readonly bool IsCheckChatState = false;    public static readonly bool AutoAgents = false;    public static readonly bool AutoPresence = true;    public static readonly bool AutoRoster = true;    //### 如果无法登陆,请在hosts文件中关联IP到Server域名 ###//C:\Windows\System32\drivers\etc\hosts 最后一行增加IP映射 比如 111.63.127.83 im    //自动解析connectserver属性,设置为true就会解析server属性即会利用System.Net.DNS.Resolve方法来将域名映射成ip地址    public static readonly bool AutoResolveConnectServer = false;    public static String MeCharacter = "我: ";    //### Organization structure data source definition ###    //### 组织架构数据源定义 ###    //support://public static IDataSource OrgSource = new     public static IDataSource OrgSource = new DbDataSource();    //### User search data source ###    //### 用户搜索数据源 ###    public static IResultsProvider SearchSource = new UserSearchProvider();

用户搜索数据源,使用统一的返回结果接口IResultsProvider

public static IResultsProvider SearchSource = new UserSearchProvider();

 使用接口的目的是使业务代码具有扩展性,比如组织架构数据源实现松耦合的具体实现。

2、登陆类: LightChat.Pages.Login

代码

/// <summary>  /// Login.xaml 的交互逻辑  /// </summary>  public partial class Login : UserControl  {    public Login()    {      InitializeComponent();      //读取设置      this.Dispatcher.BeginInvoke(new Action(LoadSettings), null);          }    private void btnLogin_Click(object sender, RoutedEventArgs e)    {      LoginTalk();    }    private void LoginTalk()    {      this.Dispatcher.BeginInvoke(new Action(BusyRun), null);      if (tbxUsername.Text.Trim() == "")      {        ModernProgressRing mpr = new ModernProgressRing();                ModernDialog.ShowMessage("注意:您的账户名未填写。", "提示",MessageBoxButton.OK);        return;      }      if (tbxPassword.Password == "")      {        ModernDialog.ShowMessage("注意:您的账户名未填写。", "提示", MessageBoxButton.OK);        return;      }      LoginXMPP();      SaveSettingsDB();    }    /// <summary>    /// 执行登录    /// </summary>    private void LoginXMPP()    {      XmppStatic.xmppCon = new XmppClientConnection();      XmppStatic.xmppCon.Server = Config.Server;      XmppStatic.xmppCon.Username = tbxUsername.Text.Trim();      XmppStatic.xmppCon.Password = tbxPassword.Password;      XmppStatic.xmppCon.Resource = Config.ResourceName;      XmppStatic.xmppCon.Priority = 0;      XmppStatic.xmppCon.AutoAgents = Config.AutoAgents;      XmppStatic.xmppCon.AutoPresence = Config.AutoPresence;      XmppStatic.xmppCon.AutoRoster = Config.AutoRoster;      XmppStatic.xmppCon.AutoResolveConnectServer = Config.AutoResolveConnectServer;      XmppStatic.xmppCon.UseStartTLS = true;      XmppStatic.xmppCon.ClientVersion = "1.0";      XmppStatic.xmppCon.Capabilities.Node = "http://www.cvtalk.cn/caps";      SetDiscoInfo();      try      {        //登录事件,异步         XmppStatic.xmppCon.OnLogin += new ObjectHandler(xmppCon_OnLogin);        //socket断开事件        XmppStatic.xmppCon.OnClose += new ObjectHandler(xmppCon_OnClose);        //连接XMPP服务        XmppStatic.xmppCon.Open();        XmppStatic.SelfJID = new Jid(tbxUsername.Text.Trim() + Config.Domain +          "/"+ Config.ResourceName);      }      catch (Exception ex)      {        Console.WriteLine(ex.Message);      }          }      private void xmppCon_OnClose(object sender)    {      this.Dispatcher.BeginInvoke(new Action(UIChangeOnClose), null);    }    /// <summary>    /// 断线后的UI改变,需要后续开发断线重连    /// </summary>    private void UIChangeOnClose()    {      UIStatic.mainHome.SetLogText("连接关闭");      //todo : 自动重连逻辑...    }     /// <summary>    /// 登录后的UI改变    /// </summary>    /// <param name="sender"></param>    void xmppCon_OnLogin(object sender)    {      this.Dispatcher.BeginInvoke(new Action(UIChangeOnLogin), null);    }    private void UIChangeBeforeLogin()    {    }    private void UIChangeOnLogin()    {      tbxUsername.IsEnabled = false;      tbxPassword.IsEnabled = false;      btnLogin.IsEnabled = false;      //设置主窗体的Tab展示      UIStatic.mainWindow.SetBussTabText("Work");       UIStatic.mainWindow.SetHomeTabText("Chat");      UIStatic.mainWindow.SetHomeTabText();      UIStatic.mainWindow.ContentSource = new System.Uri("/Pages/Home.xaml", UriKind.Relative);      BusyStop();      //VersionIq viq = new VersionIq(IqType.get, new Jid("[email protected]/PC"), new Jid("[email protected]"));      //xmppCon.Send(viq);            //DiscoManager dmg = new DiscoManager(xmppCon);      //dmg.DiscoverInformation(new Jid("im"));      //LastIq lastIq = new LastIq(IqType.get, new Jid("[email protected]/PC"));      //XmppStatic.xmppCon.Send(lastIq);      PrivateIq privateIq = new PrivateIq(IqType.get);      privateIq.Query.Storage = new agsXMPP.protocol.extensions.bookmarks.Storage();      XmppStatic.xmppCon.IqGrabber.SendIq(privateIq, new IqCB(PrivateIqResult), null);      ///SearchIq searchIq = new SearchIq(IqType.set, new Jid("search.im"));      //searchIq.Query.Email = "wangx";      //xmppCon.Send(searchIq);      //VcardIq vcardIq = new VcardIq();      //vcardIq.Type = IqType.get;      //vcardIq.To = new Jid("[email protected]");      //XmppStatic.xmppCon.IqGrabber.SendIq(vcardIq, new IqCB(VcardIqResult), null);      //BookmarkManager bkmg = new BookmarkManager(XmppStatic.xmppCon);      //bkmg.RequestBookmarks();    }    private void VcardIqResult(object sender, IQ iq, object data)    {      if (iq.Type == IqType.result)      {        Vcard vcard = iq.Vcard;        if (vcard != null)        {          string fullname = vcard.Fullname;          string nickname = vcard.Nickname;          string description = vcard.Description;          Photo photo = vcard.Photo;        }      }    }    //获取MUC会议室书签Bookmarks    private void PrivateIqResult(object sender, IQ iq, object data)    {            if (iq.Type == IqType.result)      {        Private piq = iq.Query as Private;        if (piq != null)        {          if (piq.HasChildElements)          {            XmppStatic.Conferences = ((Storage)piq.FirstChild).GetConferences();          }        }      }    }    /// <summary>    /// 设置本地客户端XMPP发现服务    /// </summary>    private void SetDiscoInfo()    {      XmppStatic.xmppCon.DiscoInfo.AddIdentity(new DiscoIdentity("pc", "cvtalk", "client"));      XmppStatic.xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_INFO));      XmppStatic.xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.DISCO_ITEMS));      XmppStatic.xmppCon.DiscoInfo.AddFeature(new DiscoFeature(agsXMPP.Uri.MUC));      // for testing to bypass disco caches      //_connection.DiscoInfo.AddFeature(new DiscoFeature(Guid.NewGuid().ToString()));     }    private void LoadSettings()    {      //建立数据库      CreateSaveTable.InitDB();      //读取设置,用户记住用户名、密码等      DataTable dtSelect = LoadTable.LoadSettings();      if (dtSelect == null || dtSelect.Rows.Count < 1) //没有数据      {        chkRememberPSW.IsChecked = true;      }      else      {        tbxUsername.Text = dtSelect.Rows[0]["Jid"] + "";        tbxPassword.Password = dtSelect.Rows[0]["Password"] + "";        chkRememberPSW.IsChecked = dtSelect.Rows[0]["RememberPSW"] + "" == "true" ? true : false;        chkAutomatic.IsChecked = dtSelect.Rows[0]["Automatic"] + "" == "true" ? true : false;      }    }    /// <summary>    /// 保存本地设置    /// </summary>    private void SaveSettingsDB()    {      CreateSaveTable.SaveSettingsDB(tbxUsername.Text, tbxPassword.Password, (bool)chkRememberPSW.IsChecked, (bool)chkAutomatic.IsChecked);    }    private void tbxPassword_KeyDown(object sender, KeyEventArgs e)    {      if (e.Key == Key.Enter)      {        //enter key is down        LoginTalk();      }    }    private void BusyRun()    {      //busyr.IsBusy = true;      this._loading.Visibility = Visibility.Visible;    }    private void BusyStop()    {      //busyr.IsBusy = false;      this._loading.Visibility = Visibility.Collapsed;    }  }

 

3、主窗体: LightChat.Pages.Home

区域介绍

 

 

代码下载地址:https://lightchat.codeplex.com/

 

下一篇预告:WPF一步步开发XMPP IM客户端:主窗体设计