腾讯从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. Centos 安装mysql5.7

    1. 从mysql的官网下载mysql57-community-release-el6-8.noarch.rpm 2. 安装第一步下载的rpm rpm -ivh mysql57-community-r ...

  2. 无需输入密码的scp/ssh/rsync操作方法

    一般使用scp/ssh/rsync传输文件时,都需要输入密码.下面是免密码传输文件的方法. 假设要在两台主机之间传送文件,host_src & host_dst.host_src是文件源地址所 ...

  3. Oracle学习系列5

    Oracle学习系列5 ************************************************************************************ ,掌握 ...

  4. Jenkins Job 自杀 groovy

    下面的groovy可以加在post groovy script里面在job跑完的时候自杀(把本Job删掉) suicide_url="http://[USER]:[PASSWORD]@[JE ...

  5. css固定姓名显示长度,排列更整齐

    white-space: nowrap 不换形 overflow: hidden 隐藏长度超出部分 text-overflow:ellipsis  文字长度超出用省略号代替 <p style=& ...

  6. 【转】Linux CentOS内核编译:下载CentOS源码、编译2.6.32-220的错误(apic.c:819 error 'numi_watchdog' undeclared)

    一.下载CentOS源码 1.1 查看CentOS版本 cat /etc/issue 1.2 查看Linux内核版本 uname -r 1.3 下载 文件名:kernel-2.6.32-220.el6 ...

  7. [转]Windows系统中监控文件复制操作的几种方式

    1. ICopyHook 作用: 监视文件夹和打印机移动,删除, 重命名, 复制操作. 可以得到源和目标文件名. 可以控制拒绝操作. 缺点: 不能对文件进行控制. 只对Shell文件操作有效, 对原生 ...

  8. css3立体旋转

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. ADF_Controller系列2_绑定TasksFlow、Region和Routers(Part2)

    2015-02-14 Created By BaoXinjian

  10. centos修改hostname以及时间同步

    centos修改hostname 方法一: 执行命令:hostname test 则修改hostname为test 方法二: 永久修改hostname vi /etc/sysconfig/networ ...