C# wpf 获取控件刷新的时机

文章目录


前言

做wpf开发不像mfc控件的刷新相对同步,wpf的控件改变宽高或者可见性后在下一行代码不会立刻看到效果,而且也没有一种事件可以获取确切的界面刷新后的时机。如果只是进行延迟等待,在不同机器效果是不同的,而且也并没有具体标识说明已刷新界面,这就导致某些场景的功能实现起来会比较困难。本文提供一种获取控件真正界面刷新时机的方法。


一、为何要获取刷新时机?

例子一、隐藏控件后截屏

需要隐藏控件后截屏,直接修改Visibility 后截屏是不行的,控件不会立刻隐藏,所以会截到控件的画面。如果不能获取到真正的刷新时机,那就这能延时等待,但延时是不稳定的,至少笔者就遇到了延时500毫秒再截屏,依然在生产环境有客户遇到了截到控件画面的bug。

例子二、修改控件大小后做计算

有一段逻辑需要根据控件大小做计算,使用的是ActualWidth、ActualHeight。在这段逻辑前刚好有修改大小的代码,但此时修改Width、Height之后ActualWidth、ActualHeight不会立刻改变,需要等到界面刷新时会改变,这就会导致计算数值不正确。这种场景很可能难以使用延时。


二、如何实现?

1.使用动画

wpf的动画有个完成事件,我们通过这个事件就可以获取绘制完一帧的时机。

csharp 复制代码
//创建
var da = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromMilliseconds(1)), FillBehavior.Stop);
da.Completed += (S, E) {
//动画完成,此处表明控件已刷新
};
//启动动画触发绘制
elememt.BeginAnimation(Control.OpacityProperty, da);

2.使用TaskCompletionSource

使用TaskCompletionSource是为了提供异步接口,方便使用,在使用时只需要await即可。

csharp 复制代码
 var tcs = new TaskCompletionSource();
 da.Completed += (S, E) => tcs.SetResult();
 await tcs.Task;

三、完整代码

csharp 复制代码
public static class ElementExtensions
{
    /// <summary>
    /// 等待控件真实刷新后
    /// </summary>
    internal static Task WaitForPaint(this UIElement elememt)
    {
        var tcs = new TaskCompletionSource();
        //通过动画来获取这个时机
        var da = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromMilliseconds(1)), FillBehavior.Stop);
        da.Completed += (S, E) => tcs.SetResult();
        //启动动画触发绘制
        elememt.BeginAnimation(Control.OpacityProperty, da);
        return tcs.Task;
    }
}

四、使用示例

1、隐藏工具条截屏

csharp 复制代码
private async void Button_Click(object sender, RoutedEventArgs e)
{   
    //隐藏工具条
    toolBar.Visibility= Visibility.Collapsed;
    //等待界面上真正隐藏
    await toolBar.WaitForPaint();
    //截屏
    略
    //显示工具条
    toolBar.Visibility= Visibility.Visible;
}

2、修改宽高后获取ActualWidth、ActualHeight

csharp 复制代码
private async void Button_Click(object sender, RoutedEventArgs e)
{
    var button = sender as Button;
    Console.WriteLine("ActualWidth=" + button.ActualWidth + "   ActualHeight=" + button.ActualHeight);
    button.Width = 100;
    button.Height = 100;
    Console.WriteLine("ActualWidth=" + button.ActualWidth + "   ActualHeight=" + button.ActualHeight);
    await button.WaitForPaint();
    Console.WriteLine("ActualWidth=" + button.ActualWidth + "   ActualHeight=" + button.ActualHeight);
}

效果预览


总结

以上就是今天要讲的内容,本文的做法是笔者曾经在某个论坛看到的建议,然后去尝试实现发现是可行的,后来进一步封装为异步方法。发出来的主要目的是作为记录,一方面是方便以后拷贝使用,另一方面也希望能对大家有所帮助。

相关推荐
Theodore_10223 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
----云烟----5 小时前
QT中QString类的各种使用
开发语言·qt
lsx2024065 小时前
SQL SELECT 语句:基础与进阶应用
开发语言
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
向宇it5 小时前
【unity小技巧】unity 什么是反射?反射的作用?反射的使用场景?反射的缺点?常用的反射操作?反射常见示例
开发语言·游戏·unity·c#·游戏引擎
武子康5 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
九鼎科技-Leo6 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
转世成为计算机大神6 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
宅小海6 小时前
scala String
大数据·开发语言·scala
qq_327342736 小时前
Java实现离线身份证号码OCR识别
java·开发语言