[起因&目标]

因为工作原因接触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:入门的更多相关文章

  1. WPF一步步开发XMPP IM客户端2:主窗体设计

    UI设计方案: 在设计窗体UI之前,先要了解一些主要的接口和帮助类: 对于主窗的左侧列表,容器内的Item必须实现ILeftItem的接口,比如联系人.系统消息.群等,接口包含点击事件 public ...

  2. 一步步开发自己的博客 .NET版(3、注册登录功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  3. 一步步开发自己的博客 .NET版 剧终篇(6、响应式布局 和 自定义样式)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  4. Converse.js – 开源的 XMPP 聊天客户端

    Converse.js 是一个运行在浏览器的免费和开源的聊天客户端.它可以集成到任何网页.类似于 Facebook 的聊天,它也支持多用户聊天室.Converse.js 可以连接到任何可访问的 XMP ...

  5. 一步步开发自己的博客 .NET版(1、基本显示)

    前言 我们每个猿都有一个搭建自己独立博客的梦,我也不例外.以前想 现在想 以后也想.之所以一直迟迟没有着手,是因为难以跨出第一步.每次心里想着,等我以后技术好了再说,然后就没有然后了.以前用过word ...

  6. 一步步开发自己的博客 .NET版(4、文章发布功能)百度编辑器

    前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做个插件,任何网站上的技术 ...

  7. 一步步开发自己的博客 .NET版(5、Lucenne.Net 和 必应站内搜索)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  8. 基于 SailingEase WinForm Framework 开发优秀的客户端应用程序(目录)

    本系统文章将详细阐述客户端应用程序的设计理念,实现方法. 本系列文章以  SailingEase WinForm Framework 为基础进行设计并实现,但其中的设计理念及方法,亦适用于任何类型的客 ...

  9. (转)【ASP.NET开发】获取客户端IP地址 via C#

    [ASP.NET开发]获取客户端IP地址 via C# 说明:本文中的内容是我综合博客园上的博文和MSDN讨论区的资料,再通过自己的实际测试而得来,属于自己原创的内容说实话很少,写这一篇是为了记录自己 ...

随机推荐

  1. windows8.1 初体验

    昨天装了Win8.1,Office2013 由于是英文版的,需要装一下中文语言包,然后就能使用自带的微软拼音输入法了. 我喜欢双屏时的桌面背景,选择span时一张图片可以跨越2个屏幕,比win7的好. ...

  2. Devexpress VCL Build v2013 vol 13.2.5 发布

    支持xe6 了,但是承诺的功能在哪里? What's New in 13.2.5 (VCL Product Line)   New Major Features in 13.2 What's New ...

  3. 2018.06.30 BZOJ1857: [Scoi2010]传送带(三分套三分)

    1857: [Scoi2010]传送带 Time Limit: 1 Sec Memory Limit: 64 MB Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段 ...

  4. Django入门与实践-第17章:保护视图(完结)

    http://127.0.0.1:8000/boards/1/ #boards/views.py from django.contrib.auth.decorators import login_re ...

  5. 全国各地dns服务器列表

    211.103.13.101 江苏省无锡市 移动DNS服务器 211.136.28.231 北京市 移动DNS服务器 211.136.28.234 北京市 移动DNS服务器 211.136.28.23 ...

  6. java Exception 出错的栈信息打印到日志中 打印堆栈信息

    我们在开发程序的过程当中,日志是必不可少的工具,这有助于我们分析问题的原因,和出错的详细信息,而java的异常机制又会方便且迅速的帮我们找到出错行的位置. try { .... } catch (Ex ...

  7. 用jQ实现一个简易计算器

    HTML和CSS结构: <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  8. UVa 12118 nspector's Dilemma (构造+DFS+欧拉回路)

    题意:给定n个点,e条边和每条边的长度t,每两个点之间都有路相连,让你求一条最短的路经过这e条边. 析:刚开始想到要判连通,然后把相应的几块加起来,但是,第二个样例就不过,后来一想,那么有欧拉回路的还 ...

  9. html 源码 引入样式

    post-title2 示例 sdf post-title 示例

  10. Yarn application has already exited with state FINISHED

    如果在运行spark-sql时遇到如下这样的错误,可能是因为yarn-site.xml中的配置项yarn.nodemanager.vmem-pmem-ratio值偏小,它的默认值为2.1,可以尝试改大 ...