腾讯从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 制作聊天窗口获取历史聊天记录的更多相关文章

  1. WPF制作的小型笔记本

    WPF制作的小型笔记本-仿有道云笔记 楼主所在的公司不允许下载外部资源, 不允许私自安装应用程序, 平时记录东西都是用记事本,时间久了很难找到以前记的东西. 平时在家都用有道笔记, 因此就模仿着做了一 ...

  2. XMPP系列(四)---发送和接收文字消息,获取历史消息功能

    今天开始做到最主要的功能发送和接收消息.获取本地历史数据. 先上到目前为止的效果图:              首先是要在XMPPFramework.h中引入数据存储模块: //聊天记录模块的导入 # ...

  3. python量化之路:获取历史某一时刻沪深上市公司股票代码及上市时间

    最近开始玩股票量化,由于想要做完整的股票回测,因此股票的上市和退市信息就必不可少.因为我们回测的时候必须要知道某一日期沪深股票的成分包含哪些对吧.所以我们要把沪深全部股票的上市时间.退市时间全部都爬下 ...

  4. WPF编程,获取句柄将外部程序嵌入到WPF界面。

    原文:WPF编程,获取句柄将外部程序嵌入到WPF界面. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/details ...

  5. VS编程,WPF中,获取鼠标相对于当前屏幕坐标的一种方法

    原文:VS编程,WPF中,获取鼠标相对于当前屏幕坐标的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/det ...

  6. VS编程,WPF中,获取鼠标相对于当前程序窗口的坐标的一种方法

    原文:VS编程,WPF中,获取鼠标相对于当前程序窗口的坐标的一种方法 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/article/ ...

  7. WPF制作表示透明区域的马赛克画刷

    最近在用WPF制作一款软件,需要像ps一样表示透明区域,于是制作了一个马赛克背景的style.实现比较简单,那么过程和思路就不表了,直接上代码 <DrawingBrush TileMode=&q ...

  8. WPF制作的小时钟

    原文:WPF制作的小时钟 周末无事, 看到WEB QQ上的小时钟挺可爱的, 于是寻思着用WPF模仿着做一个. 先看下WEB QQ的图: 打开VS, 开始动工. 建立好项目后, 面对一个空荡荡的页面, ...

  9. WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事!

    原文:WPF制作Logo,很爽,今后在应用程序中加入Logo轻松,省事! 这是效果: XAML代码:<Viewbox Width="723.955078" Height=&q ...

随机推荐

  1. 国内较快的maven镜像

    原文网址:http://www.cnblogs.com/dingyingsi/p/3856456.html 国内连接maven官方的仓库更新依赖库,网速一般很慢,收集一些国内快速的maven仓库镜像以 ...

  2. 2015GitWebRTC编译实录15

    各个库编译完成后,整合talkapp,联编时还是碰到了一些问题,主要是lib里的源码文件被错误移走,或者宏定义等有问题的,不一而足 FQ访问https://apprtc.appspot.com/,拿到 ...

  3. 当pip install不能正确安装的时候,try easy_install

    当pip install不能正确安装的时候,try easy_install 重复试了几次pip install -r requirements.txt,都在安装pillow的时候失败了,想找这个枕头 ...

  4. ubuntu 使用wine卸载软件

    现在的网络应用许多被windows绑架了,有时候不得不下载一些.exe的的东西在ubuntu下载配合一下,但是后来我想卸载这些.exe应用程序,于是百度了一下,一位博主写的好,如下,晒出来: cd ~ ...

  5. c++标准模板库algorithm头文件中accumulate算法的代码

    template <typename T>T algorithm(T* start, T* end, T total)//把[start, end)标记范围内所有元素累加到total中{  ...

  6. Python的regex模块——更强大的正则表达式引擎

    Python自带了正则表达式引擎(内置的re模块),但是不支持一些高级特性,比如下面这几个: 固化分组    Atomic grouping 占有优先量词    Possessive quantifi ...

  7. ajax+php处理案例

    <div> <table> <tr> <th>状态</th> <th>信息</th> </tr> < ...

  8. log4net.config

    <?xml version="1.0" encoding="UTF-8"?> <log4net> <root> <le ...

  9. sp_change_users_login解决孤立用户问题

    孤立帐户,指的是某个数据库的帐户只有用户名而没有登录名,这样的用户在用户库的sysusers系统表中存在,而在master数据库的syslogins中却没有对应的记录. 孤立帐户的产生一般是一下两种: ...

  10. SQL Server 2008 数据库镜像部署实例之三 配置见证服务器

    SQL Server 2008 数据库镜像部署实例之三 配置见证服务器 前面已经完成了镜像数据库的配置,并进行那个了故障转移测试.接下来将部署见证服务器,实现自动故障转移. 一.关于见证服务器 1.若 ...