Passing a Method to a class for EventHandlers - c#

Using a separate class which is being made reusable, need to pass it a function which would be called on the click event from the context menu...
Problem : Functions aren't type EventHandlers.... also Different EventHandlers require different paramters inside... e.g. the OnClose for the exit button....
Edit:
In the Class X
public void AddMenuItem(String name, EventHandler target )
{
MenuItem newItem = new MenuItem();
newItem.Index = _menuItemIndex++;
newItem.Text = name;
newItem.Click += target;
_contextMenu.MenuItems.Add(newItem);
}
In the Wpf:
addToTray.AddMenuItem("&Exit", Exit);
I would love it to link to the following method but at this point any method would do fine.
private void ShouldIExit(object sender, System.ComponentModel.CancelEventArgs e)
{
// checks if the Job is running and if so prompts to continue
if (_screenCaptureSession.Running())
{
MessageBoxResult result = System.Windows.MessageBox.Show("Capturing in Progress. Are You Sure You Want To Quit?", "Capturing", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
{
e.Cancel = true;
return;
}
}
_screenCaptureSession.Stop();
_screenCaptureSession.Dispose();
}

I'm guessing that the problem is that your ShouldIExit method does not match the EventHandler delegate. Try changing it to take a regular EventArgs parameter, and see if that works. It's best to avoid reusing the same event handler for different event types. You should encapsulate common code into separate methods, and then have different handlers call that code.
private bool CheckExit()
{
// checks if the Job is running and if so prompts to continue
if (_screenCaptureSession.Running())
{
MessageBoxResult result = System.Windows.MessageBox.Show("Capturing in Progress. Are You Sure You Want To Quit?", "Capturing", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
{
return false;
}
}
_screenCaptureSession.Stop();
_screenCaptureSession.Dispose();
return true;
}
private void ExitButtonClicked(object sender, System.ComponentModel.CancelEventArgs e)
{
if (!CheckExit())
{
e.Cancel = true;
}
}
private void ExitMenuItemClicked(object sender, EventArgs e)
{
CheckExit();
}

It is a bit vauge, but you could probably do
OnClose += (sender,e) => YourFunction(...)
which has the downside that it not possible to remove the eventhandler again if needed

Related

Is it possible to tell if the Text change on an entry was from code or from the UI?

I am working on a Xamarin project and I need to be able to tell if the changes that occur to the text in an Entry view are from the code or from the UI, is this possible in Xamarin? or is there a known work around to do this.
I know about the OnTextChanged event but this only tells you that the Text property has changed, and gives you access to the old and new value of the Text property. It does not differentiate between different causes of text change.
You can get some idea from this thread, check if the entry is focused to differentiate between different causes of text change:
public MainPage()
{
InitializeComponent();
myEntry.TextChanged += MyEntry_TextChanged;
}
private void MyEntry_TextChanged(object sender, TextChangedEventArgs e)
{
var entry = sender as Entry;
if (entry.IsFocused)
{
//change from UI
Console.WriteLine("change from UI");
}
else{
//change from code
Console.WriteLine("change from code");
}
}
Update: The better way to solve op's problem:
You can set a flag yourself that tells your code to ignore the event. For example:
private bool ignoreTextChanged;
private void textNazwa_TextCanged(object sender, EventArgs e)
{
if (ignoreTextChanged) return;
}
Create a method and use this to set the text instead of just calling Text = "...";::
private void SetTextBoxText(TextBox box, string text)
{
ignoreTextChanged = true;
box.Text = text;
ignoreTextChanged = false;
}
Refer: ignoreTextChanged
you can use EntryRenderer to detect keypress event and use that flag to detect the change by code or by UI.
Here are the step:
- Exetend your entry control with new event OnTextChangeByUI
- Write custom render for both platform
e.g for android it will be something like this
public class ExtendedEntryRender : EntryRenderer
{
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control != null)
{
Control.KeyPress += ((Entry)Element).OnTextChangeByUI;
}
}
}

Closing() with CancelEventArgs versus Form_Closing with FormClosingEventArgs

