How to notify UI about action done in API? - c#

I'm writing a simple game, like tic tac toe (only mine is bigger). The game play is simple: my turn, check if won, bot's turn, check if won. I have simple UI and API that uses Domain Entites (that's not important now). So when user's moves, API will update the board, will know that next move is bot's move, so will do it and... has to notify UI. Here is my problem.
My question is:
How to notify UI about bot's move? I mean, to keep it simple but stick to the best programming practices.
My first thought was to create an event in GameAPI class. Is that good idea? Today will all new stuff, C# 6, etc.. I'm not sure:/ Right now UI is WinForms, but I would like to use this API in other UIs, like WPF or even mobile. Here is my simplified code of UI:
EDIT: Right now I'm talking about single player game. Both UI and API is a Client. There will be multiplayer through central server in next step, but right now, single player.
public partial class Form1 : Form
{
private GameAPI api;
public Form1()
{
InitializeComponent();
api = new GameAPI();
}
private void boardClick(object sender, EventArgs e)
{
Field field = GetClickedField(e);
MoveResult result = api.MakeMove(clickedColumn);
if (result != null && result.Row >= 0)
{
MessageBox.Show(result.Row + "," + clickedColumn);
if (result.IsConnected)
{
MessageBox.Show("Success!");
}
}
}
}
and API:
public class GameAPI
{
public IGame CurrentGame { get; set; }
public void CreateGame(GameType type)
{
CurrentGame = new SinglePlayerGame();
}
public Result Move(int column)
{
if (CurrentGame == null) return null;
Player player = CurrentGame.GetNextPlayer();
if (player.Type == PlayerType.Human) return CurrentGame.Move(column, player.Id);
}
public Result MoveBot()
{
// Simulate Bot's move...
}
}

My first thought was to create an event in GameAPI class. Is that good idea?
Yes, why not? Let take for example the modern UI frameworks data binding. The key point of making data binging work is providing a property change notification (read - event) when some property value of the object is modified. Usually that's implemented via IPropertyNotifyChanged interface, which is simply a polymorphic way of declaring support for PropertyChanged event. This way, if you set the object property via code, the UI updates automatically. Hope you see the similarity with your case - the API does something and raises an event, UI (being attached handler to that event as some earlier point) receives the event and updates accordingly.

Related

Doubst about Managin player actions and cooldown time

So i got this action object witch contains a button, when i press that button the action coroutine starts, sets the cooldown of the action and enabled=false the button.
Im trying to implement a cooldown system so every time the turn ends action.currentCooldown -=1.
Im yet to implement my turn manager and i must admit im a bit clueless about it, i guess it must have a state(allyTurn, enemyTurn) and a coroutine to update all action cooldown when the turn changes.
Also what i want to do is asing a List to each unit and then display each action buttons.
here are some screenshots of the code (keep in mind its just a first draft and im still learning the basics)
Hereis the action object
here is the code for the action
I apreciate all the help i can get
So there are a couple ways to implement what you are looking for. The way that I would want to go for is using events. You could create events for each state change (i.e. AllyTurnStart and EnemyTurnStart):
public class ExampleClass : MonoBehaviour
{
bool AllyTurn = true;
UnityEvent AllyTurnStart;
UnityEvent EnemyTurnStart;
BaseAction AllyAction;
BaseAction EnemyAction;
void Start()
{
// This will setup the event listeners that get called
if (AllyTurnStart == null)
AllyTurnStart = new UnityEvent();
if (EnemyTurnStart == null)
EnemyTurnStart = new UnityEvent();
// This is for example purposes but assigning actions to the events can happen anywhere
AllyAction = new BaseAction()
EnemyAction = new BaseAction()
AllyTurnStart.AddListener(AllyAction.StartOfTurnEvents);
EnemyTurnStart.AddListener(EnemyAction.StartOfTurnEvents);
}
//This method is meant to simulate switching turns back and forth.
void NextTurn()
{
if(AllysTurn)
{
AllyTurnStart.Invoke()
}
else
{
EnemyTurnStart.Invoke()
}
//This switches whos turn it is
AllysTurn = !AllysTurn;
}
}
public class BaseAction : Monobehaviour
{
int Cooldown;
public StartOfTurnEvents()
{
// Here you can reduce the cooldown, Check if it is ready, etc.
}
}
Hope this helps.

WPF update status bar text from backgroundworker

I have a status bar on a window that I want to update with a string while a background process is running.
Specifically the user presses a button, and data is read from a file. As the file is being read, backend db calls are made to populate data objects. This is done for all lines in the file. This can take a little bit of time. This is all done in a background worker.
What I want to do is provide the user with some feel that something is happening. I would like to present the user with the name of the object that was just read in on a status line. I see BackgroundWorker provides an interger update value, but how do I do a string so that I can display it?
This is a common problem people face in WPF. I imagine you are trying to update a label from a BackgroundWorker and you are being plagued by "a different thread owns it" errors. You will probably find answers telling you to use a dispatcher to update your label. DON'T DO IT! It's unreliable and often doesn't update under heavy workloads. You should use proper binding techniques in your instance.
Hopefully this example (though not necessarily the most refactored, nor quickest to implement) can get you started on the right path, by explaining some core fundamentals or WPF.
Create an observable class that implements INotifyPropertyChanged. This is your Model. Its basically the shell of how your data will be stored (in your instance a status).
using System.ComponentModel;
public class Status_Update : INotifyPropertyChanged
{
//private property that stores value
private string status;
//public property the gets & sets value
public string Status
{
get {return status;}
set
{
if(status != value)
{
status = value;
NotifyPropertyChanged("Status");
}
}
}
//Logic to notify that property values have changed.
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Now lets move to your Control or Window's code behind constructor.
//Create a Status_Update object in your control.
public Status_Update My_Status {get;set;}
public My_Control_or_Window()
{
//Initialize the Status_Update object
MyStatus = new Status_Update(){Status=""};
InitializeComponent();
//Set the controls DataContext to itself in the constructor
DataContext=this;
}
Now in your frontend's XAML you simply bind to your control's MyStatus.Status property and it's ready for live updates from any calling thread.
<Label Content={Binding MyStatus.Status, UpdateSourceTrigger=PropertyChanged}/>
To update simply set the value of MyStatus.Status from your BackgroundWorker.
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
MyStatus.Status = "Updating the first item";
Some_Task();
MyStatus.Status = "Updating the next item";
Some_Task();
}
I want to note that this example isn't the best example of MVVM, nor is it the best code structure for what you are trying to do, but it should help give you a better understanding of how binding works in WPF and how you can update things like a status label much easier with it. It takes a little more work on the front end, but saves so much time on the back end.
Best of luck.

Trying to raise event c#

I have been studying MANY other answers and examples about this and I am just getting more and more confused on how to set this up. I need to raise an event in the Robot class based on the result of the performMove method in the form class. I know I can't raise the event from another class, so what I have obviously doesn't work. But I'm really not grasping how to set this up properly. I have read the delegates and events articles on codeProject, dreamInCode, and in this site, amongst many others. This is for a beginner c# class, and I'm pretty new to this, as I'm sure everyone can tell :)
namespace Assignment12
{
public delegate void ErrorHandler();
public partial class frmRobot : Form
{
Robot moveRobot = new Robot();
public frmRobot()
{
InitializeComponent();
reset_Position();
current_Position_Display();
moveRobot.outOfRange += new ErrorHandler(moveRobot.coor_Within_Range);
}
...
private void performMove()
{
Point loc = lblArrow.Location;
int x = moveRobot.Move_Robot_XAxis(loc.X);
int y = moveRobot.Move_Robot_YAxis(loc.Y);
if (x < -100 && x > 100)
{
moveRobot.outOfRange();
x = loc.X;
}
if (y < -100 && y > 100)
{
moveRobot.outOfRange();
y = loc.Y;
}
this.lblArrow.Location = new Point(x, y);
current_Position_Display();
}
class Robot
{
public event ErrorHandler outOfRange;
...
public void coor_Within_Range()
{
System.Console.WriteLine("TestOK");
}
}
This question is quite confusing.
The question you should be posing to yourself is: who is in charge of declaring and enforcing policy? You have two entities: "form" and "robot". You have some policy about what a legal position is for the robot. What class is responsible for coming up with that policy? Does the robot know when it is out of range, and it informs the form of that fact? Or does the form know when the robot is out of range, and it informs the robot of that fact?
The thing that wishes to be informed is the event listener. The thing that wishes to inform others of a policy violation is the event source. It is totally unclear which one of these things you want to be the listener and which one you want to be the source. But the rule you are violating is clear: the event listener is not the thing that is allowed to say when the event happens. The person listening to the concert does not get to stand up and yell instructions to the pianist about what keys to press! That's the pianist's decision, and the listener merely gets to decide whether to listen or not, and how to react.
If the form gets to decide when the robot is out of range then the robot needs to be the listener. If the robot gets to decide when the form is out of range then the form needs to be the listener. Right now you've got the form being the listener, and yet it is trying to tell the robot when it is out of range.
You don't seem to need an event. Right now it's just a complicated way to call moveRobot.coor_Within_Range(). Cut out the middleman:
if (x < -100 && x > 100)
{
moveRobot.coor_Within_Range();
x = loc.X;
}
Although Within_Range and outOfRange are curiously opposite names.
You would need an event to inform the Form about something happening in the Robot. I posted an answer here about how to do that.
Your coor_Within_Range needs to raise the event:
public void coor_Within_Range()
{
System.Console.WriteLine("TestOK");
if (this.outOfRange != null) {
this.outOfRange();
}
}
Then in your Form class you need to handle the event:
public frmRobot()
{
// snipped
moveRobot.outOfRange += new ErrorHandler(this.oncoor_Within_Range);
}
public void oncoor_Within_Range() {
Console.WriteLine("robot within range");
}

How to run a function on a background thread for Windows Phone 7?

I'm using MVVM Light to build a WP7 (Windows Phone 7) application. I wish to have all the work performed by the Model to be run on a background thread. Then, when the work is done, raise an event so that the ViewModel can process the data.
I have already found out that I cannot invoke a Delegate asynchronously from an WP7 app.
Currently I am trying to use ThreadPool.QueueUserWorkItem() to run some code on a background thread and use MVVM Light's DispatcherHelper.CheckBeginInvodeOnUI() to raise an event on the UI thread to signal the ViewModel that the data has been loaded (this crashes VS2010 and Blend 4 when they try to display a design-time view).
Is there any sample code to run some code on a background thread and then dispatch an event back to the UI thread for a WP7 app?
Thanks in advance,
Jeff.
Edit - Here is a sample Model
public class DataModel
{
public event EventHandler<DataLoadingEventArgs> DataLoadingComplete;
public event EventHandler<DataLoadingErrorEventArgs> DataLoadingError;
List<Data> _dataCasch = new List<Data>();
public void GetData()
{
ThreadPool.QueueUserWorkItem(func =>
{
try
{
LoadData();
if (DataLoadingComplete != null)
{
//Dispatch complete event back to the UI thread
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
//raise event
DataLoadingComplete(this, new DataLoadingEventArgs(_dataCasch));
});
}
}
catch (Exception ex)
{
if (DataLoadingError != null)
{
//Dispatch error event back to the UI thread
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
//raise error
DataLoadingError(this, new DataLoadingErrorEventArgs(ex));
});
}
}
});
}
private void LoadData()
{
//Do work to load data....
}
}
Here's how I'd approach a solution to this.
Your ViewModel implements INotifyPropertyChanged right? There's no need to dispatch the Events. Just raise them "bare" in the Model, then dispatch the RaisePropertyChanged in the ViewModel.
And yes, you should have some sort of singleton model/database in your code. After all, what is a SQL Database if not some gigantic singleton? Since we don't have a database in WP7, don't be shy creating a singleton object. I have one called "Database" :)
I've just tried threading my dataloads in there, and realise that in fact the best approach is simply implementing INotifyPropertyChanged right down at the model level. There's no shame in this.
So given that, here's what I'm doing in the singleton Database object to load and return my Tours "table" (note the thread.sleep to make it take a visible amount of time to load, normally its sub 100ms). Database class now implements INotifyPropertyChanged, and raises events when loading is completed:
public ObservableCollection<Tour> Tours
{
get
{
if ( _tours == null )
{
_tours = new ObservableCollection<Tour>();
ThreadPool.QueueUserWorkItem(LoadTours);
}
return _tours;
}
}
private void LoadTours(object o)
{
var start = DateTime.Now;
//simlate lots of work
Thread.Sleep(5000);
_tours = IsoStore.Deserialize<ObservableCollection<Tour>>( ToursFilename ) ?? new ObservableCollection<Tour>();
Debug.WriteLine( "Deserialize time: " + DateTime.Now.Subtract( start ).ToString() );
RaisePropertyChanged("Tours");
}
You follow? I'm deserializing the Tour list on a background thread, then raising a propertychanged event.
Now in the ViewModel, I want a list of TourViewModels to bind to, which I select with a linq query once I see that the Tours table has changed. It's probably a bit cheap to listen for the Database event in the ViewModel - it might be "nicer" to encapsulate that in the model, but let's not make work we we don't need to eh?
Hook the Database event in the Viewmodel's constructor:
public TourViewModel()
{
Database.Instance.PropertyChanged += DatabasePropertyChanged;
}
Listen for the appropriate table change (we love magic strings! ;-) ):
private void DatabasePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Tours")
{
LoadTourList();
}
}
Select the records I want from the table, then tell the view there is new data:
public void LoadTourList()
{
AllTours = ( from t in Database.Instance.Tours
select new TourViewModel( t ) ).ToList();
RaisePropertyChanged( "AllTours" );
}
And lastly, in your ViewModelBase, it's best to check if your RaisePropertyChanged needs dispatching. My "SafeDispatch" method is pretty much the same as the one from MVVMlight:
private void RaisePropertyChanged(string property)
{
if ( PropertyChanged != null )
{
UiHelper.SafeDispatch(() =>
PropertyChanged(this, new PropertyChangedEventArgs(property)));
}
}
This works perfectly in my code, and I think is fairly tidy?
Lastly, extra for experts: in WP7, it might be good to add a ProgressBar with IsIndeterminate=True to your page - this will display the "dotted" progress bar. Then what you can do is when the ViewModel first loads you could set a "ProgressBarVisible" property to Visible (and raise the associated PropertyChanged event). Bind the ProgressBar's visibility to this ViewModel property. When the Database PropertyChanged event fires, set the visibility to Collapsed to make the progressbar go away.
This way the user will see the "IsIndeterminate" progress bar at the top of their screen while the deserialization is running. Nice!
I have not developed for WP7 before, but I found this article that might be useful!
Here is the Dining Philosopher sample code from the article that should give you a good idea on how to raise an event to the UI from another thread:
public DinnersViewModel(IDinnerCatalog catalog)
{
theCatalog = catalog;
theCatalog.DinnerLoadingComplete +=
new EventHandler<DinnerLoadingEventArgs>(
Dinners_DinnerLoadingComplete);
}
public void LoadDinners()
{
theCatalog.GetDinners();
}
void Dinners_DinnerLoadingComplete(
object sender, DinnerLoadingEventArgs e)
{
// Fire Event on UI Thread
View.Dispatcher.BeginInvoke(() =>
{
// Clear the list
theDinners.Clear();
// Add the new Dinners
foreach (Dinner d in e.Results)
theDinners.Add(d);
if (LoadComplete != null)
LoadComplete(this, null);
});
}
I hope it's helpful :).
One thing that's confusing: you said that when you use the helper to raise the event, then VS2010 crashes... what exactly are you seeing when it's crashing? Are you getting an exception?
Jeff, I'm still figuring this stuff out myself. I posted a similar question and ended up answering it myself by building a simple sample. Here:
A super-simple MVVM-Light WP7 sample?
The summary is:
1) I derived my Model (yes my model) from ViewModelBase. This gives me Mvvm-Light's implementation of messaging and INotifyPropertyChanged which is handy. You could argue this is not "pure" but I don't think it matters.
2) I used Mvvm-Light DispatcherHelper.CheckBeginInvokeOnUIhelper just as you did (from my Model, NOT my ViewModel).
Hope this helps.

