c# WPF如何实现滚动显示的TextBlock

  在我们使用TextBlock进行数据显示时,经常会遇到这样一种情况就是TextBlock的文字内容太多,如果全部显示的话会占据大量的界面,这是我们就会只让其显示一部分,另外的一部分就让其随着时间的推移去滚动进行显示,但是WPF默认提供的TextBlock是不具备这种功能的,那么怎么去实现呢?

  其实个人认为思路还是比较清楚的,就是自己定义一个UserControl,然后将WPF简单的元素进行组合,最终实现一个自定义控件,所以我们顺着这个思路就很容易去实现了,我们知道Canvas这个控件可以通过设置Left、Top、Right、Bottom属性去精确控制其子控件的位置,那么很显然我们需要这一控件,另外我们在Canvas容器里面再放置TextBlock控件,并且设置TextWrapping="Wrap"让其全部显示所有的文字,当然这里面既然要让其滚动,那么TextBlock的高度肯定会超过Canvas的高度,这样才有意义,另外一个重要的部分就是设置Canvas的ClipToBounds="True"这个属性,这样超过的部分就不会显示,具体的实现思路参照代码我再一步步去认真分析!

  1 新建一个UserControl,命名为RollingTextBlock。

<UserControl x:Class="TestRoilingTextBlock.RoilingTextBlock"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
             mc:Ignorable="d" d:DesignWidth="300" Height="136" Width="400">
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <Border BorderBrush="Gray"
                    BorderThickness="1"
                    Padding="2"
                    Background="Gray">
                <Canvas x:Name="innerCanvas"
                        Width="Auto"
                        Height="Auto"
                        Background="AliceBlue"
                        ClipToBounds="True">
                    <TextBlock x:Name="textBlock"
                               Width="{Binding ActualWidth,ElementName=innerCanvas}" 
                               TextAlignment="Center"
                               TextWrapping="Wrap"
                               Height="Auto"
                               ClipToBounds="True"
                               Canvas.Left="{Binding Left,Mode=TwoWay}"
                               Canvas.Top="{Binding Top,Mode=TwoWay}"
                               FontSize="{Binding FontSize,Mode=TwoWay}"
                               Text="{Binding Text,Mode=TwoWay}"
                               Foreground="{Binding Foreground,Mode=TwoWay}">
 
                    </TextBlock>
                </Canvas>
 
            </Border>
        </ControlTemplate>
    </UserControl.Template>
</UserControl>

  这里分析几个重要的知识点:A:DataContext="{Binding RelativeSource={RelativeSource Self}}" 这个为当前的前台绑定数据源,这个是第一步,同时也是基础。B 为当前的TextBlock绑定Text、Canvas.Left、Canvas.Top以及Width等属性,当然这些属性要结合自己的需要去绑定,并在后台定义相关的依赖项属性。

然后再看看后台的逻辑代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
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;
using System.Windows.Threading;
 
namespace TestRoilingTextBlock
{
    /// <summary>
    /// RoilingTextBlock.xaml 的交互逻辑
    /// </summary>
    public partial class RoilingTextBlock : UserControl
    {
        private bool   canRoll = false;
        private double rollingInterval = 16;//每一步的偏移量
        private double offset=6;//最大的偏移量
        private TextBlock currentTextBlock = null;       
        private DispatcherTimer currentTimer = null;
        public RoilingTextBlock()
        {
            InitializeComponent();
            Loaded += RoilingTextBlock_Loaded;
        }
 
        void RoilingTextBlock_Loaded(object sender, RoutedEventArgs e)
        {
            if (this.currentTextBlock != null)
            {
                canRoll = this.currentTextBlock.ActualHeight > this.ActualHeight;
            }
            currentTimer = new System.Windows.Threading.DispatcherTimer();
            currentTimer.Interval = new TimeSpan(0, 0, 1);
            currentTimer.Tick += new EventHandler(currentTimer_Tick);
            currentTimer.Start();
        }
 
