请来围观:WPF开发的微信客户端!!!

本文经原作者授权以原创方式二次分享,欢迎转载、分享。

原文作者:眾尋

原文链接:https://www.cnblogs.com/ZXdeveloper/p/6058206.html


公司的同事离职了,接下来的日子可能会忙碌,能完善DEMO的时间也会少了,因此,把做的简易DEMO整体先记录一下,等后续不断的完善。

参考两位大神的日志:WEB版微信协议部分功能分析[1]、【完全开源】微信客户端.NET版[2]

尤其是周见智[3]大神的DEMO,因为好多和微信的服务端交互,都借鉴了大神的源码,帮助巨大,可以说我相当于做了一个翻版,只是用WPF开发的而已,外观上不同,但是实际交互上是差不多的。

微信分为两个部分,一个是登录,一个是主体,基于此,WPF也主要是这两个窗体来实现。

一、登录模块

1、登录部分分为二维码和获取用户头像两个页面(因为是给予WEB的,所以没有客户端的登录按钮,只能通过扫码来登录)

4ab62eacde669ba2a8854cfd41164d4c.png38b01ad621d34d24ca0caa0272e7a74a.png

在程序启动以后,先通过请求获取到二维码,然后,在启动一个新的线程,不断的循环检索登录状态。

private void LoopLoginCheck()
{object login_result = null;//循环判断手机扫描二维码结果while (true){login_result = ls.LoginCheck();//已扫描 未登录if (login_result is ImageSource){HeadImageSource = login_result as ImageSource;//广播,通知到LoginUC页面,切换Messenger.Default.Send<object>(null, "ShowLoginInfoUC");}//已完成登录if (login_result is string){//访问登录跳转URLls.GetSidUid(login_result as string);//广播,隐藏登录页面,打开主页面Messenger.Default.Send<object>(null, "HideLoginUC");thread.Abort();break;}超时if (login_result is int){//QRCodeImageSource = ls.GetQRCode();//返回二维码页面Messenger.Default.Send<object>(null, "ShowQRCodeUC");}}
}

因为是MVVM,所以,需要用广播来进行操作页面的切换,即填充到登录窗体中间的控件是二维码,还是头像。

2、大家可以看到我上面的截图部分包含了一部分的背景,这个是用Snagit(推荐这个截图工具,很好用)截图时,自动截出的,因为窗体本身的大小就是那么大,多余出来的部分是透明的,用来做二维码滑动出现的效果部分。

c3d009fea9371566fd832bb3abb671a1.gif

当处于二维码状态时划过,则出现动画,头像状态下则没有动画,是设置了Image的Visibility属性来控制的,滑动效果可以看我的另一篇博客微信 二维码鼠标滑动 图像显隐效果[4]

3、当扫码成功,并且在手机端点击登录以后,则跳转到主页面,此处没有加异步等待处理,所以,用户量大的朋友,请耐心等待(后期会加上)。

5595bfa332890378459fbd56dfadf90d.png

登录成功以后,就会出现主窗体和系统托盘,主窗体包含最近联系人和通讯录,系统托盘网上很多解决方案,可以自行查找。

登录成功现在发现了一个问题,就是我有两个微信号,其中一登录以后是有数据的,另一个则没有数据。

5850a1b3e7cc41dd6eb8cfdbe567f065.pnga82c65326e0c1fd22e17e7934aa9b0ac.png

跟踪代码,发现返回的Json是空的,也就是说没有返回值,试验了下周大神的代码,发现也是空的,不清楚什么情况,我同事的有的也是空的,这个一直没有深究,等把功能基本都完善以后再看看问题所在。

3f2d7fd06a8e7426c4677f57c611d848.png

二、主窗体模块

1、主窗体的布局部分很简单,采用了Grid进行分隔,三列,上面的控件如图所示

86067a684e3f72718358d7736a8d4939.png

大部分到没什么,可能大家比较疑惑的是我的聊天窗体为什么是ListBox,这个东西的话,我认为,自己有自己的开发习惯,很多控件都可以实现,panel就可以。

RadioButton的样式是用path画的,可以看我另一篇博客微信聊天和通讯录按钮样式[5]

a4995c6801b4f884044d4f6da102efe0.png