C# WinForms: Waiting for a button press inside an infinite loop

I'm making a simple Guess-The-Number game with a GUI. I need to wait on a loop waiting for the user to input a number in a text box and press "OK". How do I wait for an event inside a loop?
Note: I don't want message boxes. This is done in the main window, hence the need to wait for input.
EDIT: I should have explained myself better. I know that there's a loop inside the GUI. What I want is another loop inside a method. Maybe there's a better way to do this. I could code stuff inside the button's event handler, now that I think about it. Although I'd need global variables. Whataver, I'll think about it, but I hope my question is clearer now.
EDIT 2: Sorry that my question wasn't clear and the edit didn't do much help. First of all, the code is too big to be posted here. I'd probably have to post a screenshot of the GUI, so it wouldn't be of much use. Basically, I have two fields, "Max number" and "Number of allowed guesses". The user enters these two and clicks "Play". A new panel becomes available, with a text box and a "Guess" button. The user enters a guess, and the program checks to see if it's correct.
The purpose of the second infinite loop is to avoid global variables. See, each time the user clicks "Play", the game has to generate a new random number as the correct guess. If everything is done inside a method, no problem. But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
I'd also have to keep track of the remaining number of guesses outside of the method. Again, it's no big deal. I just want to avoid globals if I can.
Again, I'm sorry that my question wasn't too clear. I'm kind of tired, and I didn't feel like writing too much. If this still isn't clear, then don't bother. I'll think of something.
C# automatically loops infinitely waiting for events until your form is closed. You just need to respond to the button click event.
Jason Down's suggestion is wise, create a new GuessingGame class and add it to your project. I know you're worried about "global variables" (which everyone is taught in school never to use unless you absolutely have to), but think about your design specifications for a minute.
But if the "Guess" button's event handler is called multiple times, the number has to be stored as an instance variable of the Form. Sure, it's not big deal, but I think the number should be a property of the method directing the current game, not of the Form.
As an alternative, store an instance of your GuessingGame class in the form. This is not a global variable! You said so yourself, the point of the game is keep track of the guesses and generate new numbers to guess every time "Play" is clicked. If you store an instance of the game in the form then open another form (e.g. a Help or About box), then the game's instance would not be available (thus, not global).
The GuessingGame object is going to look something like:
public class GuessingGame
{
private static Random _RNG = new Random();
private bool _GameRunning;
private bool _GameWon;
private int _Number;
private int _GuessesRemaining;
public int GuessesRemaining
{
get { return _GuessesRemaining; }
}
public bool GameEnded
{
get { return !_GameRunning; }
}
public bool GameWon
{
get { return _GameWon; }
}
public GuessingGame()
{
_GameRunning = false;
_GameWon = false;
}
public void StartNewGame(int numberOfGuesses, int max)
{
if (max <= 0)
throw new ArgumentOutOfRangeException("max", "Must be > 0");
if (max == int.MaxValue)
_Number = _RNG.Next();
else
_Number = _RNG.Next(0, max + 1);
_GuessesRemaining = numberOfGuesses;
_GameRunning = true;
}
public bool MakeGuess(int guess)
{
if (_GameRunning)
{
_GuessesRemaining--;
if (_GuessesRemaining <= 0)
{
_GameRunning = false;
_GameWon = false;
return false;
}
if (guess == _Number)
{
_GameWon = true;
return true;
}
else
{
return false;
}
}
else
{
throw new Exception("The game is not running. Call StartNewGame() before making a guess.");
}
}
}
This way, all the data related to the game is encapsulated within the class. Hooking up the events is easy in the codebehind of the form:
GuessingGame game = new GuessingGame();
private void btnPlay_Click(object sender, EventArgs e)
{
int numberOfGuesses = Convert.ToInt32(txtNumberOfGuesses.Text);
int max = Convert.ToInt32(txtMax.Text);
game.StartNewGame(numberOfGuesses, max);
}
private void btnGuess_Click(object sender, EventArgs e)
{
int guess = Convert.ToInt32(txtGuess.Text);
bool correct = game.MakeGuess(guess);
if (correct)
lblWin.Visible = true;
if (game.GameEnded)
{
// disable guess button, show loss label
}
}
You should probably look for a book to actually learn windows programming.
The very basics:
1) There is already an infinite loop deep down in the windows code somewhere. Any windows program is constantly looping and scanning for input.
2) Once input is found, this loop fires off an Event.
3) Your mission, should you choose to accept it, is to write event handlers to handle those events.
you are most likely doing it wrong as it has already been pointed out, but you can use this
Application.DoEvents();
to process events when you are on an actual loop
to do it the right way
- don't use a loop
- use an edit box for the input, then a button
- implement the button onclick event
Yes, and What if I am waiting for Speech events, it could happen anytime event when a function is running, I need to handle that without recursively call a function

Categories

Resources