C# WPF React to a clicked button in another method - c#

Sorry if this is a basic question but somehow I have not been able to find a good answer to what I thought would be something very simple.
In one window I have a textblock, a textbox and a button (and some other items). The textblock has a question and you are supposed to enter the answer in the textbox. After entering the text you press the button to check if the answer is correct. The questions are handled in a foreach loop in method A. In "the middle" of the loop I need to check if the answer was correct. After searching I only found the following solution. Is this really the "correct way" of doing this?
I am quite new to C# so maybe I am overseeing something. The method seems to work but I would have expected something like a "wait for button pressed".
Any ideas or links to information would be very welcome ... Thanks a lot
TaskCompletionSource<bool> tcs = null;
private async void goingThroughTheList()
{
foreach member of the list
do something
tcs = new TaskCompletionSource<bool>();
await tcs.Task;
do something
}
private void button_method(object sender, RoutedEventArgs e)
{
do something
tcs?.TrySetResult(true); // this triggers that the other method
// knows that the button has been pressed
}

Wouldn't it be easier for the button to invoke the second part of your "goingThroughTheList()" if the first part has been completed?
bool FirstPartDone = false;
private void FirstPart()
{
foreach (var item in list)
{
// do something
}
FirstPartDone = true;
}
private void SecondPart()
{
foreach (var item in list)
{
// do something
}
}
private void button_method(object sender, RoutedEventArgs e)
{
// do something
if (FirstPartDone)
{
SecondPart();
}
}

Related

C# Removing Submenu Item Image Margins

