【问题标题】:Pausing a program to allow user to interact in wpf暂停程序以允许用户在 wpf 中进行交互
【发布时间】:2012-05-14 21:23:24
【问题描述】:

好的,所以这是一种通用程序。我正在尝试编写一个扑克程序,用户可以在其中与计算机玩家互动。但是,为了简化它,我正在使用一个更简单的程序进行测试,其中两个玩家试图从计算机中猜测一个数字。一个是人类玩家,另一个是电脑玩家。

在游戏中,玩家轮流猜测程序设置的蜜蜂数。计算机播放器只是随机化数字,用户应该输入一个数字并按下猜测按钮(或者可以选择退出)。

我的问题是,当轮到人类玩家行动时,我怎样才能暂停游戏的执行,以便人类通过按下按钮或输入文本来响应?

我尝试过使用 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();

    }

}

【问题讨论】:

    标签: c# multithreading user-interface user-interaction


    【解决方案1】:

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

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

    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 的解决方案(恕我直言)相比:他更适合简单的交互,即本地用户到本地计算机,但如果您打算将其扩展到多用户场景,事件会更好.

    【讨论】:

    • 好的,谢谢。如何从 GuessNumber 类的 play 函数中调用 ActAsync?
    • player.ActCompleted += SomeHandler; player.ActAsync(max);
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-09
      相关资源
      最近更新 更多