C# 如何在WPF中闪烁RichTextBox边框和背景色

C# 如何在WPF中闪烁RichTextBox边框和背景色,c#,wpf,windows,C#,Wpf,Windows,看起来这应该是相当简单的,就像在Winforms中一样,但我对WPF比较陌生,所以仍然试图改变对数据和UI交互方式的思考 场景:用户单击我的主窗体上的按钮。该按钮用于输入街道地址。在street address表单中,当用户单击submit按钮时,我会执行一些基本数据验证。Submit()遍历每个数据输入字段,并调用下面的方法,尝试向用户警告有问题的数据字段 以下是我所拥有的代码,它不做任何我能检测到的事情: private void FlashTextBox(RichTextBox b

看起来这应该是相当简单的,就像在Winforms中一样,但我对WPF比较陌生,所以仍然试图改变对数据和UI交互方式的思考

场景:用户单击我的主窗体上的按钮。该按钮用于输入街道地址。在street address表单中,当用户单击submit按钮时,我会执行一些基本数据验证。Submit()遍历每个数据输入字段,并调用下面的方法,尝试向用户警告有问题的数据字段

以下是我所拥有的代码,它不做任何我能检测到的事情:

    private void FlashTextBox(RichTextBox box)
    {
        var currentBorderColor = box.BorderBrush;
        var currentBackgroundColor = box.Background;

        Task.Factory.StartNew(() =>
        {
            for (int x = 0; x < 5; x++)
            {
                this.Dispatcher.Invoke(() =>
                {

                    box.Background = Brushes.Red;
                    box.BorderBrush = Brushes.IndianRed;
                    box.InvalidateVisual();

                    System.Threading.Thread.Sleep(100);

                    box.BorderBrush = currentBorderColor;
                    box.Background = currentBackgroundColor;
                    box.InvalidateVisual();

                    System.Threading.Thread.Sleep(100);
                });
            }
        });
    }
private void FlashTextBox(RichTextBox)
{
var currentBorderColor=box.BorderBrush;
var currentBackgroundColor=box.Background;
Task.Factory.StartNew(()=>
{
对于(int x=0;x<5;x++)
{
this.Dispatcher.Invoke(()=>
{
box.Background=画笔.Red;
box.BorderBrush=brush.IndianRed;
box.InvalidateVisual();
系统线程线程睡眠(100);
box.BorderBrush=currentBorderColor;
box.Background=currentBackgroundColor;
box.InvalidateVisual();
系统线程线程睡眠(100);
});
}
});
}
正如我所指出的,您的代码的主要问题是您阻止了UI线程。因此,当您将感兴趣的属性更改为循环中的新值时,实际的UI永远没有机会更新视觉表示,即屏幕上的内容

具有讽刺意味的是,尽管您注意到“这似乎应该相当简单,因为它在Winforms中是如此”,但如果您尝试在Winforms程序中编写相同的代码,您可能会遇到完全相同的问题。Winforms和WPF(事实上,大多数GUI API)都有完全相同的限制:有一个线程处理所有UI,在您更改一个或多个应该影响UI外观的数据值后,您必须将控制权返回给调用您的UI线程,以便它可以更新屏幕

现在,您还注意到您正在“尝试改变对数据和UI交互方式的思考”。这是一件好事,如果您愿意花时间学习WPF设计的MVVM概念,这将非常有帮助。Winforms还有一个数据绑定模型,事实上,您可以在Winforms中编写与WPF非常相似的代码。但是,WPF的“保留”图形模型与Winform的“即时”模型不同,即WPF跟踪图形的外观,而Winform要求您在每次屏幕需要更新时自己绘制图形,这更适合于数据绑定方法,WPF的整个设计就是基于此

这意味着您应该努力将数据保存在数据所在的位置,将UI保存在UI所在的位置。也就是说,数据在您的代码后面,UI在XAML中。这在两种API中都是一个好主意,但如果不能使用WPF,则会牺牲更多

那你的问题还剩下什么?好吧,如果没有一个好的解决方案,很难知道你的代码是什么样子的,那么最好的解决方法是什么。因此,我将提供几个示例,希望在重新定向代码以更好地适应WPF范式之后,您可以根据自己的需要应用一个。(不幸的是,我不太喜欢WPF的一点是,在某些方面它太强大了,提供了许多不同的方法来实现相同的结果;这有时会让我很难知道什么是最好的方法。)

这两个示例的不同之处在于它们需要多少代码。第一种方法将动画逻辑作为视图模型的一部分放入C代码中。一方面,这可以说不是“WPF方式”。但是第二个,使用视图代码(即XAML)来定义动画,需要在视图的代码后面添加一点额外的管道,这让我有点不舒服,因为它模糊了视图和视图模型之间的界限,这比我希望的要多

哦,好吧

以下是第一种方法的视图模型类:

类视图模型:NotifyPropertyChangedBase
{
私有字符串_文本;
公共字符串文本
{
获取=>\u文本;
set=>\u UpdateField(ref\u text,value);
}
私人住宅区;
公共图书馆
{
get=>\u isHighlighted;
set=>\u UpdateField(ref\u ishighlight,value);
}
私人住宅是一种娱乐;
公共场所的污染
{
get=>\u正在进行动画化;
set=>\u UpdateField(ref是动画,value,onisanaimatingchanged);
}
私有void\u OnIsAnimatingChanged(bool oldValue)
{
_toggleIsHighlightedCommand.RaiseCanExecuteChanged();
_animateIsHighlightedCommand.RaiseCanExecuteChanged();
}
私有只读DelegateCommand _toggleIsHighlightedCommand;
私有只读DelegateCommand_animateIsHighlightedCommand;
public ICommand ToggleIsHighlightedCommand=>\u ToggleIsHighlightedCommand;
公共ICommand AnimateIsHighlightedCommand=>\u AnimateIsHighlightedCommand;
公共视图模型()
{
_toggleIsHighlightedCommand=新的DelegateCommand(()=>IsHighlighted=!IsHighlighted,()=>!IsAnimating);
_animateIsHighlightedCommand=新的DelegateCommand(()=>_flashishightlighted(此),()=>!正在动画化);
}
专用静态异步void\u闪光灯亮起(ViewModel ViewModel)
{
viewModel.IsAnimating=true;
对于(int i=0;i<10;i++)
{
viewModel.IsHighlighted=!viewModel.IsHighlighted;
等待任务。延迟(200);
}
viewModel.IsAnimating=false;
}
}
类NotifyPropertyChangedBase:INotifyPropertyChanged
{
公共事件属性更改