See the linked screenshot below.
In short, I need those little white boxes to disappear - they're supposed to house an image, but there is no image, and so I'd rather they disappear.
I've accomplished this using the follow code:
foreach (ToolStripMenuItem menuItem in mnuMain.Items)
((ToolStripDropDownMenu)menuItem.DropDown).ShowImageMargin = false;
This works for what I guess are the main items, but not the sub-items, as you can see in the picture.
I've tried a few variations on the above code to try and get it to capture everything instead of just the first level items, but no luck.
What am I doing wrong?
http://i.imgur.com/bst1i4v.png
You should do that for sub items too. To do so, you can use this code:
private void Form1_Load(object sender, EventArgs e)
{
SetValuesOnSubItems(this.menuStrip1.Items.OfType<ToolStripMenuItem>().ToList());
}
private void SetValuesOnSubItems(List<ToolStripMenuItem> items)
{
items.ForEach(item =>
{
var dropdown = (ToolStripDropDownMenu)item.DropDown;
if (dropdown != null)
{
dropdown.ShowImageMargin = false;
SetValuesOnSubItems(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
}
});
}
This is a modified version of above. Use:
MainMenuStrip.HideImageMargins();
Because the recursive method performs the intended manipulation, I used overloading to make it clearer what is intended. Pattern matching is used because the above sample will throw an exception, not return null.
public static void HideImageMargins([NotNull] this MenuStrip menuStrip)
{
HideImageMargins(menuStrip.Items.OfType<ToolStripMenuItem>().ToList());
}
private static void HideImageMargins([NotNull] this List<ToolStripMenuItem> toolStripMenuItems)
{
toolStripMenuItems.ForEach(item =>
{
if (!(item.DropDown is ToolStripDropDownMenu dropdown))
{
return;
}
dropdown.ShowImageMargin = false;
HideImageMargins(item.DropDownItems.OfType<ToolStripMenuItem>().ToList());
});
}

Pause the while loop until the button is pressed w/o using event handler C#

I am struggling to workout how to create something that essentially pauses my while loop until my button1 is pressed, I know about the event handler button1_Click but I don't think that will work in this situation as I have lots of loops nested in each other on my form_load.
Any help would be highly appreciated!
This is a snipped of my code where I want the loop to be 'paused' with the notes:
while (reader2.Read())
{
QuestionSpace = Convert.ToString(reader2["Question Space"]);
label1.Text = QuestionSpace;
if (button1.Click = true) // if the button is clicked)
{
// continue with the while loop (I am going to add an INSERT SQL query in here later)
}
else
{
// pause until the button is pressed
}
}
My whole code for the form:
public partial class CurrentlySetTestForm : Form
{
private int QuestionID { get; set; }
private string QuestionSpace { get; set; }
public CurrentlySetTestForm()
{
InitializeComponent();
}
private void CurrentlySetTestForm_Load(object sender, EventArgs e)
{
string y = GlobalVariableClass.Signedinteacher;
MessageBox.Show(y);
Convert.ToInt32(y);
string connectionString = ConfigurationManager.ConnectionStrings["myconnectionstring"].ConnectionString;
SqlConnection connect = new SqlConnection(connectionString);
connect.Open();
SqlCommand command18 = new SqlCommand("SELECT [QuestionID] FROM QuestionStudentAssociation WHERE ( [StudentID]=#Signedinstudent)", connect);
command18.Parameters.AddWithValue("#Signedinstudent", y);
var reader = command18.ExecuteReader();
while (reader.Read())
{
QuestionID = Convert.ToInt32(reader["QuestionID"]);
SqlCommand command19 = new SqlCommand(#"SELECT [Question Space] FROM Questions WHERE ( [QuestionID] = #currentQID )", connect);
command19.Parameters.AddWithValue("#currentQID", QuestionID);
try
{
var reader2 = command19.ExecuteReader();
while (reader2.Read())
{
QuestionSpace = Convert.ToString(reader2["Question Space"]);
label1.Text = QuestionSpace;
if (button1.Click = true) // if the button is clicked)
{
// continue with the while loop (I am going to add an INSERT SQL query in here later)
}
else
{
// pause until the button is pressed
}
}
}
catch (SyntaxErrorException ex)
{
MessageBox.Show(ex.Message);
}
finally
{
MessageBox.Show("Done one loop");
}
}
}
}
Sounds like your not ready to learn TPL
So maybe a BackgroundWorker , you can paint it on the form
To make the click cancel the background worker have a look at Cancel backgroundworker
I would some time to learn TPL as its going to create a simpler and more elegant solution.
As for pausing I would refactor the code, you should not keep the reader open waiting on the user.
You do want event-driven response to UI events, always. However, I guess that you don't want to split your logic into a state machine by hand (where each event triggers progress to the next state). Well, you're in luck, the C# compiler has some keywords to build state machines automagically so you don't have to manage the details.
There are actually two different mechanisms for continuation-passing style implemented in C#. The old one, yield return, works great if your UI events are pretty much interchangeable (or you're only interested in one). Works like this:
IEnumerator<int> Coroutine;
// this could be a Form_Load, but don't you need to get the user information before making the database connection?
void BeginQuiz_Click( object sender, EventArgs unused )
{
Coroutine = RunQA();
}
IEnumerator<int> RunQA()
{
// connect to DB
// show first question on UI
return ContinueQA();
}
IEnumerator<int> ContinueQA()
{
// you can use a while loop instead if you really want
for( int question = 0; question < questionCount; ++question )
{
// check answer
if (/* too many wrong answers*/) {
// report failure in DB
yield break;
}
// get next question from DB
// show new question on the UI
// wait for UI action
yield return question;
}
// report score in DB
// update UI with completion certificate
}
void AnswerButton_Click( object sender, EventArgs unused )
{
answer = sender;
Coroutine.MoveNext(); // MAGIC HAPPENS HERE
}
void TimeoutTimer_Tick( object sender, EventArgs unused )
{
answer = TimeoutTimer;
Coroutine.MoveNext();
}
The magic comes from yield return. Every time the function reaches yield return, the compiler saves what you were doing. When the button click event comes and calls MoveNext, the compiler generates code that starts where yield return paused everything, and keeps going from there until the next yield return.
Important note, the code inside ContinueQA doesn't start when RunQA() does return ContinueQA(); It actually starts on the first MoveNext(). So split your code between RunQA() and ContinueQA accordingly.
If you need different pause reasons at different places in your code, then async/await will be more helpful.
A better way to handle this would be the use of a timer. This would allow the form to draw it's controls and handle all input, such as clicking the button.
Adjust the timer interval (ms) to your needs.
Another way of doing this would be, as Mehrzad Chehraz said, to use multi-threading.
On a side note, I would strongly recommend condition checks over the try/catch checks if possible.
Enable/Disable the timer using the button and call the loop when the timer ticks.
Example:
Timer loopTimer = new Timer();
private void Form1_Load(object sender, EventArgs e)
{
loopTimer.Interval = 100;
loopTimer.Tick += loopTimer_Tick;
loopTimer.Enabled = true;
}
void loopTimer_Tick(object sender, EventArgs e)
{
//perform the loop here at the set interval
}
private void button1_Click(object sender, EventArgs e)
{
//pause/play the loop
loopTimer.Enabled = !loopTimer.Enabled;
}

Why isn't this causing an infinite loop of events?

I have a simple application that reverses any text typed to it in another textbox. The catch is, you can modify either textbox and the changes will be (literally) reflected in the other.
I wrote this code, believing for it to cause problems.
private void realText_TextChanged(object sender, EventArgs e)
{
mirrorText.Text = mirror(realText.Text);
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
realText.Text = mirror(mirrorText.Text);
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
I then tried it out, believing that it would cause an infinite loop (realText changes mirrorText, another event happens, mirrorText changes realText, etc). However, nothing except the intended behavior happened.
I'm of course happy about this, I could just leave it here. Or could I?
I'm quite sure the TextChanged event is supposed to be fired whenever Text is changed. Is this intended behavior of some error protection in the events, or was I just lucky? Can this code misbehave on another computer, with other build settings, etc? It can be easily fixed:
private void realText_TextChanged(object sender, EventArgs e)
{
if (realText.Focused)
{
mirrorText.Text = Mirror(realText.Text);
}
}
I'll probably do it anyway to be safe, but is it required to check this? (I'm not even going to ask if it's recommended.)
Per the comments, and as already answered, the TextChanged event is not getting raised when you set the Text property to the value it already has.
It's not clear whether this is something you can safely rely upon. It is a sensible optimisation, and I would be very surprised if future versions of .NET Framework drop it, but I cannot speak for older versions, nor for third-party implementations (Mono).
To be absolutely safe, I would not use the Focused check you put in your question. I would do exactly what the Text setter does now.
private void realText_TextChanged(object sender, EventArgs e)
{
var newMirrorText = Mirror(realText.Text);
if (mirrorText.Text != newMirrorText)
mirrorText.Text = newMirrorText;
}
This has the same advantage of preventing infinite recursion, but plays more nicely with other code you may put in your form that changes the text as a result of some other event.
The reason it doesn't cause a loop is that it checks whether the Text property actually changed, i.e. if the new value does not equal the old value. In your case the mirror function happens to reverse itself, which leads to the same text after two passes.
It's pretty easy to check.
First, replace both textbox controls with
class T : TextBox
{
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
}
}
}
Second, set the breakpoint on setter. Add these expressions to the Watch window:
Name
Text
value
Third, launch the app, copy '123' from somewhere and paste it to the first textbox. Here it goes:
1st break:
Name: "mirrorText"
Text: ""
value: "321"
2nd break:
Name: "realText"
Text: "123"
value: "123"
3rd... whoops, it does not breaks anymore. To detect why we had to go deeper. Look at referencesource: text box setter does nothing unusual, but TextBoxBase's one looks interesting:
set {
if (value != base.Text) { // Gotcha!
base.Text = value;
if (IsHandleCreated) {
// clear the modified flag
SendMessage(NativeMethods.EM_SETMODIFY, 0, 0);
}
}
}
So, as hvd already answered, the reason is the textbox does not raise TextChanged if old and new values are the same. I don't think the behavior will change, at least for winforms. But if you want more robust solution, here it is:
private void RunOnce(ref bool flag, Action callback)
{
if (!flag)
{
try
{
flag = true;
callback();
}
finally
{
flag = false;
}
}
}
private bool inMirror;
private void realText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
mirrorText.Text = mirror(realText.Text);
});
}
private void mirrorText_TextChanged(object sender, EventArgs e)
{
RunOnce(ref inMirror, () =>
{
realText.Text = mirror(mirrorText.Text);
});
}
private string mirror(string text)
{
return new string(text.Reverse().ToArray()).Replace("\n\r", "\r\n");
}
P.S. mirror() will fail on surrogate pairs. Here're some solutions.
If textbox has a Text, and we try to change it with the same Text, the TextChange event is not raising because new text is same as the previous.
In your code, the realText_TextChanged event reverses the text and changes the mirrorText with it.
The mirrorText_TextChanged event reverses the text and try to change the realText.
The realText has already this text and does not raises the realText_TextChanged event.

