C# timer control - good practice? - c#

I'm building an application which basically has a lot of user input, text boxes, combo boxes etc. I came across a problem involving input validation, and basically my solution was to call a method(which checks textbox input) within the timer.tick method.
The method:
private void AllowCreate()
{
if (firstNameText.Text == String.Empty || lastNameText.Text == String.Empty
|| descriptionText.Text == String.Empty)
{
createButton.Enabled = false;
}
else
{
createButton.Enabled = true;
}
}
So every tick, the method is called and checks if the text boxes are empty.
My question is: Is using a timer in this way, good practice? If not, are there more efficient ways of accomplishing what I am trying to do? Thanks.

I would not use these method.
I always catch TextChanged event for TextBoxes and SelectedIndexChanged for ComboBoxes and call check routine from there, enabling or disabling the button.
Basically, if you send all the events to
private void Something_Changed(object sender, EventArgs e)
{
createButton.Enabled =
!String.IsNullOrEmpty(firstNameText.Text) &&
!String.IsNullOrEmpty(lastNameText.Text) &&
!String.IsNullOrEmpty(descriptionText.Text);
}
you've done.

Using timers can create a slightly strange user experience, as I can change a field, then the next field, then suddenly I get a validation error on the first field, which I fix but the error doesn't go away for a while. Setting the timout shorter helps with this, but increases load unnecessarily when nothing is changing.
Like the others have said, use the validation events - they are there for a reason.
If you want to do all your validation in one place, that's okay too, just have one big "CheckValidation()" method and call that from each TextChanged event, or wherever you feel the need to re-validate.

Is there a reason you can't use the textchanged event for this?
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.textchanged.aspx

If you are not actually doing any particular validation its better to move your current code in to the createButton click event handler and if they are empty pop up a message.
If the fields are empty don't proceed with the code execution and notify the user instead of checking it in a timer (user might be having his coffee :) )

This is not the right way because it will validate input unnecessarily
You should either
Validate the input when user do some action that need values in textbox or combo box. For example user presses save button. Validate the input. If they are rght save otherwise tell
user the error
or
validate once the value of textbox or combo box has been changed

We have various requirements to use a timer style of functionality. However we have been avoiding Timer component. Instead we user Quartz Server. Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems.
Quartz is indeed a powerful library, but for simple requirements the Timer API can save the day, keeping you from putting unnecessary complexity into the system. You should think of using the Timer API in cases where you don't have a lot of tasks that need to be scheduled, and when their execution times are well-known (and unchangeable) in the design process. In these situations, you don't have to worry whether some tasks are lost because of a shutdown or crash. For more sophisticated needs, Quartz is an elegant scheduling solution.

Related

Problem with adding with ++ operator, what can I do?

So, I as trying to create an "Open world exploration" game in C# WinForms, And while coding the mining, (which works just fine), I encountered a problem with saving the number of broken blocks to the inventory (a label). Basically, for every block player breaks, it gets added to the inventory as inventoryWrite.Text = $"Grass: {grassHolder}, Rock: {rockHolder}";.
Now, the thing is, sometimes, even though I use the ++ operator, it adds up to 4 to the inventory. I'm citing the code below.
private void Remove(object sender, EventArgs e, PictureBox itm)
{
if (itm.BorderStyle == BorderStyle.FixedSingle)
{
if (itm.Tag.Equals("grass") && items.Contains(itm))
{
grassHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("rock") && items.Contains(itm))
{
rockHolder++;
itm.Tag = "";
}
if (itm.Tag.Equals("dio") && items.Contains(itm))
{
dioHolder++;
itm.Tag = "";
}
this.Controls.Remove(itm);
items.Remove(itm);
}
}
I update the inventory in a public loop, don't worry about that (interval is 1ms). But I don't think that's the problem, since I tried putting it in the Remove() function, and nothing seemed to change.
I've even double locked the if statement, but nothing! It still adds more than 1. Can anybody tell me how to solve this? Thank you a lot.
EDIT:
As a reply to Ronald's comment, the if statement is called ONLY when the block is selected. ONLY once when the method is called.
There are too many points to cover in a comment and so I've had to enter an answer.
In itself the ++ operator is not the issue and will always behave as it should, but as someone reviewing a small piece of code the following points crop up.
grassHolder, rockHolder, dioHolder appear to have
accessibility beyond this function and so could be altered
elsewhere.
Function void Remove(object sender, EventArgs e, PictureBox itm) appears to be an event handler and yet there is no locking mechanism to ensure that the externally accessible parameters are not changed or used elsewhere whilst the function code is executed. Specifically items which is appears to be a collection of sorts and is used both in logic to determine whether parameters in (1) are incremented, but also has its contents changed within the function.
From comments made it would appear that this logic is run in
response to user interaction, maybe by use of a mouse button or key
event. Is this base event de-bounced to ensure that multiple
triggers aren't handled?
Your statement "saving the number of broken blocks to the inventory (a label)." Implies that you are storing game data within the UI. This should be avoided as it ties game data directly to the UI implementation and therefore makes it difficult to alter the game, but also ties any handling of game data directly to the UI thread.
Recommended actions:
Ensure that the parameters in question are not accessed and altered
elsewhere causing the issue seen.
Utilize a lock(x) statement to ensure that items is not changed
whilst this function is being executed. More information here
De-bounce the mouse button or key click that triggers this function
to ensure that multiple events aren't triggered. This is performed
by placing a minimum time between event triggers. A minimum time
period of say 150ms would be a good starting point. This would equate to a reasonably quick, conscious user action, but be slower than multiple events triggered by partial/poor switch contact. Incidentally this is especially true on touch screen interfaces.
Consider controlling access to global parameters through use of
access functions. For example
int IncrementRockHolder(){ rockHolder++;} Although implementation may appear onerous, they can greatly help with debugging as call stack information is then available showing what code is calling the function and thus making the change.
Implement a game engine class to control access to game data and implement game logic. This
would allow you to unit test game functionality whilst also freeing
it from UI implementation and restrictions.

