暂停程序以允许用户在 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();
    }
}

暂停程序以允许用户在 wpf 中进行交互

你必须从不同的角度来处理这个问题。 而不是有一个同步运行的方法要求用户采取行动,就像你可能与本地计算机交互一样,你需要请求它,然后异步等待某些东西(例如事件(告诉你用户已经完成。(更类似于请求远程计算机的某些内容(

以下是帮助您入门的内容:

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解决方案将处理更高级的方法。根据计时器是否是得分的一部分,您可能还需要使用线程。这些应该是您的起点。