WPF一步步开发XMPP IM客户端1:入门
[起因&目标]
因为工作原因接触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 = ;
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 < ) //没有数据
{
chkRememberPSW.IsChecked = true;
}
else
{
tbxUsername.Text = dtSelect.Rows[]["Jid"] + "";
tbxPassword.Password = dtSelect.Rows[]["Password"] + "";
chkRememberPSW.IsChecked = dtSelect.Rows[]["RememberPSW"] + "" == "true" ? true : false;
chkAutomatic.IsChecked = dtSelect.Rows[]["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客户端:主窗体设计
WPF一步步开发XMPP IM客户端1:入门的更多相关文章
- WPF一步步开发XMPP IM客户端2:主窗体设计
UI设计方案: 在设计窗体UI之前,先要了解一些主要的接口和帮助类: 对于主窗的左侧列表,容器内的Item必须实现ILeftItem的接口,比如联系人.系统消息.群等,接口包含点击事件 public ...
- 一步步开发自己的博客 .NET版(3、注册登录功能)
前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做 ...
- 一步步开发自己的博客 .NET版 剧终篇(6、响应式布局 和 自定义样式)
前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做 ...
- Converse.js – 开源的 XMPP 聊天客户端
Converse.js 是一个运行在浏览器的免费和开源的聊天客户端.它可以集成到任何网页.类似于 Facebook 的聊天,它也支持多用户聊天室.Converse.js 可以连接到任何可访问的 XMP ...
- 一步步开发自己的博客 .NET版(1、基本显示)
前言 我们每个猿都有一个搭建自己独立博客的梦,我也不例外.以前想 现在想 以后也想.之所以一直迟迟没有着手,是因为难以跨出第一步.每次心里想着,等我以后技术好了再说,然后就没有然后了.以前用过word ...
- 一步步开发自己的博客 .NET版(4、文章发布功能)百度编辑器
前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做个插件,任何网站上的技术 ...
- 一步步开发自己的博客 .NET版(5、Lucenne.Net 和 必应站内搜索)
前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做 ...
- 基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(目录)
本系统文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以 SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...
- (转)【ASP.NET开发】获取客户端IP地址 via C#
[ASP.NET开发]获取客户端IP地址 via C# 说明:本文中的内容是我综合博客园上的博文和MSDN讨论区的资料,再通过自己的实际测试而得来,属于自己原创的内容说实话很少,写这一篇是为了记录自己 ...
随机推荐
- JSONModel简便应用
JSONModel, Mantle 这两个开源库都是用来进行封装JSON->Model的, 想想看, 直接向服务器发起一个请求,然后回来后,就是一个Model, 直接使用, 这是一个多么美好的事 ...
- 自然语言处理--中文文本向量化counterVectorizer()
1.载入文档 #!/usr/bin/python # -*- coding: utf-8 -*- import pandas as pd import re import jieba from skl ...
- Remove duplicates
https://github.com/Premiumlab/Python-for-Algorithms--Data-Structures--and-Interviews/blob/master/Moc ...
- 2018.09.12 earthquake(最优比率生成树)
描述 地震已经破坏了农夫约翰所有的农场以及所有连接农场的道路.作为一个意志坚强的人,他决定重建所有的农场.在重建全部N(1 <= N <= 400)个农场之前,首先必须把所有农场用道路连接 ...
- 2018.07.29~30 uoj#170. Picks loves segment tree VIII(线段树)
传送门 线段树好题. 维护区间取两种最值,区间加,求区间两种历史最值,区间最小值. 自己的写法调了一个晚上+一个上午+一个下午+一个晚上并没有调出来,90" role="prese ...
- Part 3 - Advanced Concepts(11-13)
https://simpleisbetterthancomplex.com/series/2017/09/18/a-complete-beginners-guide-to-django-part-3. ...
- 今天研究了一下手机通信录管理系统(C语言)
题目:手机通信录管理系统 一.题目要求 二.需求分析 三.设计步骤/编写代码 四.上机/运行结果 五.总结 一.题目要求 模拟手机通信录管理系统,实现对手机中的通信录进行管理操作.功能要求: (1)查 ...
- Mac pro 安装IntelliJ IDEA 2017版
1.官网下载这个版本https://www.jetbrains.com 2.点击下载即可 3.下载好后放入本地 4.启动mac终端进行破解 输入命令:sudo vim /private/etc/hos ...
- Resharper 修改命名空间
1. 使用Reshared 右键->Refactor->Rename 修改所有文件的命名空间(鼠标移动到对应类的命名空间) 2.修改类库中的命名空间 包括程序集信息 右键->属性 3 ...
- VTK7.0.0编译安装心得
配置:Win7(64bit)+VS2013+VTK700+QT5 (为了编译适应所用工业设备32bit,所有编译凑在32bit下进行) 预安装: (1)安装CMake软件,用于编译重构VTK源文件,编 ...