2、聊天列表里,未读的消息上会有带数字的小红点,这个是用Button写的,Item的整体组成是Image(头像)、Button(未读数)、TextBlock(昵称、时间和聊天内容)

<Style x:Key="ListBoxItemChatStyle" TargetType="{x:Type ListBoxItem}"><Setter Property="Template"><Setter.Value><ControlTemplate TargetType="ListBoxItem"><Border><StackPanel x:Name="sp" Orientation="Horizontal" Height="{Binding Converter={StaticResource objectToHeight}}" Background="{Binding Converter={StaticResource objectToColor}}"><Grid><Image Source="{Binding Icon}" Width="40" Height="40" Margin="10"/><Button Foreground="White" Visibility="{Binding UnReadCount,Converter={StaticResource countToVisibility}}" Content="{Binding UnReadCount}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5" Style="{StaticResource CirButtonStyle}"/></Grid><Grid Width="176"><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><TextBlock Grid.Row="0" Text="{Binding ShowName}" FontSize="15" HorizontalAlignment="Left" Margin="5,10,0,0"/><TextBlock Grid.Row="0" Text="{Binding LastTime}" FontSize="15" HorizontalAlignment="Right" Margin="0,10,5,0"/><TextBlock Grid.Row="1" Text="{Binding LastMsg}" FontSize="12" HorizontalAlignment="Left" Margin="5,0,0,0"/></Grid></StackPanel></Border><ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="true"><Setter Property="Background" Value="#FFE2E4E6" TargetName="sp"/></Trigger><Trigger Property="IsSelected" Value="true"><Setter Property="Background" Value="#FFCACDD3" TargetName="sp"/></Trigger></ControlTemplate.Triggers></ControlTemplate></Setter.Value></Setter>
</Style>
d4bd1ca2f65adbcfc18bd19f8771a7a7.png

3、聊天内容部分用的是ScrollingListBox,继承自ListBox,但是重写了里面的OnItemsChanged属性,保证可以时刻滚动到最后一行

public class ScrollingListBox : ListBox
{protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e){if (e.NewItems!=null){int newItemCount = e.NewItems.Count;if (newItemCount > 0)this.ScrollIntoView(e.NewItems[newItemCount - 1]);base.OnItemsChanged(e);}            }
}

样式部分是重写控件模板用的是Image(头像),path(三角部分),textbox(内容部分)

<Style x:Key="ChatListBoxStyle" TargetType="{x:Type ListBox}"><Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/><Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/><Setter Property="BorderThickness" Value="0"/><Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/><Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/><Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/><Setter Property="ScrollViewer.CanContentScroll" Value="true"/><Setter Property="ScrollViewer.PanningMode" Value="Both"/><Setter Property="Stylus.IsFlicksEnabled" Value="False"/><Setter Property="VerticalContentAlignment" Value="Center"/><Setter Property="ItemContainerStyle"><Setter.Value><Style TargetType="ListBoxItem"><Setter Property="Focusable" Value="False"/><Setter Property="Template"><Setter.Value><ControlTemplate><StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" FlowDirection="{Binding FlowDir}" Margin="15,5"><Image Grid.Column="1" Source="{Binding Image}" Height="35" Width="35" VerticalAlignment="Top"/><Path Grid.Column="2" StrokeThickness="1" Stroke="{Binding TbColor}" Data="M12,13 L5,18 L12,23Z" Fill="{Binding TbColor}" Margin="0" SnapsToDevicePixels="True"/><TextBox Grid.Column="3" MaxWidth="355" TextWrapping="Wrap" FontSize="15" BorderBrush="{Binding TbColor}" Background="{Binding TbColor}" IsReadOnly="True" BorderThickness="0" Style="{StaticResource ChatTextBoxStyle}" FlowDirection="LeftToRight" Text="{Binding Message}"/></StackPanel></ControlTemplate></Setter.Value></Setter></Style></Setter.Value></Setter>
</Style>

需要注意的是:此处必须要重写控件模板,而不能重写数据模板,虽然,很多情况下控件模板和数据模板可以得到的效果相同,但是此处,如果写数据模板的话,则自己发的信息不会在右侧,就算设置FlowDirection也没有用,大家可以自行尝试。

