WPF一步步开发XMPP IM客户端1:入门

来源:互联网 发布:忘羡捏脸数据 编辑:程序博客网 时间:2024/06/05 15:53
[起因&目标]

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

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

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

 

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

1、  单聊

2、  群聊

3、  人员搜索

4、  消息搜索

5、  截屏传输

6、  文件传输

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

 

特性:

  • XMPP message、iq、presence、vcard、roster.....
  • MUC(Multi-User Chat) support
  • Fast localized(sqlite) user search,and Openfire ofUser import & sync
  • Easy message search console
  • Extensible organizational structure tree interface (Chinese users love it)
  • HD resolution screen support
  • ...

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

第一版的界面,单聊界面

消息搜索:

 

[项目介绍]

项目目录介绍:

 

 

主要类介绍

 

1、Config.cs:全局设置类

//是否显示即时调试窗口        public static bool IsDebug = false;        public static String Version = "0.1";//@加上bareJid的域名部分        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:XML\Json\SqliteDB        //public static IDataSource OrgSource = new XmlDataSource();        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("wangxin@im/PC"), new Jid("zz@im"));            //xmppCon.Send(viq);                        //DiscoManager dmg = new DiscoManager(xmppCon);            //dmg.DiscoverInformation(new Jid("im"));            //LastIq lastIq = new LastIq(IqType.get, new Jid("wangxin@im/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("wangxin@im");            //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客户端:主窗体设计

0 0
原创粉丝点击