We are currently upgrading and remodularizing our code as we transfer from 32 to 64 bit system. In due process, one of our goals is to change from an Init() function where things were added as such example.
this.Closing += new System.ComponentModel.CancelEventHandler(this.Form_Closing);
I'd like Windows events to handle these sorts of things. So I went to the Form_Closing event in the Windows Form Events and [without surprise] saw that this was not the form_closing event. My question to this is, is there any difference between what is actually going on with CancelEventArgs vs FormClosingArgs, or are these two pieces of code literally doing the same thing with one being a Component of System and one being the result of a Windows Event handling what it does best? I'm just sort of diving and indulging myself into this new project as an intern. Is it possible to just replace the CancelEventArgs with the FormClosing one without any loss of data or issues?
Code 1: CancelArgs
private void Form_Closing(object sender, CancelEventArgs e)
{
// If the user hit Cancel, just close the form.
if (this.DialogResult == DialogResult.Ignore)
return;
if (this.DialogResult == DialogResult.OK)
{
// If the address is not dirty, just cancel out of
// the form.
if (!this._editedObject.IsDirty)
{
this.DialogResult = DialogResult.Cancel;
return;
}
// Save changes. If save fails, don't close the form.
try
{
SaveChanges();
return;
}
catch (Exception ex)
{
ShowException se = new ShowException();
se.ShowDialog(ex, _errorObject);
_errorObject = null;
e.Cancel = true;
return;
}
}
Form_Closing -- Preferred Route
private void ScheduleItemDetail_FormClosing(object sender, FormClosingEventArgs e)
{
// If the user hit Cancel, just close the form.
if (this.DialogResult == DialogResult.Ignore)
return;
if (this.DialogResult == DialogResult.OK)
{
// If the address is not dirty, just cancel out of
// the form.
if (!this._editedObject.IsDirty)
{
this.DialogResult = DialogResult.Cancel;
return;
}
// Save changes. If save fails, don't close the form.
try
{
SaveChanges();
return;
}
catch (Exception ex)
{
ShowException se = new ShowException();
se.ShowDialog(ex, _errorObject);
_errorObject = null;
e.Cancel = true;
return;
}
}
}
You don't get the CloseReason property with the CancelEventArgs class, which is the only difference, since FormClosingEventArgs inherits from the CancelEventArgs class. The FormClosingEventArgs was introduced in .Net 2.0.
Alternatively, instead of using the event, you could just override the OnFormClosing method, too.
protected override void OnFormClosing(FormClosingEventArgs e) {
// your code
base.OnFormClosing(e);
}

C# wait for user to finish typing in a Text Box

Is there a way in C# to wait till the user finished typing in a textbox before taking in values they have typed without hitting enter?
Revised this question a little:
Okay I have a simple calculator that multiplies by 2.
Here is what I want it to do: The user inputs a value like 1000 into a textbox and it automatically displays 2000.
Here is what happens: As soon as the user enters in 1 its multiplies by 2 and outputs 2.
I define "finished typing" now as "user has typed something but has not typed anything after a certain time". Having that as a definition i wrote a little class that derives from TextBox to extend it by a DelayedTextChanged event. I do not ensure that is complete and bug free but it satisfied a small smoke test. Feel free to change and/or use it. I called it MyTextBox cause i could not come up with a better name right now. You may use the DelayedTextChangedTimeout property to change the wait timeout. Default is 10000ms (= 10 seconds).
public class MyTextBox : TextBox
{
private Timer m_delayedTextChangedTimer;
public event EventHandler DelayedTextChanged;
public MyTextBox() : base()
{
this.DelayedTextChangedTimeout = 10 * 1000; // 10 seconds
}
protected override void Dispose(bool disposing)
{
if (m_delayedTextChangedTimer != null)
{
m_delayedTextChangedTimer.Stop();
if (disposing)
m_delayedTextChangedTimer.Dispose();
}
base.Dispose(disposing);
}
public int DelayedTextChangedTimeout { get; set; }
protected virtual void OnDelayedTextChanged(EventArgs e)
{
if (this.DelayedTextChanged != null)
this.DelayedTextChanged(this, e);
}
protected override void OnTextChanged(EventArgs e)
{
this.InitializeDelayedTextChangedEvent();
base.OnTextChanged(e);
}
private void InitializeDelayedTextChangedEvent()
{
if (m_delayedTextChangedTimer != null)
m_delayedTextChangedTimer.Stop();
if (m_delayedTextChangedTimer == null || m_delayedTextChangedTimer.Interval != this.DelayedTextChangedTimeout)
{
m_delayedTextChangedTimer = new Timer();
m_delayedTextChangedTimer.Tick += new EventHandler(HandleDelayedTextChangedTimerTick);
m_delayedTextChangedTimer.Interval = this.DelayedTextChangedTimeout;
}
m_delayedTextChangedTimer.Start();
}
private void HandleDelayedTextChangedTimerTick(object sender, EventArgs e)
{
Timer timer = sender as Timer;
timer.Stop();
this.OnDelayedTextChanged(EventArgs.Empty);
}
}
Another simple solution would be to add a timer to your form, set the Interval property to 250 and then use the timer's tick event as follows:
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
Calculate(); // method to calculate value
}
private void txtNumber_TextChanged(object sender, EventArgs e)
{
timer1.Stop();
timer1.Start();
}
If you are using WPF and .NET 4.5 or later there is a new property on the Binding part of a control named "Delay". It defines a timespan after which the source is updated.
<TextBox Text="{Binding Name, Delay=500}" />
This means the source is updated only after 500 milliseconds. As far as I see it it does the update after typing in the TextBox ended. Btw. this property can be usefull in other scenarios as well, eg. ListBox etc.
I faced the same challenge, and here is my simple approach. This works without issues.
public partial class Form2 : Form
{
static int VALIDATION_DELAY = 1500;
System.Threading.Timer timer = null;
public Form2()
{
InitializeComponent();
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox origin = sender as TextBox;
if (!origin.ContainsFocus)
return;
DisposeTimer();
timer = new System.Threading.Timer(TimerElapsed, null, VALIDATION_DELAY, VALIDATION_DELAY);
}
private void TimerElapsed(Object obj)
{
CheckSyntaxAndReport();
DisposeTimer();
}
private void DisposeTimer()
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
private void CheckSyntaxAndReport()
{
this.Invoke(new Action(() =>
{
string s = textBox1.Text.ToUpper(); //Do everything on the UI thread itself
label1.Text = s;
}
));
}
}
You can handle the LostFocus event of the text box which will fire everytime the user finishes typing and navigates away from the text box. Here is the documentation on LostFocus: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.lostfocus.aspx
However, I am not sure what exactly you are trying to do here as the question is not very clear about what "finish" means.
In UWP, I did a delayed check by making a static lastTimeOfTyping and checking the time when the "TextChanged" event happened. This waits till the static lastTimeOfTyping matches when a new "TextChanged" time matches and then executes the desired function.
private const int millisecondsToWait = 500;
private static DateTime s_lastTimeOfTyping;
private void SearchField_OnTextChanged(object sender, TextChangedEventArgs e)
{
var latestTimeOfTyping = DateTime.Now;
var text = ((TextBox)sender).Text;
Task.Run(()=>DelayedCheck(latestTimeOfTyping, text));
s_lastTimeOfTyping = latestTimeOfTyping;
}
private async Task DelayedCheck(DateTime latestTimeOfTyping, string text)
{
await Task.Delay(millisecondsToWait);
if (latestTimeOfTyping.Equals(s_lastTimeOfTyping))
{
// Execute your function here after last text change
// Will need to bring back to the UI if doing UI changes
}
}
As an asynchronous extension method. Adapted from Grecon14's answer.
Note: This is lacking any consideration for cursor position changes, so if the user is moving around with the arrow keys but not actually changing the text it would return true. The question states "finished typing" and I'm not sure if moving the cursor around constitutes actually typing, maybe? As a user I would want it to incorporate this activity. Unfortunately it would need to be more complex than the following for proper interface functionality. See SurfingSanta's answer which has a keydown subscription if you need that.
public static class UIExtensionMethods
{
public static async Task<bool> GetIdle(this TextBox txb)
{
string txt = txb.Text;
await Task.Delay(500);
return txt == txb.Text;
}
}
Usage:
if (await myTextBox.GetIdle()){
// typing has stopped, do stuff
}
I don't know if the onChange() only exists in an older version of c#, but I can't find it!
The following works for detecting when a user either hits the Enter key, or tabs out of the TextBox, but only after changing some text:
//--- this block deals with user editing the textBoxInputFile --- //
private Boolean textChanged = false;
private void textBoxInputFile_TextChanged(object sender, EventArgs e) {
textChanged = true;
}
private void textBoxInputFile_Leave(object sender, EventArgs e) {
if (textChanged) {
fileNameChanged();
}
textChanged = false;
}
private void textBoxInputFile_KeyDown(object sender, KeyEventArgs e) {
if (textChanged & e.KeyCode == Keys.Enter) {
fileNameChanged();
}
textChanged = false;
}
//--- end block --- //
You can use textbox onChange() event. If text is changed in textbox, check if entered value is a number and calculate total value according to the other value.
You want to use handle either the Leave or LostFocus event for the textbox in question. I'm assuming you are using WinForm even though you don't state it in your question.
What if you trigger an event based on a keystroke like tab or return?
A coworker of mine suggested a solution using Rx and event throttling:
var FindDelay = 500;//milliseconds
//textBox is your text box element
Observable.FromEventPattern<EventArgs>(textBox, "TextChanged")
.Select(ea => ((TextBox) ea.Sender).Text)
.DistinctUntilChanged()
.Throttle(TimeSpan.FromMilliseconds(FindDelay))
.Subscribe(text => {
//your handler here
});
Ideally an inheritance solution like esskar’s is the way to go but it doesn’t play well with the designer so to get the re-use I opted for a helper style side-class:
using System;
using System.Threading;
using System.Windows.Forms;
using Timer = System.Threading.Timer;
internal class DelayedText : IDisposable
{
private readonly EventHandler _onTextChangedDelayed;
private readonly TextBox _textBox;
private readonly int _period;
private Timer _timer;
public DelayedText(TextBox textBox, EventHandler onTextChangedDelayed, int period = 250)
{
_textBox = textBox;
_onTextChangedDelayed = onTextChangedDelayed;
_textBox.TextChanged += TextBoxOnTextChanged;
_period = period;
}
public void Dispose()
{
_timer?.Dispose();
_timer = null;
}
private void TextBoxOnTextChanged(object sender, EventArgs e)
{
Dispose();
_timer = new Timer(TimerElapsed, null, _period, Timeout.Infinite);
}
private void TimerElapsed(object state)
{
_onTextChangedDelayed(_textBox, EventArgs.Empty);
}
}
Usage, in the form constructor:
InitializeComponent();
...
new DelayedText(txtEdit, txtEdit_OnTextChangedDelayed);
I haven't kicked it hard, but seems to work for me.
If user is typing fast and you want to delay the calculation until he stopped typing then below code may help:
private void valueInput_TextChanged(object sender, EventArgs e)
{
CalculateAfterStopTyping();
}
Thread delayedCalculationThread;
int delay = 0;
private void CalculateAfterStopTyping()
{
delay += 200;
if (delayedCalculationThread != null && delayedCalculationThread.IsAlive)
return;
delayedCalculationThread = new Thread(() =>
{
while (delay >= 200)
{
delay = delay - 200;
try
{
Thread.Sleep(200);
}
catch (Exception) {}
}
Invoke(new Action(() =>
{
// do your calcualation here...
}));
});
delayedCalculationThread.Start();
}
Most straight forward approach.
*.xaml
<TextBox Name="Textbox1"
TextChanged="Textbox1_TextChanged"/>
*.xaml.cs
using System.Threading.Tasks;
public bool isChanging = false;
async private void Textbox1_TextChanged(object sender,
TextChangedEventArgs e)
{
// entry flag
if (isChanging)
{
return;
}
isChanging = true;
await Task.Delay(500);
// do your stuff here or call a function
// exit flag
isChanging = false;
}
I had the same problem and i think the simplest solution is to use the LostFocus event:
xaml
<TextBox x:Name="YourTextBox" LostFocus="YourTextBox_LostFocus" />
xaml.cs
private void YourTextBox_LostFocus(object sender, RoutedEventArgs e)
{
//Your code here
}
I wanted to commit a textbox both on Return/Tab and on LostFocus, so i have used this convoluted solution, but it works.
public static void TextBoxEditCommit(TextBox tb, Action<TextBox>OnEditCommit)
{
if (OnEditCommit == null)
throw new ArgumentException("OnEditCommit delegate is mandatory.");
//THis delegate fire the OnEditCommit Action
EventHandler _OnEditCommit = delegate(object sender, EventArgs e)
{ OnEditCommit(tb); };
//Edit commit on Enter or Tab
tb.KeyDown += delegate (object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
{
//Temporary remove lostfocus event for avoid double commits
tb.LostFocus -= _OnEditCommit;
OnEditCommit(tb);
tb.LostFocus += _OnEditCommit;
}
};
//Edit commit on LostFocus
tb.LostFocus += _OnEditCommit;
}
You can use this event generator with this simple code:
//Check for valid content
UIUtil.TextBoxEditCommit(tbRuleName, (tb) => {
//Your code here, tb.text is the value collected
});