        public override void OnApplyTemplate()
        {
            try
            {
                base.OnApplyTemplate();
                currentTextBlock = this.GetTemplateChild("textBlock") as TextBlock;
            }
            catch (Exception ex)
            {               
               
            }
              
        }
 
        void currentTimer_Tick(object sender, EventArgs e)
        {
            if (this.currentTextBlock != null && canRoll)
            {
                if (Math.Abs(Top) <= this.currentTextBlock.ActualHeight-offset)
                {
                    Top-=rollingInterval;
                }
                else
                {
                    Top = this.ActualHeight;
                }
 
            }
        }
 
        #region Dependency Properties
        public static DependencyProperty TextProperty =
           DependencyProperty.Register("Text", typeof(string), typeof(RoilingTextBlock),
           new PropertyMetadata(""));
 
        public static DependencyProperty FontSizeProperty =
            DependencyProperty.Register("FontSize", typeof(double), typeof(RoilingTextBlock),
            new PropertyMetadata(14D));       
 
        public static readonly DependencyProperty ForegroundProperty =
           DependencyProperty.Register("Foreground", typeof(Brush), typeof(RoilingTextBlock), new FrameworkPropertyMetadata(Brushes.Green));
 
        public static DependencyProperty LeftProperty =
           DependencyProperty.Register("Left", typeof(double), typeof(RoilingTextBlock),new PropertyMetadata(0D));
 
        public static DependencyProperty TopProperty =
           DependencyProperty.Register("Top", typeof(double), typeof(RoilingTextBlock),new PropertyMetadata(0D));
     
        #endregion
 
        #region Public Variables
        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }
 
        public double FontSize
        {
            get { return (double)GetValue(FontSizeProperty); }
            set { SetValue(FontSizeProperty, value); }
        }
 
        public Brush Foreground
        {
            get { return (Brush)GetValue(ForegroundProperty); }
            set { SetValue(ForegroundProperty, value); }
        }
 
        public double Left
        {
            get { return (double)GetValue(LeftProperty); }
            set { SetValue(LeftProperty, value); }
        }
 
        public double Top
        {
            get { return (double)GetValue(TopProperty); }
            set { SetValue(TopProperty, value); }
        }
        #endregion
    }
}

  再看后台的代码,这里我们只是通过一个定时器每隔1秒钟去更新TextBlock在Canvas中的位置,这里面有一个知识点需要注意,如何获取当前TextBlock的ActualHeight,我们可以通过重写基类的OnApplyTemplate这个方法来获取,另外这个方法还是存在前台和后台的耦合,是否可以通过绑定来获取TextBlock的ActualHeight,如果通过绑定应该注意些什么?这其中需要特别注意的是ActualHeight表示的是元素重绘制后的尺寸,并且是只读的,也就是说其始终是真实值,在绑定时是无法为依赖性属性增加Set的,并且在绑定时绑定的模式只能够是Mode=“OneWayToSource”而不是默认的Mode=“TwoWay”。

  另外在使用定时器时为什么使用System.Windows.Threading.DispatcherTimer而不是System.Timers.Timer?这个需要我们去认真分析原因,只有这样才能真正地去学会WPF。

  当然本文只是提供一种简单的思路,后面还有很多可以扩展的地方,比如每次移动的距离如何确定,移动的速率是多少?这个如果做丰富,是有很多的内容,这个需要根据具体的项目需要去扩展,这里只是提供最简单的一种方式,仅仅提供一种思路。

  2 如何引用当前的自定义RollingTextBlock?

<Window x:Class="TestRoilingTextBlock.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestRoilingTextBlock"
        Title="MainWindow" Height="550" Width="525">
    <Grid>
        <local:RoilingTextBlock Foreground="Teal"
                                Text="汉皇重色思倾国,御宇多年求不得。杨家有女初长成,养在深闺人未识。天生丽质难自弃,一朝选在君王侧。回眸一笑百媚生,六宫粉黛无颜色。春寒赐浴华清池,温泉水滑洗凝脂。
                                侍儿扶起娇无力,始是新承恩泽时。云鬓花颜金步摇,芙蓉帐暖度春宵。春宵苦短日高起,从此君王不早朝。"
                                FontSize="22">           
        </local:RoilingTextBlock>
 
    </Grid>
