我尝试创建一个数独解析器。因此,在业务逻辑中,我们有一个代表数独的游戏板。在业务逻辑中,我们使用一种执行回溯处理的方法来解决数独问题。
我在 Form1 中的通知更改方法中遇到以下错误.cs: tableLayoutPanel1.Controls.Add(label, column, line);
线程间操作 没有从创建它的线程以外的线程访问的有效控件
数独听众.cs :
interface SudokuListener
{
void NotifyChange(Int32 line, Int32 column, Token token);
}
表格1 :
public partial class Form1 : Form, SudokuListener
{
delegate void Del(SudokuListener sudokuListener);
void SudokuListener.NotifyChange(int line, int column, Token token)
{
Label label = new Label();
if (token.IsMarked())
{
label.ForeColor = Color.Blue;
}
label.Text = ""+token.GetNbr();
tableLayoutPanel1.Controls.Add(label, column, line);
}
private void Form1_Load(object sender, EventArgs e)
{
Del del = new Del(SudokuThread.Start);
BackgroundWorker backgroundWorker = new BackgroundWorker();
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.DoWork += new DoWorkEventHandler(brol);
backgroundWorker.RunWorkerAsync();
}
private void brol(object sender, DoWorkEventArgs e)
{
SudokuThread.Start(this);
}
}
数独线程.cs :
class SudokuThread
{
public static void Start(SudokuListener listener)
{
SudokuGameBoard sudokuGameBoard = new SudokuGameBoard();
Sudoku sudoku = new Sudoku();
sudokuGameBoard.AddListeners(listener);
sudoku.Load(sudokuGameBoard);
sudoku.FindSolution(sudokuGameBoard, 0);
}
}
数独.cs :
class Sudoku
{
public void Load(SudokuGameBoard gameBoard)
{
gameBoard.SetToken(0, 0, new Token(0, false));
gameBoard.SetToken(0, 1, new Token(0, false));
gameBoard.SetToken(0, 2, new Token(0, false));
gameBoard.SetToken(0, 3, new Token(0, false));
gameBoard.SetToken(0, 4, new Token(0, false));
gameBoard.SetToken(0, 5, new Token(0, false));
gameBoard.SetToken(0, 6, new Token(0, false));
gameBoard.SetToken(0, 7, new Token(0, false));
gameBoard.SetToken(0, 8, new Token(0, false));
gameBoard.SetToken(1, 0, new Token(0, false));
gameBoard.SetToken(1, 1, new Token(0, false));
gameBoard.SetToken(1, 2, new Token(7, true));
gameBoard.SetToken(1, 3, new Token(8, true));
//rest of the code
}
private void PutToken(SudokuGameBoard gameBoard, Int32 line, Int32 column, Token token)
{
if ( !gameBoard.GetToken(line,column).IsMarked())
{
gameBoard.SetToken(line, column, token);
}
}
public Boolean FindSolution(SudokuGameBoard gameBoard, Int32 position)
{
Boolean finish = false;
Int32 currentNbr = 0;
do {
currentNbr++;
if ( IsPossible(gameBoard, position / 9, position % 9, currentNbr) )
{
PutToken(gameBoard, position / 9, position % 9, new Token(currentNbr));
//rest of the code
}
}while (currentNbr != 9 && !finish);
}
}
数独游戏板.cs :
class SudokuGameBoard
{
public void SetToken(Int32 line, Int32 column, Token newToken)
{
array[line, column] = newToken;
NotifyListeners(line, column, newToken);
}
private void NotifyListeners(Int32 line, Int32 column, Token token)
{
for (Int32 i = 0; i < listeners.Count; i++)
{
listeners[i].NotifyChange(line, column, token);
}
}
}
导致错误本身是因为无法从非 UI 线程修改 UI。这不是 .NET 或 Windows 的限制。没有窗口系统允许后台线程修改 UI。
您的代码也过于复杂,使用充当事件的委托和后台工作者。BGW 已经过时,因为它所做的任何事情都可以通过任务和用于报告的 IProgress 接口轻松完成。任务允许您轻松执行多个后台操作,或在后台操作后返回到 UI 线程。对于BGW来说,这两件事都非常困难。
您的代码可以像以下代码一样简单:
报告讨论区状态的类
class BoardStatus
{
public int Line {get;}
public int Column {get;}
public Token Token {get;}
public BoardStatus(int line, int column,Token token)
{
Line=line;
Column=column;
this.Token=token;
}
}
以及以下代码的格式:
void UpdateStatus(BoardStatus status)
{
var color = status.token.IsMarked() ? Color.Blue : Color.Black;
var label = new Label
{
Text = token.GetNbr().ToString(),
ForeColor = color;
};
tableLayoutPanel1.Controls.Add(label, status.column, status.line);
}
public async void Start_Click(object sender, EventArgs args)
{
TotalStatusLabel.Text="Starting";
var progress=new Progress<TokenProgress>(UpdateStatus);
//Run in the background without blocking
await Task.Run(()=>Solve(progress));
//We are back in the UI
TotalStatusLabel.Text="Finished";
}
private void Solve(IProgress<TokenStatus> progress)
{
SudokuGameBoard sudokuGameBoard = new SudokuGameBoard(progress);
Sudoku sudoku = new Sudoku();
sudoku.Load(sudokuGameBoard);
sudoku.FindSolution(sudokuGameBoard, 0);
}
随着董事会通过 IProgress 界面报告进度:
class SudokuGameBoard
{
IProgress<BoardStatus> _progress;
public SudokuGameBoard(IProgress<BoardStatus> progress)
{
_progress=progress;
}
public void SetToken(Int32 line, Int32 column, Token newToken)
{
array[line, column] = newToken;
_progress.Report(new BoardStatus(line, column, newToken));
}
}