我在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 { }));
}
有没有更好的方法来做到这一点,或者什么可能是交叉有时没有显示的问题。
我尝试了代码,对我来说,我甚至看不到十字架,实际上窗口挂起了。这是我所期待的。
您正在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();
}
}
}
}
WPF GUI更新在GUI线程中完成,但是您在Start函数中使用Thread.Sleep
锁定该线程,该函数由窗口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();