"An ItemsControl is inconsistent with its items source" thrown when calling MessageBox.Show()

I'm extremely new to WPF and I have the following problem:
I'm trying to develop a practice application to help me have control of my budgets.
I have a class Partida like so:
public class Partida
{
public delegate void PartidaChangedHandler(Partida p);
public event PartidaChangedHandler OnPartidaChanged;
private ObservableCollection<PartidaEntry> content;
public Partida()
{
content = new ObservableCollection<PartidaEntry>();
content.CollectionChanged += PartidaEntriesCollectionChanged;
}
public void PartidaEntriesCollectionChanged(object s, NotifyCollectionChangedEventArgs args)
{
if (OnPartidaChanged != null)
{
OnPartidaChanged(this);
}
}
}
I'm displaying the content collection with a Datagrid, but I need to know when the content collection of this class changed and fire the OnPartidaChanged event.
Outside of the class (in MainWindow) I hook up to the OnPartidaChanged event like so:
p.OnPartidaChanged += (Partida ppp) =>
{
int foo = 5;
MessageBox.Show("A partida has changed!");
};
When I add a new row within the DataGrid, the event fires correctly, however as soon as MessageBox gets executed I get an InvalidOperationException with the following message:
An ItemsControl is inconsistent with its items source.
Any idea how can a fix that problem without losing the ability to listen to the ObservableCollection's CollectionChanged event and firing OnPartidaChanged after that event?
Thanks in advance!
P.S.: Also, I would like to know what on earth MessageBox has to do with ItemControl... why is the exception firing if MessageBox only displays a simple box! :S
The answer in WPF add item to datagrid bound to observablecollection exception explains clearly exactly what is happening: the event handler is being called while the operation that is changing the collection is in progress, and calling MessageBox.Show() gives the dispatcher message-handling loop a chance to start processing messages again. This causes re-entrancy that is incompatible with the way WPF works: the collection-changing operation has not been completely resolved, but the UI is being given a chance to try to run the logic that should not be run until after that operation has been completely resolved.
In other words, just as the exception states, the control is in an inconsistent state, because it was allowed to do some processing that should not have occurred until after it was completely done dealing with the collection changing.
I grant that the advice in the other answer is not terribly compelling. It is reasonable advice as far as it goes, but offers no real alternatives.
In your scenario, without changing anything else about your implementation, one obvious solution is to defer the message box until after the collection-changing operation has been completely resolved. You can do this by using the Dispatcher.InvokeAsync() method to defer execution of the call to MessageBox.Show():
p.OnPartidaChanged += (Partida ppp) =>
{
int foo = 5;
Dispatcher.InvokeAsync(() => MessageBox.Show("A partida has changed!"));
};
Of course, there is the question as to whether showing a message box is really the best way to handle the event. It's not really clear from the limited information in your question why your event handler looks the way it does. If you are sure that showing a message box every time the collection changes is really the right thing to do, the above should resolve your problem.
But you might want to consider alternative means of presenting the information to the user, such as displaying it in a status field in the UI, or even providing some kind of log of events e.g. in a multi-line text box or list-box. These kinds of approaches will generally involve data-binding that fit within the normal flow of event and data handling in WPF, and would work as synchronous code without running into the issue you're seeing here.

