WPF 制作聊天窗口获取历史聊天记录
腾讯从QQ2013版起开始在聊天记录里添加了历史记录查看功能,个人聊天窗口可以点击最上边的‘查看历史消息’,而群组里的未读消息可以通过滚动鼠标中键或者拖动滚动条加载更多消息,那这个用wpf怎么实现呢?
我用Scrollviewer和RichTextBox做了一个简陋尝试,真的是太陋了,大家戴好眼镜了哈。现在开始:
首先是前台的陋XAML:
- <Window x:Class="testFlowDocument.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- Title="MainWindow" Height="" Width="" Loaded="Window_Loaded">
- <Grid>
- <ScrollViewer x:Name="sv_richtextbox" Background="Transparent" PreviewMouseLeftButtonUp="sv_richtextbox_PreviewMouseLeftButtonUp"
- PreviewMouseWheel="sv_richtextbox_PreviewMouseWheel" VerticalScrollBarVisibility="Auto" ScrollChanged="sv_richtextbox_ScrollChanged">
- <RichTextBox IsReadOnly="True" x:Name="RichTextBoxMessageHistory" BorderBrush="#B7D9ED"
- Margin="3,3,3,0" Background="Silver" />
- </ScrollViewer>
- <Button Name="previousadd" Content="前加" Height="" Width="" VerticalAlignment="Bottom" HorizontalAlignment="Left" Click="previousadd_Click"></Button>
- <Button Name="clearadd" Content="清空" Height="" Width="" VerticalAlignment="Bottom" Click="clearadd_Click"></Button>
- <Button Name="add20" Content="加20条" Height="" Width="" VerticalAlignment="Bottom" Margin="0,0,110,0" HorizontalAlignment="Right" Click="add20_Click"></Button>
- <Button Name="lastadd" Content="后加" Height="" Width="" VerticalAlignment="Bottom" HorizontalAlignment="Right" Click="lastadd_Click"></Button>
- </Grid>
- </Window>
在基本布局里添加了一个Scrollviewer包含RichTextBox,另外添加了4个Button控件来添加简单数据。previousadd往最上端插入数据,lastadd从底部添加数据。add20快速添加20条数据使之出现滚动条。
好了,下面是后台陋CS实现:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Windows;
- using System.Windows.Controls;
- using System.Windows.Data;
- using System.Windows.Documents;
- using System.Windows.Input;
- using System.Windows.Media;
- using System.Windows.Media.Imaging;
- using System.Windows.Navigation;
- using System.Windows.Shapes;
- namespace testFlowDocument
- {
- /// <summary>
- /// MainWindow.xaml 的交互逻辑
- /// </summary>
- public partial class MainWindow : Window
- {
- public MainWindow()
- {
- InitializeComponent();
- }
- int i = ;
- private void previousadd_Click(object sender, RoutedEventArgs e)
- {
- addmessage();
- }
- /// <summary>
- /// 插入数据
- /// </summary>
- void addmessage(int pagesize)
- {
- for (int j = ; j < pagesize; j++)
- {
- i++;
- vScrollposition = sv_richtextbox.ExtentHeight;
- Paragraph pggethistoryNo = new Paragraph();
- pggethistoryNo.Background = Brushes.LightBlue;
- pggethistoryNo.Margin = new Thickness(, , , );
- TextBlock tblockgethistoryNo = new TextBlock();
- tblockgethistoryNo.Text = i.ToString();
- tblockgethistoryNo.Foreground = Brushes.Black;
- pggethistoryNo.Inlines.Add(tblockgethistoryNo);
- if (RichTextBoxMessageHistory.Document.Blocks != null && RichTextBoxMessageHistory.Document.Blocks.Count > )
- {//判断是否存在数据了
- RichTextBoxMessageHistory.Document.Blocks.InsertBefore(RichTextBoxMessageHistory.Document.Blocks.FirstBlock, pggethistoryNo);
- }
- else
- {//若不存在,第一条要加入而非插入
- RichTextBoxMessageHistory.Document.Blocks.Add(pggethistoryNo);
- }
- isEnd = false;
- }
- }
- bool isEnd = false;//是否滚动到底部
- double vScrollposition = ;//当前接收到的所有文本内容高度(包括历史消息)
- private void sv_richtextbox_ScrollChanged(object sender, ScrollChangedEventArgs e)
- {
- if (e.ViewportHeightChange > )
- {
- if (isEnd == true)
- {//判断是否是从底部添加数据
- if (sv_richtextbox.ScrollableHeight == sv_richtextbox.VerticalOffset)
- {//判断滚动条是否在最底部
- sv_richtextbox.ScrollToEnd();
- }
- }
- else
- {//定位到上次位置
- double changevScrollHeight = sv_richtextbox.ExtentHeight - vScrollposition;
- if (changevScrollHeight > )
- {
- sv_richtextbox.ScrollToVerticalOffset(e.ViewportHeightChange + changevScrollHeight);
- return;
- }
- sv_richtextbox.ScrollToVerticalOffset(e.ViewportHeightChange);
- return;
- }
- }
- }
- private void lastadd_Click(object sender, RoutedEventArgs e)
- {
- i++;
- Paragraph pggethistoryNo = new Paragraph();
- pggethistoryNo.Background = Brushes.LightGreen;
- pggethistoryNo.Margin = new Thickness(, , , );
- TextBlock tblockgethistoryNo = new TextBlock();
- tblockgethistoryNo.Text = i.ToString();
- tblockgethistoryNo.Foreground = Brushes.Black;
- pggethistoryNo.Inlines.Add(tblockgethistoryNo);
- RichTextBoxMessageHistory.Document.Blocks.Add(pggethistoryNo);
- isEnd = true;
- }
- private void clearadd_Click(object sender, RoutedEventArgs e)
- {
- RichTextBoxMessageHistory.Document.Blocks.Clear();
- }
- private void Window_Loaded(object sender, RoutedEventArgs e)
- {
- RichTextBoxMessageHistory.Document.Blocks.Clear();
- }
- private void add20_Click(object sender, RoutedEventArgs e)
- {
- addmessage();
- }
- private void sv_richtextbox_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
- {
- if (e.Delta > )
- {
- isAddMessage();
- }
- }
- private void sv_richtextbox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
- {
- isAddMessage();
- }
- void isAddMessage()
- {
- double offi = sv_richtextbox.VerticalOffset;
- if (offi == )
- {
- double Maxinum = sv_richtextbox.ScrollableHeight;
- if (Maxinum == )
- return;
- vScrollposition = sv_richtextbox.ExtentHeight;
- addmessage();
- RichTextBoxMessageHistory.Focus();
- }
- }
- }
- }
向RichTextBox控件追加内容,可以用Document.Blocks.Add(Block item)方法。
而向RichTextBox插入内容,用的是Document.Blocks.InsertBefore(Block nextSibling, Block newItem)方法,其中nextSibling指的是将要被插入的位置,newItem指的是将要插入的新内容。而获取历史聊天记录后,我们可以用此方法往最上端插入数据。所以,此处我们可以写作 RichTextBoxMessageHistory.Document.Blocks.InsertBefore(RichTextBoxMessageHistory.Document.Blocks.FirstBlock, pggethistoryNo);其中pggethistoryNo是新定义的内容;
其实今天的主角是‘拖动滚动条和滚动鼠标键加载数据’,而幕后的英雄是ScrollChanged事件。当我们拖动滚动条和滚动鼠标键加载出新数据时,会有一个滚动条定位的问题,有人说收到新消息时应该跳到新消息处虽新的聊天自动往下滚动,即总在最底端;有人说当你正在看历史消息时如果突然来了一条消息就跳到最底端那还得再重新找刚才的位置,让人很抓狂;还有人说当拖动加载出新消息时如果滚动条呆在新加载出内容的顶端,还得再去手动找刚才读到的位置也是一件烦人眼珠子的事。能不能做一件完美的事情同时满足三者呢?有时候猜不到结局就勇敢的去做吧~~
1、自动滚到最底部: sv_richtextbox.ScrollToEnd();
2,3、定位在某位置: sv_richtextbox.ScrollToVerticalOffset(double offset);
如何判断是从最上边插入的还是从最下边添加的呢?我们设置了参数isEnd来判断,true表示滚到最下端。如何判断添加新消息时滚动条是否在最下边呢?用sv_richtextbox.ScrollableHeight == sv_richtextbox.VerticalOffset判断。当滚动条有变化(位置或大小)时ScrollChanged事件会捕获到,我们就在该事件里做判断。
需要特别注意:很多人说自己在Scrollviewer中鼠标事件无效,提醒一下,在Scrollviewer控件中捕获不到MouseUp等事件,但可以捕获到PreviewMouseUp等事件。
附两张陋图:
本文博客园地址:http://www.cnblogs.com/jying/p/3223431.html
到此为止,我要说的说完了,谢谢大家捧场。。
WPF 制作聊天窗口获取历史聊天记录的更多相关文章
- WPF制作的小型笔记本
WPF制作的小型笔记本-仿有道云笔记 楼主所在的公司不允许下载外部资源, 不允许私自安装应用程序, 平时记录东西都是用记事本,时间久了很难找到以前记的东西. 平时在家都用有道笔记, 因此就模仿着做了一 ...
- XMPP系列(四)---发送和接收文字消息,获取历史消息功能
今天开始做到最主要的功能发送和接收消息.获取本地历史数据. 先上到目前为止的效果图: 首先是要在XMPPFramework.h中引入数据存储模块: //聊天记录模块的导入 # ...
- python量化之路:获取历史某一时刻沪深上市公司股票代码及上市时间
最近开始玩股票量化,由于想要做完整的股票回测,因此股票的上市和退市信息就必不可少.因为我们回测的时候必须要知道某一日期沪深股票的成分包含哪些对吧.所以我们要把沪深全部股票的上市时间.退市时间全部都爬下 ...
- WPF编程,获取句柄将外部程序嵌入到WPF界面。
原文:WPF编程,获取句柄将外部程序嵌入到WPF界面. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details ...
- VS编程,WPF中,获取鼠标相对于当前屏幕坐标的一种方法
原文:VS编程,WPF中,获取鼠标相对于当前屏幕坐标的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/det ...
- VS编程,WPF中,获取鼠标相对于当前程序窗口的坐标的一种方法
原文:VS编程,WPF中,获取鼠标相对于当前程序窗口的坐标的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/ ...
- WPF制作表示透明区域的马赛克画刷
最近在用WPF制作一款软件,需要像ps一样表示透明区域,于是制作了一个马赛克背景的style.实现比较简单,那么过程和思路就不表了,直接上代码 <DrawingBrush TileMode=&q ...
- WPF制作的小时钟
原文:WPF制作的小时钟 周末无事, 看到WEB QQ上的小时钟挺可爱的, 于是寻思着用WPF模仿着做一个. 先看下WEB QQ的图: 打开VS, 开始动工. 建立好项目后, 面对一个空荡荡的页面, ...
- WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事!
原文:WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事! 这是效果: XAML代码:<Viewbox Width="723.955078" Height=&q ...
随机推荐
- Java-->xml的pull解析
--> pull解析器是android内置的解析器,解析原理与sax类似 --> xml文件student.xml: <?xml version="1.0" en ...
- 以ls命令为实例介绍命令基本格式
登陆Linux命令行会显示一行字符,例如[root@localhost ~ ]#, 其中root表示当前登陆用户,localhost表示主机名,~显示的是当前路径,(-表示当前用户的家目录),#表示 ...
- HDU 3966 Aragorn's Story
题意:给一棵树,并给定各个点权的值,然后有3种操作:I C1 C2 K: 把C1与C2的路径上的所有点权值加上KD C1 C2 K:把C1与C2的路径上的所有点权值减去KQ C:查询节点编号为C的权值 ...
- AndroidStudio 中的坑Error:(1, 0) Plugin is too old, please update to a more recent version, or set ANDROID_DAILY_OVERRID
将 build.gradle 中 的 classpath改为2.0.+ dependencies { classpath 'com.android.tools.build:gradle:2.0.+'然 ...
- QPS/QPS/PV/UV/服务器数量/并发数/吐吞量/响应时间计算公式
QPS:每秒查询率(Query Per Second) ,每秒的响应请求数,也即是最大吞吐能力.QPS = req/sec = 请求数/秒QPS统计方式 [一般使用 http_load 进行统计]QP ...
- CentOS中Device eth0 does not seem to be present错误解决办法
今天克隆的虚拟机,当需要多台虚拟机的时候,试用克隆真是方便,不过遇到了 Device eth0 does not seem to be present 的问题,在网上找到遇到同样问题的解决方法, 很顺 ...
- knockout 学习实例7 foreach
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- [zz] be similar with和be similar to的区别
http://wenda.tianya.cn/question/4cb13da080ee34c9 be similar to后边既可以加物主代词又可以加人,即:be similar to sth/sb ...
- SVM之SMO最小序列
转载自:JerryLead http://www.cnblogs.com/jerrylead/archive/2011/03/18/1988419.html 11 SMO优化算法(Sequential ...
- Hadoop on Yarn 各组件详细原理
运行在独立的节点上的ResourceManager和NodeManager一起组成了yarn的核心,构建了整个平台.ApplicationMaster和相应的container一起组成了一个Yarn的 ...