我似乎不能让我的按钮改变颜色,尽管使用Dispatcher从UI线程实例化的线程。
下面是我的简单XAML:<Window x:Class="GOL.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:GOL"
mc:Ignorable="d"
Title="MainWindow">
<Grid Name="GOL">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<!--extra row for START button-->
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
进一步,逻辑如下:
using System;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace GOL
{
public partial class MainWindow : Window
{
private const int LENGTH = 30;
private Thread gameThread;
public MainWindow()
{
gameThread = null;
InitializeComponent();
//Load the game grid:
Button cell;
for (int i = 0; i < LENGTH; ++i) {
for (int j = 0; j < LENGTH; ++j) {
cell = new Button();
cell.Background = Brushes.Black;
cell.Name = "cell_"+((LENGTH * i) + j).ToString();
cell.Click += new RoutedEventHandler(Button_Click);
Grid.SetRow(cell, i);
Grid.SetColumn(cell, j);
//adds children in same order as in name
GOL.Children.Add(cell);
}
}
//finally add start button:
Button start = new Button();
start.Name = "start";
start.Content = "GO!";
start.Click += new RoutedEventHandler(Button_Start);
Grid.SetRow(start, (LENGTH + 1));
Grid.SetColumn(start, (LENGTH/2));
GOL.Children.Add(start);
}
/**
* Changes state of cell
*/
private void Button_Click(object sender, RoutedEventArgs e)
{
Button cell = (Button)sender;
if (cell.Background.Equals(Brushes.Black))
cell.Background = Brushes.White;
else
cell.Background = Brushes.Black;
}
/**
*
*/
private void Button_Start(object sender, RoutedEventArgs e)
{
Button start = (Button)sender;
if (start.Content.Equals("GO!")) {
start.Content = "STOP";
gameThread = new Thread(game);
gameThread.Start();
}
else {
start.Content = "GO!";
try {
gameThread.Abort();
gameThread.Join();
} catch (ThreadAbortException){
;//Assumption: gameThread halted
}
}
}
private void game()
{
int neighbours = 0;
Button cell = null;
while (true)
{
//forAll <i, j> of the board:
for (int i = 0; (i < LENGTH); ++i)
{
for (int j = 0; (j < LENGTH); ++j)
{
//board is owned by UI thread
Dispatcher.Invoke(() => {
//reference cell (i, j)
cell = (Button)GOL.Children[(LENGTH * i) + j];
});
//get neighbour count
neighbours = liveNeighbours(i, j);
//State transition w/r neighbour count:
switch (neighbours) {
case 0:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 1:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 2:
//no change
break;
case 3:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.White
));
break;
case 4:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 5:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 6:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 7:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 8:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
}
}
}
}
}
private int liveNeighbours(int x, int y)
{
int living = 0, location;
Button neighbour = null;
for (int Xoff = -1; (Xoff < 2); ++Xoff) {
for (int Yoff = -1; (Yoff < 2); ++Yoff) {
location = ((x + Xoff) * LENGTH) + y + Yoff;
if ((Xoff == 0) && (Yoff == Xoff))
;//skip if self
else if ((location < 0) || (location > (Math.Pow(LENGTH, 2) - 1)))
;//skip if outside grid
else {
Dispatcher.Invoke(() => {
neighbour = (Button)GOL.Children[location];
if (neighbour.Background.Equals(Brushes.White))
++living;//add to living iff white
});
}
}
}
return living;
}
}
}
主程序参照game()
方法。此外,除了start
按钮外,我将按钮称为单元格。现在请注意,我总是封装对网格GOL
的任何调用,或者它的任何子节点,在Dispatch
操作中,正如其他论坛向我解释的那样。然而,它似乎对UI没有任何影响。
谢谢你的帮助。
您的代码陷入while
循环。因为它是一个无限循环,所以它不能退出循环并阻止您执行任何进一步的操作。
你可以创建一个在UI线程中工作的DispatcherTimer
,并在其中创建你的Game函数。
DispatcherTimer dispatcherTimer = new DispatcherTimer();
你可以通过删除游戏功能中的while(true)
来避免它卡住。您可以将game()
函数挂钩到您创建的DispatcherTimer
标记函数。
private void Button_Start(object sender, RoutedEventArgs e)
{
Button start = (Button)sender;
if (start.Content.Equals("GO!"))
{
start.Content = "STOP";
dispatcherTimer.Start();
dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1);
dispatcherTimer.Tick += DispatcherTimer_Tick;
dispatcherTimer.Start();
}
else
{
start.Content = "GO!";
try
{
dispatcherTimer.Stop();
}
catch (ThreadAbortException)
{
;//Assumption: gameThread halted
}
}
}
private void DispatcherTimer_Tick(object sender, EventArgs e)
{
game();
}
private void game()
{
int neighbours = 0;
Button cell = null;
//forAll <i, j> of the board:
for (int i = 0; (i < LENGTH); ++i)
{
for (int j = 0; (j < LENGTH); ++j)
{
//board is owned by UI thread
Dispatcher.Invoke(() => {
//reference cell (i, j)
cell = (Button)GOL.Children[(LENGTH * i) + j];
});
//get neighbour count
neighbours = liveNeighbours(i, j);
//State transition w/r neighbour count:
switch (neighbours)
{
case 0:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 1:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 2:
//no change
break;
case 3:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.White
));
break;
case 4:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 5:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 6:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 7:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
case 8:
cell.Dispatcher.BeginInvoke((Action)(() =>
cell.Background = Brushes.Black
));
break;
}
}
}
}
当你将以下代码添加到ButtonClick
事件时,在游戏开始前按钮的颜色将不会改变。
if (!dispatcherTimer.IsEnabled) return;