4、如果发送内容是空的情况下,则会有一个ToolTip出现,此处的TooLTipye也是重写了样式的Button,好定位,毕竟就算是最大化,位置也是不变的。

abe13356ab97748634b32d63e22f5e44.png

通讯录部分,和聊天列表差不多,不过,由于需要进行分组,也就是A、B……这种组合,所以用的Object类型,在点选过程中,通过is来进行判别是不是WeChatUser,如果是,则进行转换,来进一步处理。

大家可以看到上面那个好友是 同程旅游顾问<span …… 其实它是一个emoji,只是现在我还没有做到那一部分,如果做到的话,则进行转换,如果谁有好的emoji处理方式希望告知,谢谢了。

9362930928d2c1bcf13ee860b6d2efde.png5c5764e9fcec0a8508097fbceaa5a99f.png

当点选列表以后,并且转换成功的情况下,则显示出用户的信息,通过内容是否未空,来判别是否要显示

<Grid Grid.Row="1" Grid.RowSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding ElementName=rb_friend,Path=IsChecked,Converter={StaticResource boolToVisibility}}" Margin="0,50,0,0"><Grid.RowDefinitions><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><Image Source="{Binding FriendInfo.Icon}" Grid.Row="0" Height="124" Width="124" HorizontalAlignment="Center"/><StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center"><TextBlock Text="{Binding FriendInfo.NickName}" FontSize="30" Foreground="Black" FontWeight="Bold"/><Image Visibility="{Binding FriendInfo.Sex,Converter={StaticResource parameterToVisibility},ConverterParameter=2}" Source="/Image/female.png"/><Image Visibility="{Binding FriendInfo.Sex,Converter={StaticResource parameterToVisibility},ConverterParameter=1}" Source="/Image/male.png"/></StackPanel><TextBlock Text="{Binding FriendInfo.Signature}" Foreground="#FF919191" Grid.Row="2" HorizontalAlignment="Center"/><StackPanel Orientation="Horizontal" Visibility="{Binding FriendInfo.RemarkName,Converter={StaticResource epmtyToVisibility}}" Margin="10" Grid.Row="3" HorizontalAlignment="Center"><TextBlock Text="备  注" Margin="0,0,10,0" FontSize="15"/><TextBlock Text="{Binding FriendInfo.RemarkName}" FontSize="15"/></StackPanel><StackPanel Orientation="Horizontal" Visibility="{Binding FriendInfo.Province,Converter={StaticResource epmtyToVisibility}}" Margin="10" Grid.Row="4" HorizontalAlignment="Center"><TextBlock Text="地区" Margin="0,0,10,0" FontSize="15"/><TextBlock Text="{Binding FriendInfo.Province}" Margin="0,0,2,0" FontSize="15"/><TextBlock Text="{Binding FriendInfo.City}" FontSize="15"/></StackPanel><Button Content="发消息" Width="166" Height="37" Grid.Row="5" Command="{Binding FriendSendComamnd}" Margin="0,50,0,0" Style="{StaticResource FriSendButtonStyle}"/><Grid Grid.Row="0" Grid.RowSpan="7" Background="WhiteSmoke" Visibility="{Binding FriendInfo,Converter={StaticResource nullToVisibility}}"/>
</Grid>

点击发消息按钮,则跳转回聊天页面,然后,将当前的好友加入到聊天的第一项。

三、总结

做WPF微信DEMO,用到了转换器,转换颜色,转换显隐;重写了控件的样式,例如Button、RadioButton、ListBox;然后MVVM模式下,Bind的用法,感觉这个DEOM对于初学者来说应该会有很大的帮助。

不过这个DEMO的BUG和不完善的地方还有很多,例如系统托盘还没有做闪烁,现在只能发送文字,最大化的问题。

系统托盘闪烁可以用Timer和Opacity来进行控制,比如来未读消息了,则在进行时间间隔的控制显隐。

后期会把TextBox换成RichTextBox,这样可以发送图片和emoji。

