暂停程序以允许用户在 wpf 中进行交互
本文关键字:wpf 交互 用户 程序 许用户 暂停 | 更新日期: 2023-09-27 18:34:12
好的,所以这是一种通用程序。我正在尝试编写一个扑克程序,用户可以在其中与计算机玩家进行交互。但是,为了简化它,我正在使用一个更简单的程序进行测试,其中两个玩家试图从计算机中猜测一个数字。一个是人类玩家,另一个是电脑玩家。
在游戏中,玩家轮流猜测程序设置的数字。计算机播放器只是随机化数字,其中用户应该输入一个数字并按下猜测按钮(或可以选择退出(。
我的问题是,当轮到人类玩家采取行动时,我如何暂停游戏的执行,以便人类通过按下按钮或输入文本来做出回应?
我尝试使用 Timer 并启动它并等待线程结束,但似乎这仍然冻结了 gui。
我还尝试从后台工作线程发送消息以更新 gui,但这给了我一个错误,即无法从另一个线程访问 gui 元素。
有人对如何实现这一点有任何建议吗?
请注意,这不是一个完整的程序,因为我这样做只是为了了解如何在执行过程中与 gui 交互。
数字猜测器类:
public class NumberGuesser {
public Player HumanPlayer { get; private set; }
public Player ComputerPlayer { get; private set;}
private Player[] _players = new Player[2];
private int _maxNumber = 20;
private bool _done = false;
private Random random;
public NumberGuesser() {
HumanPlayer = new HumanPlayer("Me");
ComputerPlayer = new ComputerPlayer("HAL9000");
_players[0] = HumanPlayer;
_players[1] = ComputerPlayer;
random = new Random((int) DateTime.Now.Ticks);
}
public void Play() {
var myNumber = random.Next(_maxNumber);
var index = 0;
while (!_done) {
var currentPlayer = _players[index];
var guess = currentPlayer.Act(_maxNumber);
if (guess == myNumber) {
currentPlayer.Score++;
}
index = (1 + index)%2;
}
}
}
电脑播放器:
public class ComputerPlayer : Player {
private readonly Random _random = new Random((int)DateTime.Now.Ticks);
public ComputerPlayer(string name) : base(name) {
}
public override int Act(int max) {
return _random.Next(max);
}
}
人类玩家:
public class HumanPlayer : Player {
public HumanPlayer(string name) : base(name) {
}
public override int Act(int max) {
return UserGuess();
}
private int UserGuess() {
return 0;
}
}
玩家类别:
public class Player {
public string Name { get; private set; }
public int Score { get; set; }
public Player(string name) {
Name = name;
}
public virtual int Act(int max) {
// How should the human enter the guess here???
return 0;
}
}
我的主窗口:
<Window x:Class="GuessMyNumber.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="395" Width="728">
<Grid>
<Label Content="Guess my number!" Height="28" HorizontalAlignment="Left" Margin="122,58,0,0" Name="label1" VerticalAlignment="Top" />
<Grid Height="174" HorizontalAlignment="Left" Margin="31,170,0,0" Name="grid1" VerticalAlignment="Top" Width="272" DataContext="{Binding Path=HumanPlayer}">
<Label Content="Human player" Height="28" HorizontalAlignment="Left" Margin="6,6,0,0" Name="label2" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="55,92,0,0" Name="textBox1" VerticalAlignment="Top" Width="120"
Text="{Binding Path=HumanGuess, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Guess" Height="23" HorizontalAlignment="Left" Margin="79,130,0,0" Name="button1" VerticalAlignment="Top" Width="75"
Command="{Binding Path=GuessCommand}"/>
<Label Content="{Binding Path=Name}" Height="28" HorizontalAlignment="Left" Margin="96,6,0,0" Name="label3" VerticalAlignment="Top" />
<Label Content="Score" Height="28" HorizontalAlignment="Left" Margin="6,40,0,0" Name="label4" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="55,42,0,0" Name="textBox2" VerticalAlignment="Top" Width="75" Text="{Binding Path=Score, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
<Button Content="Quit" Height="23" HorizontalAlignment="Left" Margin="182,130,0,0" Name="buttonQuit" VerticalAlignment="Top" Width="75" Command="{Binding Path=QuitCommand}" />
</Grid>
<Grid Height="174" HorizontalAlignment="Left" Margin="328,170,0,0" Name="grid2" VerticalAlignment="Top" Width="270" DataContext="{Binding Path=ComputerPlayer}" >
<Label Content="Computer player" Height="28" HorizontalAlignment="Left" Margin="6,6,0,0" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="55,92,0,0" VerticalAlignment="Top" Width="120" Text="{Binding Path=HumanGuess, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="{Binding Path=Name}" Height="28" HorizontalAlignment="Left" Margin="111,6,0,0" VerticalAlignment="Top" />
<Label Content="Score" Height="28" HorizontalAlignment="Left" Margin="6,40,0,0" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="55,42,0,0" VerticalAlignment="Top" Width="75" Text="{Binding Path=Score, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</Grid>
<Button Content="Start Playing" Height="23" HorizontalAlignment="Left" Margin="254,59,0,0" Name="buttonStartPlay" VerticalAlignment="Top" Width="75" Click="buttonStartPlay_Click" />
</Grid>
以及背后的代码:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
private readonly NumberGuesser _numberGuesser;
public MainWindow() {
InitializeComponent();
_numberGuesser = new NumberGuesser();
DataContext = _numberGuesser;
}
private void buttonStartPlay_Click(object sender, RoutedEventArgs e) {
_numberGuesser.Play();
}
}
你必须从不同的角度来处理这个问题。 而不是有一个同步运行的方法要求用户采取行动,就像你可能与本地计算机交互一样,你需要请求它,然后异步等待某些东西(例如事件(告诉你用户已经完成。(更类似于请求远程计算机的某些内容(
以下是帮助您入门的内容:
public abstract class Player
{
public string Name { get; private set; }
public int Score { get; set; }
public Player(string name)
{
Name = name;
}
public abstract void ActAsync(int max);
public virtual event EventHandler<ActEventArgs> ActComplete;
}
public class ActEventArgs : EventArgs
{
public int Result { get; private set; }
public ActEventArgs(int result)
{
this.Result = result;
}
}
public class HumanPlayer : Player
{
public HumanPlayer(string name)
: base(name)
{
}
public override void ActAsync(int max)
{
// button1 should be the user's guess button; anything else you need to do to allow them to interact should be done here
button1.IsEnabled = true;
// this will result in a handler being added each time the user is asked to act; you'll actually want to attach just once
button1.Click += (s, e) =>
{
button1.IsEnabled = false;
if (ActComplete != null)
ActComplete(this, new ActEventArgs(int.Parse(data.HumanGuess))); // data.HumanGuess is their input, as a string
};
}
public override event EventHandler<ActEventArgs> ActComplete;
}
public class ComputerPlayer : Player
{
private readonly Random _random = new Random((int)DateTime.Now.Ticks);
public ComputerPlayer(string name)
: base(name)
{
}
public override void ActAsync(int max)
{
if (ActComplete != null)
ActComplete(this, new ActEventArgs(_random.Next(max)));
}
public override event EventHandler<ActEventArgs> ActComplete;
}
编辑:与Jmyster的解决方案(恕我直言(相比:他的解决方案更适合简单的交互,即本地用户到本地计算机,但如果您打算将其扩展到多用户场景,事件会更好。
我不会在 While 循环中玩游戏。我会做这样的事情:
private int numberToGuess = 0;
private void buttonStartPlay_Click(object sender, RoutedEventArgs e)
{
numberToGuess = //Create Random Int;
}
private void buttonHumanGuess_Click(object sender, RoutedEventArgs e)
{
if(IsAnswerCorrect(//HumanGuess))
//Human Wins
else
ComputerGuess();
//Or
MoveToNextPlayer();
}
private void MoveToNextPlayer()
{
//Do something to figure out who the next player is
}
private int ComputerGuess()
{
//Do something to get computer answer
if(IsAnswerCorrect(//ComputerGuess))
//Computer Wins
}
private bool IsAnswerCorrect(int answer)
{
if(answer == numberToGuess)
return true;
else
return false;
}
这是非常通用的,但想法是让用户输入一些东西,然后单击猜测按钮。 处理答案后,运行计算机猜测。
编辑:@Tim解决方案将处理更高级的方法。根据计时器是否是得分的一部分,您可能还需要使用线程。这些应该是您的起点。