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.
Related
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.
In my Silverlight application, I have 15+ screens. I want a permission group 'CanOnlySeeTraining' to NOT have access to all screens except 2 of them. I know I can put the code below but the thing is I have to put this code for each and EVERY screen that I have out there except the 2 that I want them to see. Is there a way in Silverlight C# to say to put a permission from the top and flow down the screen tree(like Windows folder permissions)? Let me know if you need more clarification. Thanks.
partial void <ScreenName>_CanRun(ref bool result)
{
if (this.User.HasPermission(Permissions.CanOnlySeeTraining))
{
result = false;
}
else
{
result = true;
}
}
We don't do this exactly, but... Our app assigns an 'activity id' to each window (or batch process, we have several), and a standard part of startup in each is to check if the user's permission group has access to that activity. We have a general function that compares group & activity to determine if that combination is allowed; that function just returns a flag value that says whether or not the startup should continue. Everything uses the 'start-up allowed' check function and only continues if it is specifically allowed. Nobody needs to have any specific check like your sample, they just need to know if they are allowed to continue right now.
Our relation data is kept in a database table but it would be fairly trivial to move that to a file.
Our implementation relies on inheritance to ensure the check function doesn't get skipped and only a few base objects (two, I think) actually have the check coded.
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.
I have the following problem:
I use c++ library from my WPF app, the library throws an assertion in some very rare cases. It shows a nice dialog with c++ filename, the line number and assert expression. So the question is: can I disable asserts in the c++ library assuming that I don't have source code. What I really need is to "catch" this assertion and log it.
Thanks.
One way is to create a thread that performs EnumWindows every so often and detects if an assert window pops up, it can then capture the message and click the ignore button. This will still cause the window to show up for a short while (depending on your interval between EnumWindows but I assume you customers aren't going to be getting the debug DLL so it shouldn't matter.
Another option is calling _CrtSetReportMode(_CRT_ASSERT, 0) to disable asserts from being shown altogether.
If you want to PInvoke this from .NET note that _CRT_ASSERT is equal to 2.
Depending on your assembler skills and whether steps have been taken deliberately to block this type of stuff, it's usually possible to modify binary code to prevent this sort of message being displayed.
However, an assertion firing is often a precursor to some more spectacular crash or other misbehaviour, so just stopping the message box might not get you much further. Of course, some assertions are buggy, so this might be all you need.
If I had to modify this DLL, I would disassemble it with IDA and work out a patch. Hiding the assertion would probably be fairly easy, logging it quite a lot harder.
I recently had to fix some old code which relied on a DLL which sometimes popped up an assert message. I tried all the above suggestions, and the only one which I got working was to click on the Ignore button. The user above suggested running EnumWindows on a separate thread - I used FindWindow instead.
This is the function which finds the Assert popup message, finds the Ignore button within there, and then clicks it. It goes on a loop which checks a global variable each time (ugly but quick):
void CloseAssertBox (void *param) {
HWND window, button;
Sleep (200); //wait 200 milliseconds
while (!finishThread) { //see if we can stop checking
if ((window = FindWindow (NULL, L"Microsoft Visual C++ Runtime Library"))
&& (button = FindWindowEx (window, NULL, L"Button", L"&Ignore")))
SendMessage (button, BM_CLICK, 0, 0); //click the button
Sleep (50); //then check every 50 milliseconds
}
}
The title of your Assert box might be different. If your Ignore button is referenced differently, you can use EnumChildWindows to get the name of each child control including buttons.
Before the bit of code which pops up the assert, I start a new thread which calls the function above.
finishThread = 0; //this is set to 1 when the thread should finish
_beginthread (CloseAssertBox, 0, NULL); //begin the thread
After making it through the dangerous assert-prone code, I set:
finishThread = 1; //done threaded stuff
That way the thread will close next time round its loop. There's probably better ways of doing that.
I had to include these libraries to make it work:
#include <process.h> //for multithreading
#include <WinBase.h> //for Sleep function
int finishThread; //to tell the thread when encoding has finished
This was all done in Visual Studio 2010 using a library from 2006.
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.