最大化问题,是我一直还没有想到好的解决办法,最大化的情况下会占据整个屏幕,而不把状态栏空出来,网上的办法都是重新设置Width和Height,但是这样的话,就要记录原来的大小和位置,一直没有找到可以重写WindowState.Maximized的方法,好像是不能重写,所以比较纠结,希望哪位大神看完我的代码以后,能够给提供一下解决思路,谢谢了。

参考资料

[1]

WEB版微信协议部分功能分析: http://blog.csdn.net/wonxxx/article/details/51787041

[2]

【完全开源】微信客户端.NET版: http://www.cnblogs.com/xiaozhi_5638/p/4923811.html

[3]

周见智: http://home.cnblogs.com/u/xiaozhi_5638/

[4]

微信 二维码鼠标滑动 图像显隐效果: http://www.cnblogs.com/ZXdeveloper/p/6054278.html

[5]

微信聊天和通讯录按钮样式: http://www.cnblogs.com/ZXdeveloper/p/5997372.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/287205.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ios-新浪微博-下拉刷新获取最新的消息(解决消息重复的问题)(五)

2019独角兽企业重金招聘Python工程师标准>>> 第一步 在上一篇博文的基础上&#xff0c;利用新浪提供的since_id进行判断&#xff0c;在刷新监听的方法中&#xff0c;引入下面的代码 结果如下图 转载于:https://my.oschina.net/iOSliuhui/blog/520495

《看聊天记录都学不会Python到游戏实战?太菜了吧》(7)我用函数写了个特洛伊木马

本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖了新手在学习中的一般问题。此系列将会持续更新&#xff0c;包括别的语言以及实战都将使用对话的方式进行教学&#xff0c;基础编程语言教学适用于零基础小白&#xff0c;之后实战课程也将会逐步更新。 若…

linux修改windows注册表,妙招:让修改的注册表立即生效的几种方法

建站学院(LieHuo.Net)Windows文档Windows操作系统是全球最广泛&#xff0c;使用者最多的软件&#xff0c;熟悉Windows软件成了电脑操作者必不可少的功课&#xff0c;注册表作为“Windows的神经系统”非常重要&#xff0c;相信很多朋友都非常熟悉注册&#xff0c;在开始&#xf…

Android之通过用户名和密码连接指定wifi热点(兼容Android9.0和Android10.0和addNetwork(wifiNewConfiguration)返回-1问题)

1 需求 通过用户名和密码连接指定wifi热点,网上的代码乱七八糟,没几个可以用,我这边整理了下,测试了华为Android9.0和小米Android9.0和10.0和OPPO Android9.0 都没问题,直接回调结果就行。 2 问题 在Android10.0手机上 mWifiManager.addNetwork(wifiNewConfiguration);…

WPF怎么做新手引导界面?

本文经原作者授权以原创方式二次分享&#xff0c;欢迎转载、分享。原文作者&#xff1a;眾尋原文链接&#xff1a;https://www.cnblogs.com/ZXdeveloper/p/8391864.html这两天不忙&#xff0c;所以&#xff0c;做了一个简易的新手引导小Demo。因为&#xff0c;不是项目上应用&a…

《看聊天记录都学不会C语言?太菜了吧》(19)巩固开始,数字1、2、3、4能够组成多少个 3 位数的不同的排列

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

【经典回放】多种语言系列数据结构算法:串(C版)

我们这里说的串、就是标准的C语言的串,这点,和我们教材中另行定义的串并不一致。我们这里强调仅仅是按C语言的标准处理串,是因为你会按C语言的标准构造串、而不是按其它的模式定义的。在我们的教材上,串相当与一个: struct ElemType {char *str; }; 构造的顺序表、或者是…

可以只读的 ServiceCollection

可以只读的 ServiceCollectionIntro在 .NET 7 Preview 4 中&#xff0c;ServiceCollection 可以声明为只读了&#xff0c;这使得我们可以有效避免在构建了 ServiceProvider 之后再新增服务&#xff0c;导致服务注册失败。Sample在新的版本中&#xff0c;ServiceCollection 新增…

阿里云与中国联通首个公共云平台上线

11月8日&#xff0c;阿里云与中国联通关于公共云合作的首个项目&#xff1a;浙江联通“沃云Powered by Alibaba cloud” 平台&#xff08;cloud.10010zj.com.cn&#xff09;正式发布上线。该平台将以沃云品牌为客户提供“阿里云联通”服务&#xff0c;结合阿里云技术及浙江联通…