Migrate C# code from Console to Windows Form Application

I have been learning C# using the book "Programming in the Key of C#...", this book has been very good in helping me understand the language but only deals with Console programs. I am ready to move on to developing versions of my past coding projects as Windows form applications but one program in particular is causing me a lot of frustration. I developed a simple movie trivia program utilizing arrays to hold the questions, answer choices, and the correct answer. It worked by displaying on the console the questions, the possible answers and waited for the user to provide a response (basically A,B,C or D) by using Console.Readline() to assign the response.
Now I want to be able to have the user enter the answer by selecting 1 of 4 buttons (A through D). Based on my old code, I am unsure how I get the program to wait for the user to click one of the buttons. I assume i need to change the nature of the loops but I just cant figure out how. Any help would be much appreciated.
Here is a snippet of my Console code:
while (iAsked < 5)
{
iLocation = rand.Next(0, astrQuestions.GetLength(0));
if (list.Contains(iLocation))
rand.Next(0, astrQuestions.GetLength(0));
else
{
iAsked++;
list.Add(iLocation);
Console.WriteLine("Question {0}", iAsked);
Console.WriteLine("------------");
Console.WriteLine(astrQuestions[(iLocation)]);
Console.WriteLine(astrChoices[(iLocation)]);
Console.Write("Answer:");
iResponse = Console.ReadLine();
if (iResponse == astrAnswers[(iLocation)])
{
Console.WriteLine("Correct\n");
iPoints += 5;
iCorrect++;
}
else
{
Console.WriteLine("Incorrect\n");
}
}
Moving from a prompting-centric environment like a console program to an event-driven environment like Winforms, yes…that definitely will require at least some change in "the nature of the loops". :)
That said, the latest version of C# offers an async/await-based approach that can minimize some of the culture-shock that might come from moving from console to GUI. Writing and using async method is itself non-trivial, but IMHO the simpler scenarios are not too hard to understand. More importantly, because it allows you to structure the code in a more directly-imperative way, similar to that which would be used in a console program, it's very much worth learning this along with Winforms generally.
In your particular scenario, you have two separate things you'll need to deal with: prompting the user, and receiving the user's input.
Because of the way an event-driven system works, you need to separate these tasks. But .NET has a class, TaskCompletionSource, which we can use to keep the two glued together, even though they wind up in different places.
First, what happens when the user starts the process? Presumably, you'll have a form, where on that form is a button (or possible a menu item) which when clicked/selected, starts the whole thing. That might look something like this:
private TaskCompletionSource<bool> _completionSource;
private async void button1_Click(object sender, EventArgs e)
{
int[] questionIndexes = ShuffleQuestions();
for (int iAsked = 0; iAsked < 5; iAsked++)
{
textBoxQuestionNumber.Text = string.Format("Question {0}", iAsked);
textBoxQuestion.Text = astrQuestions[questionIndexes[iAsked]];
textBoxChoices.Text = astrChoices[questionIndexes[iAsked]];
_completionSource =
new TaskCompletionSource<bool>(astrAnswers[questionIndexes[iAsked]]);
button2.Enabled = true;
bool result = await _completionSource.Task;
MessageBox.Show(result ? "Correct" : "Incorrect");
if (result)
{
iPoints += 5;
iCorrect++;
}
button2.Enabled = false;
_completionSource = null;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (_completionSource != null)
{
_completionSource.SetResult(
textBoxUserAnswer.Text == (string)_completionsSource.Task.AsyncState);
}
}
(I have changed your question-selection logic above to something more efficient, by assuming that you have a ShuffleQuestions() method. See Is using Random and OrderBy a good shuffle algorithm? for details on how to implement that).
What the above code does is, in response to the user clicking the button1 button (which presumably has text like "Start" or something), executes a loop that is very similar to what you had in your console program. The two main differences are:
Where in your console program, you use Console.WriteLine() to display text to the user, here I have shown the use of TextBox controls in your form which are used to display the same text.
What in your console program, you use Console.ReadLine() to receive input from the user, this loop creates a TaskCompletionSource object for a completely different method to use. That method, which is executed with your button2 button (which presumably has text like "Check Answer" or something) will read the text entered in a text box by the user (here, I've given it the name textBoxUserAnswer), compare it to the correct answer for the question (which has been provided to this method by the other method via the AsyncState property of the Task created by the TaskCompletionSource object I created), and set the Task's result to true or false, depending on whether the user got the answer correct or not.
The tricky part above is that "under the hood", that first method actually returns as soon as it is done filling in the text for the first question and reaches the await statement in the loop. The compiler rewrites the entire method to facilitate this.
When button2 is pushed, and sets the result of the Task, the framework then knows to resume executing the first method where it left off at the await statement, continuing your loop.
This sequence continues until the user has answered all of the question.
Some final notes about the UI:
I have used TextBox's everywhere for user input and output. Of course, there are other ways to display text. Also, the default state for a TextBox is a single-line, read/write text. But for displaying to the user, you may find that setting the ReadOnly property of the TextBox to true is better (i.e. to prevent the user from accidentally changing the text), and/or that you prefer setting the Multiline property to true (i.e. so that more than one line of text is displayed).
The above also assumes that the initial state for the button2 button's Enabled property is false. I.e. that button can't be clicked until the first method above explicitly enables the button at the appropriate time.

How to efficiently determine if the user has actually produced input in C#

Working on a text editor here...
I'm trying to figure out if the user has actually caused any changes in a textbox after a keyboard event has occurred. Obviously if they press a standard keys like, A B C, it will fire, but I'm more interested if there's a way, besides enumerating all possible, non-input options.
I will note that I realize the state of the textbox could be compared before and after, but that seems like it would be a poor use of memory, and CPU time if it was a large document. Also, I'm using the Modified property for another purpose, and since it only fires when it changes, that also isn't an options, as it will more often than not be in the true state.
I've though about the enumeration side, but that also seems inefficient, as, I would not only have to ensure that every non-input option is there, but also check for all the exceptions. One example that comes to mind:
If Ctrl is pressed, I don't want to do anything, unless I have Ctrl + V or Ctrl + X.
However, if there's nothing in the clipboard, then Ctrl + V shouldn't do anything, and if nothing is selected, then Ctrl + X shouldn't do anything.
I'm sure there are more exceptions that I'm not thinking of right now.
As for the Modified property, when that changes I'm using it to modify the widow title with/without an asterisk based on it's state. So if I reset the property, it will remove the asterisk.
In the famous method of rubber duck debugging, I ended up solving this simply by asking it. If I've missed a more efficient option, please let me know.
The first and what would seem the be the simpliest option would be to use the TextChanged event, which I actually learned about by adding tags to my post.
If for whatever reason that doesn't work for the future reader, then using the ModifiedChanged event would be the next best option.
Simply put, in my situation, the asterisk only goes away when the document is saved. By setting a flag, I can use an if statement to determine if I need to remove it, and then reset the flag at that point. Something like this:
void saved(){
// ...
saved = true;
}
// ...
if(saved){
removeAsterisk();
saved = false;
}
// ...
Those seem to be the best options, which are neither long in code, nor consuming in time by checking 100k characters.

How to create IDLE -like functionality to WinForms application

I'd like to add "IDLE-like functionality" to C# WinForms application, but I don't quite have an idea how to do that and couldn't find anything useful with Google.
So basically I want interactive command line interface, where user could enter some Python code and execute it (not just expressions, should be possible to define new functions).
So, where to start? Are there any good tutorials or samples available?
If my memory serves me correctly there's a chapter on embedding Python in the book Python in a Nutshell. Perhaps you can find some useful information there, but since the book is not Windows specific, you may have to adapt it yourself.
I would setyp my WinForm like this: add 2 textboxes.
1: for output. Set the multiline property of the first to true, and make it read only.
2: for input. Use KeyUp Or KeyPress Event for e.g. the return key and use the text to do what you want: add command to output textbox, launch code against the engine and capture output of interpreter
This link (http://groups.google.com/group/ironpy/browse_thread/thread/5e61a944c7c94d4b/0cbf29ec0f5fbb64?pli=1) might give some answers about launching commands agains a python engine.
IronRuby comes with a command line interpreter. Doesn't IronPython also have one? If so, the source code would be a good start :)
Oh, and if it doesn't, be sure to look at the IronRuby interpreter, because both languages are based on the DLR and are therefore similar enough to learn from both.
Thru IronPython mailing list I found IronTextBox2, which is good example how things are done. It needs a little tweaking, to get it running, but otherwise is good solution.
Here go my most generic solution:
Point cursorPoint;
int minutesIdle=0;
private bool isIdle(int minutes)
{
return minutesIdle >= minutes;
}
private void idleTimer_Tick(object sender, EventArgs e)
{
if (Cursor.Position != cursorPoint)
{
// The mouse moved since last check
minutesIdle = 0;
}
else
{
// Mouse still stoped
minutesIdle++;
}
// Save current position
cursorPoint = Cursor.Position;
}
You can setup a timer running on 60000 interval. By this way you will just know how many minutes the user don't move the mice. You can also call "isIdle" on the Tick event itself to check on each interval.

Categories

Resources