textbox.Focus() not working in C#

am wondering why this code fails to focus the textbox...?
private void sendEmail_btn_Click(object sender, EventArgs e)
{
String sendTo = recipientEmail_tbx.Text.Trim();
if (!IsValidEmailAddress(sendTo))
{
MessageBox.Show("Please Enter valid Email address","Cognex" MessageBoxButtons.OK, MessageBoxIcon.Error);
recipientEmail_tbx.Focus();
}
}
Use Select() instead:
recipientEmail_tbx.Select();
Focus is a low-level method intended primarily for custom control authors. Instead, application programmers should use the Select method or the ActiveControl property for child controls, or the Activate method for forms.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.focus.aspx
Add Delay some miliSec. Delay then call Focus() and Not forget to put inside Dispatcher.
Task.Delay(100).ContinueWith(_ =>
{
Application.Current.Dispatcher.Invoke(new Action(() =>
{
TextBoxNAme.Focus();
}));
});
Even I tried with lots of above solutions but none of them worked for me as am trying to focus on page load. Finally I got this solution and it worked.
private void txtBox_LayoutUpdated(object sender, EventArgs e)
{
txtBox.Focus();
}
Use the Form_Activated event handler, in conjunction with a firstActivation boolean.
private bool firstActivation = true;
private Control firstWindowsControl = null;
...
private void DynamicForm_Activated(object sender, EventArgs e)
{
if (firstActivation)
{
firstActivation = false;
bool fwcPresent = (firstWindowsControl != null);
Console.WriteLine($"DynamicForm_Activated: firstWindowControl present: {fwcPresent}");
if (fwcPresent)
{
firstWindowsControl.Focus();
}
}

Pause and Resume a For-Loop in C#

I'm working on a windows App in C#, I have a for-loop which update something in a loop, and I have 3 buttons on the form named "Stop,Pause,Resume". So the purpose is as same as the buttons named. Does anyone know how to do this?
Here is the Loop
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
setGeneralValue();
for (int i = 1; i <= autoGridView.Rows.Count - 1; i++)
{
if (SRP == "Pause") // this is what I was thinking but it won't work
{ // it will step into end-less loop
do // how to stop this loop on "Resume" button click
{
}while(SRP!="Resume")
}
car = false;
try
{
MemberID = Convert.ToInt64(autoGridView.Rows[0].Cells["Member_ID"].Value);
DispID = Convert.ToString(autoGridView.Rows[0].Cells["Disp_Id"].Value);
Mobile = Convert.ToString(autoGridView.Rows[0].Cells["Mobile"].Value);
DueDate = Convert.ToString(autoGridView.Rows[0].Cells["Due_Date"].Value);
}
catch (Exception)
{
MessageBox.Show("Row Not Found");
}
AutoRecharge(network_name, pack_name, Mobile, Mobile, Convert.ToString(autoGridView.Rows[0].Cells["Rck_Amt"].Value), vendor_id, vendor_pwd, pack_id, oxinetwork_id);
autoGridView.Rows.RemoveAt(0);
}
}
Here are the 3 button events in which I'm setting a variable
private void btnPause_Click(object sender, EventArgs e)
{
SRP = "Pause";
}
private void btnStop_Click(object sender, EventArgs e)
{
SRP = "Stop";
autoGridView.DataSource = "";
}
private void btnResume_Click(object sender, EventArgs e)
{
SRP = "Resume";
}
The reason this doesn't work as you expect is this:
A Windows Forms application uses a single UI thread, which continually processes incoming messages from a queue. Any event handlers you attach to the events of a Windows Forms control get sent to this queue and processed by the UI thread as quickly as possible.
Your btnCompleteAuto_Click is one such handler. Once it starts, nothing else will be processed by the UI thread until it exits. Thus any other handlers you attach to other events (btnPause_Click, btnStop_Click, etc.) must wait their turn, as they will run on the same (UI) thread.
If you want pause/resume functionality, this has to be achieved on a separate thread.
A possible way to implement it might be to use a BackgroundWorker, as suggested by saurabh.
Here is a rough sketch of what your updated code might look like (I have not even attempted to compile this, let alone debug it; it's intended only as a basic outline of how you might accomplish this functionality).
You need to be aware, however, that accessing UI controls directly from a non-UI thread is a no-no. Use a mechanism such as the BackgroundWorker.ProgressChanged event to handle any UI updates that you need to happen based on activity on a non-UI thread.
ManualResetEvent _busy = new ManualResetEvent(false);
private void btnCompleteAuto_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
_busy.Set();
btnAutoCompleteAuto.Text = "Pause";
backgroundWorker.RunWorkerAsync();
}
else
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Resume";
}
btnStop.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
_busy.Set();
backgroundWorker.CancelAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// for (something)
// {
_busy.WaitOne();
if (backgroundWorker.CancellationPending)
{
return;
}
// Do your work here.
// }
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_busy.Reset();
btnAutoCompleteAuto.Text = "Start";
btnStop.Enabled = false;
}
After Reading your actual requirement in our comment , i would suggest that use Background worker class which supports cancellation of running process.
See here

Categories