我在wpf中有一个8行8列的网格:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Loaded="Window_Loaded"
Title="MainWindow" Height="560" Width="800">
<Grid x:Name="MyGrid" ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</Window>
后面的代码是:
public partial class MainWindow : Window
{
private const int MaxRow = 8;
private const int MaxCol = 8;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Start()
{
for (int i = 0; i < MaxRow ; i++)
{
for (int j = 0; j < MaxCol ; j++)
{
string current = $"ImgR{i}C{j}";
object currentImg = this.FindName(current);
if (currentImg?.GetType() == typeof(Image))
{
var img = ((Image)currentImg);
Thread.Sleep(1500);
img.Visibility = Visibility.Visible;
DoEvents();
Thread.Sleep(1500);
img.Visibility = Visibility.Hidden;
DoEvents();
}
}
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var pngImage = new BitmapImage(new Uri(@"C:\Test\cross.png", UriKind.Absolute));
for (int i = 0; i < MaxRow ; i++)
{
for (int j = 0; j < MaxCol; j++)
{
var img = new Image
{
Source = pngImage,
Name = $"ImgR{i}C{j}",
Visibility = Visibility.Hidden
};
Grid.SetRow(img, i);
Grid.SetColumn(img, j);
MyGrid.Children.Add(img);
RegisterName($"ImgR{i}C{j}", img);
}
}
Start();
}
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
}
因此,我的想法是我动态创建8x8
图像并进行注册。然后在两个循环中更改其可见性。因此,结果是图像交叉穿过8x8
网格
程序似乎正确地执行了该操作,但是过渡有时并不顺利,有时,我的意思是,十字架改变了它的可见性,但偶尔(程序通常运行良好)没有显示。
我想问题是当我使用以下方法更新Ui时
public static void DoEvents()
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
是否有更好的方法来执行此操作,或者有时无法显示交叉的问题。
答案 0 :(得分:2)
WPF GUI更新是在GUI线程中完成的,但是您正在使用Thread.Sleep
窗口事件处理程序(该函数本身称为Start函数)中的Loaded
来锁定该线程。通过GUI线程。我可以看到您正在尝试使用DoEvents
函数,但这不是更新GUI线程的可靠方法(您已经开始使用GUI线程了,所以您依赖于某些未知的框架内部行为来强制进行更新。
并发编程不是一件容易的事,我建议您先阅读它,然后再继续。对于初学者,您永远不要致电Thread.Sleep()
。线程在C#中已过时,并已由异步编程取代(异步编程可能在内部使用或可能不在内部使用线程,但这通常与应用程序开发人员无关)。您需要在此处执行的操作是将Start函数修改为异步的,例如:
private async Task Start()
{
for (int i = 0; i < MaxRow; i++)
{
for (int j = 0; j < MaxCol; j++)
{
string current = $"ImgR{i}C{j}";
object currentImg = this.FindName(current);
if (currentImg?.GetType() == typeof(Image))
{
var img = ((Image)currentImg);
await Task.Delay(TimeSpan.FromMilliseconds(1500));
Application.Current.Dispatcher.Invoke(() =>
{
img.Visibility = Visibility.Visible;
});
await Task.Delay(TimeSpan.FromMilliseconds(1500));
Application.Current.Dispatcher.Invoke(() =>
{
img.Visibility = Visibility.Hidden;
});
}
}
}
}
然后在您已加载的函数中,使用以下命令启动任务:
private CancellationTokenSource CancelSource;
...
this.CancelSource = new CancellationTokenSource();
Task.Run(Start, this.CancelSource.Token);
如果您需要取消任务,则使用取消令牌源,例如如果用户关闭窗口:
this.CancelSource.Cancel();
答案 1 :(得分:2)
我尝试了代码,对我来说我什至看不到十字架,实际上是窗户挂了。这就是我的期望。
您正在UI线程中调用Start
方法,并在其中运行循环或放置Thread.Sleep。这两个操作都处于阻止状态,即它们将使用UI线程中的资源,并且窗口将挂起。
要解决此问题,您应该在后台方法/任务中启动该方法。以下应该工作。与其直接调用Start
,而是尝试以下方法:
Task.Run(() => Start());
我也不了解您的Do Events方法,而且由于您现在整个Start方法都在后台,因此您必须确保自己处理跨线程操作。您还必须添加适当的异常处理。
private void Start()
{
for (int i = 0; i < MaxRow; i++)
{
for (int j = 0; j < MaxCol; j++)
{
string current = $"ImgR{i}C{j}";
object currentImg = Application.Current.Dispatcher.Invoke(() => this.FindName(current));
if (currentImg?.GetType() == typeof(Image))
{
var img = ((Image) currentImg);
Thread.Sleep(100);
Application.Current.Dispatcher.Invoke(() => img.Visibility = Visibility.Visible);
//DoEvents();
//Thread.Sleep(100);
//Application.Current.Dispatcher.Invoke(() => img.Visibility = Visibility.Visible);
//DoEvents();
}
}
}
}