WP7 Text-To-Speech Playing Whole Collection At Once

I working on a Windows Phone 7 app with Text-To-Speech capabilities. I'm using Text-To-Speech with Microsoft Translator Service and the following C# code...
// Text-To-Speech with Microsoft Translator Service (http://translatorservice.codeplex.com/)
private void TextToSpeech_Play(object sender, EventArgs e)
{
SpeechSynthesizer speech = new SpeechSynthesizer(CLIENT_ID, CLIENT_SECRET);
//string content = "This is a beautiful day!";
string language = "en";
//speech.SpeakAsync(content, language);
foreach (UIElement element in todayschapter.Children)
{
if (element is TextBlock)
{
string content = (element as TextBlock).Text;
speech.SpeakAsync(content, language);
}
}
}
In this instance, todayschapter is a StackPanel and its Children are TextBlocks. I'm wanting to simply play audio of each TextBlock, in succession. The problem is that it is play the audio of EVERY TextBlock at the same time.
I have a sneaking suspicion that the problem is SpeakAsync(), but I'm not sure. The documentation shows Speak(), but that isn't available (maybe a different version) in the Visual Studio methods helper dropdown (little thing that shows as you type - not sure what it's called).
Is there a way to make it wait for each play to finish before playing the next? Is foreach not the right choice, for this?
As always, if my code just looks stupid, please recommend better ways. I'm very much a beginner programmer.
Just use the Speak instead of the async call, since you want to have it one after another anyway.
The SpeakAsync call is indeed the problem. Unfortunately, since SpeakAsync doesn't return a Task, you can't just convert this to await SpeakAsync() (which would be the most straightforward conversion).
And, looking at the source code, it doesn't fire an event to tell when it's done. So let's add one (in SpeechSynthesizer.cs):
public event EventHandler<SpeechEventArgs> SpeakCompleted;
public void SpeakAsync(string text, string language)
{
this.GetSpeakStreamAsyncHelper(text, language, result =>
{
if (result.Error == null)
{
SoundEffect effect = SoundEffect.FromStream(result.Stream);
FrameworkDispatcher.Update();
effect.Play();
this.OnSpeakCompleted(new SpeechEventArgs(result.Error)); // added to call completion handler
}
else
{
this.OnSpeakFailed(new SpeechEventArgs(result.Error));
}
});
}
// new function
private void OnSpeakCompleted(SpeechEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (SpeakCompleted != null)
SpeakCompleted(this, e);
});
}
Now, you'll need to handle the SpeakCompleted event and start speaking the next string.
Something like this (I haven't even compiled this, so be warned):
private Queue<string> utterances;
private SpeechSynthesizer speech;
private void TextToSpeech_Play(object sender, EventArgs e)
{
speech = new SpeechSynthesizer(CLIENT_ID, CLIENT_SECRET);
speech.SpeechCompleted += new EventHandler<SpeechEventArgs>(TextToSpeech_Completed);
foreach (UIElement element in todayschapter.Children)
{
if (element is TextBlock)
{
string content = (element as TextBlock).Text;
utterances.Enqueue(content);
}
}
TextToSpeech_Completed(null, null); // start speaking the first one
}
private void TextToSpeech_Completed(object sender, SpeechEventArgs e)
{
if (utterances.Any())
{
string contents = utterances.Dequeue();
speech.SpeakAsync(contents);
}
}

How to display data received from serial port in a textbox without the text disappearing in Visual Studio C#?

So, I'm trying to develop a simple application in visual C# which gets data from serial port and displays it in a textbox (to monitor temperature). I'm acquiring and displaying the data successfully, using the DataReceived event to update a global string variable and a timer to update the text field on my text box, as shown:
private void port_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
try
{
globalVar.updateTemp = port.ReadLine(); //This is my global string
}
catch (IOException)
{
}
catch (InvalidOperationException)
{
}
catch (TimeoutException)
{
}
}
private void timer1_Tick(object sender, EventArgs e)
{
tempDisplayBox.Text = globalVar.updateTemp; //This is my textbox updating
}
The only issue I have is that the value shown in the textbox keeps flashing, making it hard to read. My timer is set to trigger every 10 ms (which should be fast enough, right?). Is there any way to make it more stable? I realize this may be a newb question, but to be fair I am a newb :) Any help is appreciated! Thanks!
Do you really need it updating every 10ms? What about every 500 ms or if not that then 100ms. 100ms will require your update method run 10 times less and therefore update 10 times less. The flickering you are expiriencing is due to the refresh speed. You could create custom method which will only update the temp only when target Label or textBox value is different than source port. But that will only sort the flickering when temp is steady, when temp will start vary it will bring back the flickering. Good luck ;-)
UPDATE
Hi I tried to reproduce the conditions and could not make my textbox nor Label flash. The way I tested it was by assigning int ntick = 0; and then increment the ++ntick; inside of the timer_tick method. The results didn't make any of the controls flash and were updated even every milisecond at some point. I also tried string.Format to put some load on the method. Is your app responsive?
The trick is to use double buffering. This way the operating system will redraw the Control off-screen, and only show the control when it is fully redrawn.
I have had the same problem, and solved it by extending the TextBox control like this:
public FastLogBox()
{
InitializeComponent();
_logBoxText = new StringBuilder(150000);
timer1.Interval = 20;
timer1.Tick += timer1_Tick;
timer1.Start();
SetStyle(ControlStyles.DoubleBuffer, true);
}
void timer1_Tick(object sender, EventArgs e)
{
if (_timeToClear)
{
_logBoxText.Clear();
_timeToClear = false;
}
if (_logQueue.Count <= 0) return;
while (!_logQueue.IsEmpty)
{
string element;
if (!_logQueue.TryDequeue(out element)) continue;
{
_logBoxText.Insert(0, element + "\r\n");
}
}
if (_logBoxText.Length > 150000)
{
_logBoxText.Remove(150000, _logBoxText.Length - 150001);
}
Text = _logBoxText.ToString();
}
public new void Clear()
{
_timeToClear = true;
while (!_logQueue.IsEmpty)
{
string element;
_logQueue.TryDequeue(out element);
}
}
public void AddToQueue(string message)
{
_logQueue.Enqueue(message);
}
}
I also use a timer and a concurrentQueue to avoid using Invoke to update the control from another thread. I also use a StringBuilder to prepare the string before putting it into the TextBox. StringBuilder is faster when building larger strings.
You can use ReadExisting() to read the whole data at a time.
You need to handle DataReceived Event of SerialPort
serialPort1.ReadExisting();
Sample:
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
String myData=serialPort1.ReadExisting();
}
Example Code: Here i would like to show you the code to Read Data(RFID Tag Code which is basically of length 12)
String macid = "";
private void DoWork()
{
Invoke(
new SetTextDeleg(machineExe ),
new object[] { macid });
macid = "";
}
private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string str1;
macid += serialPort1.ReadExisting();
if (macid.Length == 12)
{
macid = macid.Substring(0, 10);
Thread t = new Thread(new ThreadStart(DoWork));
t.Start();
}
}
public void machineExe(string text)
{
TextBox1.Text=text;
}
Thank you so much for the answers! I found a way to work around this issue:
Instead of replacing the contents of my textbox by rewriting the TextBox.Text property - which, as HenningNT implied, refreshes the control and causes the flickering - I'm now using the TextBox.AppendText method. Though, as I want to display only one line of data at a time, I use the textbox in multiline mode and the Environment.NewLine to jump to a new line before appending the text. As for the method of updating, I've gone back to using the timer because with the invoke method was crashing my application when I close the form, for some reason. Also, enabling double buffering didn't do me much good, although I guess I was doing it wrong... It still flickers a bit, but it's much better now :) I know this is not really a perfect solution (much more of a workaround), so I'll keep looking for it. If I find it, I'll be sure to update it here ;) My code:
private void timer1_Tick(object sender, EventArgs e) //Timer to update textbox
{
if (tempDisplayBox.Text != globalVar.updateTemp) //Only update if temperature is different
{
try
{
tempDisplayBox.AppendText(Environment.NewLine);
tempDisplayBox.AppendText(globalVar.updateTemp);
}
catch (NullReferenceException)
{
}
}
}

Categories

Resources