IBM X System ServerGuide 8.41 服务器 系统安装 引导盘

IBM X System ServerGuide 8.41 支持操作系统: 32位: Microsoft Windows 2003/2003 R2 (Enterprise, Standard, Web and DataCenter UV) Microsoft Small Business Server 2003/2003 R2 (Standard/Premium Edition) Microsoft Windows 2008 (Enterprise, Standard, Web and Dat…

Matlab R2018b简体中文版完整安装图文教程(附安装包下载)

目 录 一、安装过程 二、下载地址 matlab R2018b安装包如下所示,包括主程序和补丁文件两部分。 一、安装过程 双击安装包中的setup.exe。 选择使用文件安装MIYAO,点击下一步。

《看聊天记录都学不会C语言?太菜了吧》(20)(必懂!题解)我能知道2000年后的这个月一共有几天

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

linux nginx线程池,nginx使用线程池提升9倍性能

众所周知nginx使用异步&#xff0c;事件驱动方法处理连接。这意味着nginx使用一个worker进程处理多个连接和请求&#xff0c;而不是每一个请求有一个专门的进程或着线程处理(像传统架构的服务器那样&#xff0c;例如apache)。为了实现这个目的&#xff0c;nginx使用非阻塞模式的…

Android之解决androidx.appcompat.widget.Toolbar去掉左边距

1 问题 androidx.appcompat.widget.Toolbar默认左边有间距 2 解决办法 在Toolbar下面添加如下熟悉 app:contentInsetLeft"0dp"app:contentInsetStart"0dp"app:contentInsetEnd"0dp"app:maxButtonHeight"20dp"app:titleMargin"0…

C# 配置日志记录

在 .NET Core 中&#xff0c;可以给配置文件使用提供程序&#xff0c;例如从 JSON 或 XML文件、环境变量或命令行参数中读取配置。只需要从 NuGet 包 Microsoft.ExtensionsConfiguration 中创建一个ConfigurationBuilder&#xff0c;并向此构建器添加提供程序。要添加 JSON 提供…

使用“using” 的 “Cursor”

很多时候&#xff0c;我们会写下面的这段代码&#xff1a; private void button1_Click(object sender, EventArgs e) {Cursor cursor Cursor.Current;this.Cursor Cursors.WaitCursor;LongTimeMethod();this.Cursor cursor; }private void LongTimeMethod() {for (int i 0…

《看聊天记录都学不会C语言?太菜了吧》(21)(必懂!题解)在现实生活中,打擂台比赛争名次竟用的是冒泡排序?

若是大一学子或者是真心想学习刚入门的小伙伴可以私聊我&#xff0c;若你是真心学习可以送你书籍&#xff0c;指导你学习&#xff0c;给予你目标方向的学习路线&#xff0c;无套路&#xff0c;博客为证。 本系列文章将会以通俗易懂的对话方式进行教学&#xff0c;对话中将涵盖…

ArcGIS 10.7如何获取矢量多边形所有折点的坐标(平面坐标和经纬度坐标)?

Descriptions:As shown in the figure below is a surface pattern spot, how to obtain the coordinates of each vertex (plane coordinates and latitude and longitude coordinates)? The results we want are as follows: Via data source,we get the information that…

Mahout分类算法学习之实现Naive Bayes分类示例

1.简介 (1) 贝叶斯分类器的分类原理发源于古典概率理论&#xff0c;是通过某对象的先验概率&#xff0c;利用贝叶斯公式计算出其后验概率&#xff0c;即该对象属于某一类的概率&#xff0c;选择具有最大后验概率的类作为该对象所属的类。朴素贝叶斯分类器(Naive Bayes Classifi…

一维条形码***技术(Badbarcode)

【转】http://future-sec.com/badbarcode.html 前言在日常生活中&#xff0c;条形码随处可见&#xff0c;特别在超市&#xff0c;便利店&#xff0c;物流业&#xff0c;但你们扫的条形码真的安全吗&#xff1f;之前TK教主在PacSec介绍的条形码攻击和twitter上的demo视频太炫酷&…