大家好,我是宝弟!
今天给大家推荐一个使用WPF开发的高仿微信网页版的应用程序WeChatClient,WeChatClient目标框架是.Net Framewor 4.6.1。

软件中的图标和表情都是使用网页微信的资源,还有很多功能是待实现的,例如任务栏托盘图标,图片文件发送,截屏,创建群聊等。
项目中用到了prism和reactiveui框架,虽然它们都是MVVM框架,这里用它们做不同的事情。使用prism是为了将应用程序划分为多个离散的、松散耦合的、半独立的组件,使用reactiveui是为了反应式编程。
登录
public class LoginModule : IModule{public void OnInitialized(IContainerProvider containerProvider){var regionManager = containerProvider.Resolve<IRegionManager>();var ea = containerProvider.Resolve<IEventAggregator>();IRegion region = regionManager.Regions[WeChatClientConst.MainRegionName];var qrView = containerProvider.Resolve<QRCodeView>();region.Add(qrView);var headView = containerProvider.Resolve<HeadView>();region.Add(headView);var errorView = containerProvider.Resolve<ErrorView>();region.Add(errorView);Task.Run(() =>{LoopLoginCheck(ea, region, qrView, headView, errorView);});ea.GetEvent<SwitchAccountEvent>().Subscribe(() =>{region.Activate(qrView);});}public void RegisterTypes(IContainerRegistry containerRegistry){}/// <summary>/// 循环检测是否登录了/// </summary>private void LoopLoginCheck(IEventAggregator ea, IRegion region, QRCodeView qrView, HeadView headView, ErrorView errorView){LoginService ls = new LoginService();object login_result = null;//循环判断手机扫描二维码结果while (true){login_result = ls.LoginCheck();//已扫描 未登录if (login_result is ImageSource){ea.GetEvent<UpdateHeadImageEvent>().Publish(login_result as ImageSource);Application.Current.Dispatcher.BeginInvoke(new Action(() =>{region.Activate(headView);}));}//已完成登录if (login_result is string){//访问登录跳转URLif (ls.GetSidUid(login_result as string, out string errorMsg)){Application.Current.Dispatcher.BeginInvoke(new Action(() =>{region.RemoveAll();ea.GetEvent<LoginSuccessfulEvent>().Publish();}));break;}else{ea.GetEvent<ShowErrorMsgEvent>().Publish(errorMsg);Application.Current.Dispatcher.BeginInvoke(new Action(() =>{region.Activate(errorView);}));break;}}////超时if (login_result is int){ea.GetEvent<UpdateQRCodeEvent>().Publish();}}}}
聊天
[ExposeServices(ServiceLifetime.Singleton, typeof(IChatListManager))] //注册为IChatListManager接口(单例)public class ChatListViewModel : ReactiveObject, IChatListManager, INavigationAware{public ObservableCollection<WeChatUser> ChatList { get; private set; } = new ObservableCollection<WeChatUser>();private WeChatUser _selectedItem;//[Reactive]public WeChatUser SelectedItem{get { return _selectedItem; }set{this.RaiseAndSetIfChanged(ref _selectedItem, value);if (_selectedItem != null)_selectedItem.UnReadCount = 0;}}public ICommand CloseChatCommand { get; }[Dependency]protected IImageDownloadService ChatImageDownloadService { get; set; }[Dependency]protected IImageDownloadService MessageImageDownloadService { get; set; }[Dependency]protected IContactListManager ContactListManager { get; set; }//[Dependency]//protected Lazy<IMainManager> MainManager { get; set; }public ChatListViewModel(IChatContentManager chatContentManager){CloseChatCommand = ReactiveCommand.Create<WeChatUser>(CloseChat);this.WhenAnyValue(p => p.SelectedItem).Subscribe(p => chatContentManager.SelectedChat = p);}private void CloseChat(WeChatUser chat){ChatList.Remove(chat);}public void AddChat(params WeChatUser[] chat){foreach (var item in chat){if (ChatList.Any(p => p.UserName == item.UserName))continue;ChatList.Add(item);ChatImageDownloadService.Add(item);}}public void ModChat(params WeChatUser[] chat){var selected = SelectedItem;foreach (var item in chat){var local = ChatList.FirstOrDefault(p => p.UserName == item.UserName);if (local != null){if (SelectedItem == local)selected = item;item.MessageList.AddRange(local.MessageList); //将本地聊天的信息拷贝过来item.UnReadCount = local.UnReadCount;ChatList.Remove(local);}ChatList.Insert(0, item); //将修改后的聊天放在首位ChatImageDownloadService.Add(item);}SelectedItem = selected;}/// <summary>/// 是否包含/// </summary>/// <param name="userName"></param>/// <returns></returns>public bool Contains(string userName){return ChatList.Any(p => p.UserName == userName);}public void SyncMessage(params WeChatMessage[] messages){var selected = SelectedItem;foreach (var msg in messages){if (msg.IsLoadMoreChats)continue;string chatUserName = msg.IsReceive ? msg.FromUserName : msg.ToUserName;var chat = ChatList.FirstOrDefault(p => p.UserName == chatUserName);if (chat != null){//消息在当前聊天列表中产生ChatList.Move(ChatList.IndexOf(chat), 0);}else{//当前列表没有找到chat = ContactListManager.FindContact(chatUserName);if (chat == null)continue;ChatList.Insert(0, chat);ChatImageDownloadService.Add(chat);}if (msg.MsgType != 51) //51类型消息不插入消息列表{chat.LastMessage = msg.Content?.Trim();chat.LastShortTime = msg.ShortTime;msg.IsRoom = chat.IsRoomContact();if (msg.MsgType != 10000) //如果不是系统消息{if (msg.IsReceive) //只有收到消息需要显示名称{//是收到消息if (msg.IsRoom){//如果是群消息,且不是邀请人进群的消息var member = chat.MemberList.FirstOrDefault(p => msg.Content.StartsWith(p.UserName));if (member == null)continue;msg.Content = msg.Content.Replace(member.UserName + ":<br/>", "");chat.LastMessage = member.ShowName + ":" + msg.Content;msg.FromUserName = member.UserName;msg.FromUserShowName = member.ShowName;}else{//不是群消息则显示对方显示名称msg.FromUserShowName = chat.ShowName;}chat.UnReadCount++;}//处理消息头像msg.Uri = msg.FromUserName.GetIconUrl();MessageImageDownloadService.Add(msg);var last = chat.MessageList.Where(p => p.ShowShortTime).LastOrDefault();if (last == null || (msg.CreateDateTime - last.CreateDateTime).Minutes > 3) //如果新消息之前的消息不存在或者不在三分钟内,则显示时间{msg.ShowShortTime = true;}}chat.MessageList.Add(msg);}}SelectedItem = selected;}public void UpdateInitGroupMember(params WeChatUser[] groupChats){foreach (var item in groupChats){var chat = ChatList.FirstOrDefault(p => p.UserName == item.UserName);if (chat == null) continue;chat.MemberList = item.MemberList;}}public void DelChat(params string[] userNames){foreach (var item in userNames){var user = ChatList.FirstOrDefault(p => p.UserName == item);if (user != null){ChatList.Remove(user);}}}public void ChatWithContact(WeChatUser contact){var chat = ChatList.FirstOrDefault(p => p.UserName == contact.UserName);if(chat == null){chat = contact;ChatList.Insert(0, chat);}else{ChatList.Move(ChatList.IndexOf(chat), 0);}SelectedItem = chat;}public WeChatUser[] SearchRoomChat(string searchText){return ChatList.Where(p => p.IsRoomContact()).Where(p => p.ShowName?.Contains(searchText) == true || p.ShowPinYin?.Contains(searchText) == true).ToArray();}#region INavigationAwarepublic void OnNavigatedTo(NavigationContext navigationContext){navigationContext.NavigationService.Region.RegionManager.RequestNavigate(WeChatClientConst.ContentRegionName, "ChatContentView");}public bool IsNavigationTarget(NavigationContext navigationContext){return true;}public void OnNavigatedFrom(NavigationContext navigationContext){}#endregion}
其他内容也是类似,这里就不一一展示了。







资源获取方式
https://github.com/845669495/WeChatClient