</Window>

  3 最后来看看最终的效果,当然数据是处于不断滚动状态,这里仅仅贴出一张图片。

以上就是c# WPF如何实现滚动显示的TextBlock的详细内容,更多关于WPF实现滚动显示的TextBlock的资料请关注我们其它相关文章!

(0)

相关推荐

  • C# WPF 通过委托实现多窗口间的传值的方法

    在使用WPF开发的时候就不免会遇到需要两个窗口间进行传值操作,当然多窗口间传值的方法有很多种,本文介绍的是使用委托实现多窗口间的传值. 在上代码之前呢,先简单介绍一下什么是C#中的委托(如果只想了解如何传值可以略过这部分)在网络上有很多对于委托的介绍和讲解,经过我的学习和总结加上了一点我自己的理解,我认为委托是一种类似于C语言的指针,但是它指向的是方法而不是变量.如果把委托看作一个变量,那么这个变量里存着的就是你目标方法的地址,调用委托约等于调用你的目标方法.(个人理解欢迎指正交流) 以下正文:

  • 在C# WPF下自定义滚动条ScrollViewer样式的操作

    一.实现对ScrollViewer样式的自定义主要包括: 1.滚动条宽度设置 2.滚动条颜色 3.滚动条圆角 4.滚动条拉动时的效果mouseover 二.实现效果: 三.实现方法 1.创建资源字典( ResourceDictionary)文件 由于style代码比较多,之间在控件文件中加载style比较混乱,也不利于其它窗口复用,这里单独创建了ScrollViewDictionary.xaml文件代码如下: <ResourceDictionary xmlns="http://schema

  • c# WPF实现Windows资源管理器(附源码)

      今天我来写一篇关于利用WPF来实现Windows的资源管理器功能,当然只是局部实现这个功能,因为在很多时候我们需要来实现对本机资源的管理,当然我们可以使用OpenFileDialog dialog = new OpenFileDialog()这个Microsoft.Win32命名空间下的这个类来实现一些资源查找和导入的功能,但是在很多时候我们可能需要更多的功能,并且希望能够集成到我们自己的项目中,但是我们这个时候就不得不自己来写一套来集成到我们的软件中去了,因为OpenFileDialog这

  • C# WPF 父控件通过使用可视化树找到子控件的示例代码

    在我们使用WPF设计前台界面时,经常会重写数据模板,或者把控件放到数据模板里.但是一旦将控件放到数据模板中,在后台就没有办法通过控件的名字来获取它了,更没办法对它进行操作(例如,隐藏,改变控件的某个值). 如果你是比我还白的小白,对我刚刚陈述的东西不清楚,接下来我简单说一下什么是把控件放在数据模板中,怎么样的情况没法后台通过名字来获取控件,如果读者对于数据模板这些事儿已经清楚了,或者只关心如何使用可视化树可以将这部分跳过哈. 先上代码介绍一下什么是数据模板以WPF中ListBox控件为例: <L

  • C# WPF使用AForge类库操作USB摄像头拍照并保存

    项目中用到 USB 摄像头,需要根据情况进行图像抓拍,查了半天资料,比较多的是使用 WPFMediaKit 和 AForge . 但是由于项目要求不显示 USB 摄像头拍摄的画面,最终确定使用 AForge 解决. 下面用一个测试程序记录一下. 一.无预览拍照 首先建立一个 WPF 项目,我的就叫 AForgeTest,你们随意就好: 然后在 NuGet 包管理器中安装 AForge 库: 我只安装了图中打勾的几个库,这个根据自己项目需要安装就好. 不过用 USB 摄像头拍照必须安装: AFor

  • C#中WPF ListView绑定数据的实例详解

    C#中WPF ListView绑定数据的实例详解 WPF中ListView用来显示数据十分方便, 我们可以将它分成几个列,每一个列用来显示一条数据,但是又是在一方之中. 那么怎样实现这样的效果的呢,这就要用绑定了. 我们先来看一看他的xmal代码 <ListView Name="receiveList" Grid.Row="0"> <ListView.View> <GridView> <GridView.Columns>

  • c# WPF中System.Windows.Interactivity的使用

    背景 在我们进行WPF开发应用程序的时候不可避免的要使用到事件,很多时候没有严格按照MVVM模式进行开发的时候习惯直接在xaml中定义事件,然后再在对应的.cs文件中直接写事件的处理过程,这种处理方式写起来非常简单而且不用过多地处理考虑代码之间是否符合规范,但是我们在写代码的时候如果完全按照WPF规范的MVVM模式进行开发的时候就应该将相应的事件处理写在ViewModel层,这样整个代码才更加符合规范而且层次也更加清楚,更加符合MVVM规范. 常规用法 1 引入命名空间 通过在代码中引入Syst

  • c# WPF中如何自定义MarkupExtension

    在介绍这一篇文章之前,我们首先来回顾一下WPF中的一些基础的概念,首先当然是XAML了,XAML全称是Extensible Application Markup Language (可扩展应用程序标记语言),是专门用于WPF技术中的UI设计语言,通过使用XAML语言,我们能够快速设计软件界面,同时能够通过绑定这种机制能够很好地实现界面和实现逻辑之间的解耦,这个就是MVVM模式的核心了,那么今天我们介绍的MarkupExtension和XAML之间又有哪些的关系呢? Markup Extensio

  • C# WPF上位机实现和下位机TCP通讯的方法

    下位机使用北京大华程控电源DH1766-1,上位机使用WPF.实现了电压电流实时采集,曲线显示.上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟.昨天写的TCP服务端正好排上用场. 界面如下: 服务端 服务端实在上篇基础上实现的.需要做如下更改: while (true) { try { byte[] bufferDate = new byte[1024]; int realLen = pSocket.Receive(bufferDate); if (realLen <= 0) { t

  • C#中WPF依赖属性的正确学习方法

    前言 我在学习WPF的早期,对依赖属性理解一直都非常的不到位,其恶果就是,我每次在写依赖属性的时候,需要翻过去的代码来复制黏贴. 相信很多朋友有着和我相同的经历,所以这篇文章希望能帮助到那些刚刚开始学依赖属性的朋友. 那些[讨厌]的依赖属性的讲解文章 初学者肯定会面临一件事,就是百度,谷歌,或者MSDN来查看依赖属性的定义和使用,而这些文章虽然都写的很好,但,那是相对于已经学会使用依赖属性的朋友而言. 而对于初学者而言,说是误导都不过分. 比如,官网的这篇文章https://docs.micro

  • C# wpf Brush转Hex字符串的实例代码

    我就废话不多说了,大家还是直接看代码吧~ //from MaterialDesignDemo.Converters public class BrushToHexConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null) return null; string lowerH

  • C# WPF 建立无边框(标题栏)的登录窗口的示例

    前言:笔者最近用c#写WPF做了一个项目,此前未曾做过完整的WPF项目,算是一边学一边用,网上搜了不少资料,效率当然是不敢恭维的,有时会在一些很简单的问题上纠结很长时间,血与泪的教训可不少. 不过,正如电视剧某榜里的一句话:既然我活了下来,就不会白白活着!笔者怎么也算挣扎过了,有些经验与教训可以分享,趁着记忆深刻总结写下来.希望后来者少走弯路,提高工作效率.如果有写得不好的地方,希望读者能够指正,一起进步! --------------------------------- 今天先从登录窗口说起

随机推荐