I have several buttons, and I want them to do something when the cursor has been positioned over them for an already specified time. In this case they should just write their content in a textbox.
This is the Timer:
private static System.Timers.Timer myTimer =
new System.Timers.Timer(1500);
This is the method the buttons execute with the MouseEnter event:
private void keysHover(object sender, EventArgs e)
{
myTimer.Elapsed += delegate { keysHoverOK(sender); };
myTimer.Enabled = true;
}
And this is what gets executed if the Timer finishes:
private void keysHoverOK(object sender)
{
this.Dispatcher.Invoke((Action)(() =>
{
txtTest.Text += (sender as System.Windows.Controls.Button).Content.ToString();
}));
myTimer.Enabled = false;
}
I don't quite understand why this is happening, but everytime one of the buttons completes the Timer the keysHoverOK method will write as many characters as there have been hovered. For example, if I hover over the button A, it will write A, if I then hover over the button B, it will write AB, thus getting AAB written on the textbox and so on and so forth, the sentence executes as many times as the rest of the buttons have executed the keysHover method, even if they didn't complete the Timer themselves, it's like their content got saved somewhere. Now of course all I want the buttons to do is to write their content and their content only. So do you have an idea of what I'm doing wrong?
Do you mean the MouseEnter event? I'm not aware of any MouseOver event in WPF.
Without a good, minimal, complete code example, it's impossible to know for sure what the problem is. However, based on the small amount of code you've shared and your problem description, it appears that your main issue is that you're sharing a single Timer object with multiple controls. This is exacerbated by the fact that when one control subscribes to the Timer.Elapsed event, it never unsubscribes. So if another control enables the timer (subscribing to the event as well), both controls are notified when the timer interval elapses.
Even a single control is problematic, as it subscribes itself to the event each time the MouseEnter event is raised.
The fix is to disable the timer and unsubscribe from the event when the mouse leaves the bounds of the control, or when the timer interval has elapsed. That might look something like this:
private EventHandler _timerElapsedHandler;
// Subscribed to the MouseEnter event
private void keysHover(object sender, EventArgs e)
{
_timerElapsedHandler = delegate { keysHoverOK(sender); };
myTimer.Elapsed += _timerElapsedHandler;
myTimer.Enabled = true;
}
// Subscribed to the MouseLeave event
private void keysLeave(object sender, EventArgs e)
{
DisableTimer();
}
private void keysHoverOK(object sender)
{
this.Dispatcher.Invoke((Action)(() =>
{
txtTest.Text += (sender as System.Windows.Controls.Button).Content.ToString();
}));
DisableTimer();
}
private void DisableTimer()
{
myTimer.Elapsed -= _timerElapsedHandler;
myTimer.Enabled = false;
_timerElapsedHandler = null;
}
Other comments:
You should cast instead of using as. Only use as when a reference can legitimately be of a different type than you are checking for. Use a cast when it is always supposed to be the type you are checking for. That way, if you have a bug, you will get a meaningful exception, instead of just some NullReferenceException
The above example fixes the problem with the least disruption to your code. But really, I would make other changes too. For example, rather than storing the delegate in a field, I would just get the Content.ToString() value and store that. Then instead of using an anonymous method for the delegate instance, I would use a named method that simply uses the stored string value to append to the Text property. You can subscribe and unsubscribe the named method by name; the delegate type does the right thing even though it's using a different delegate instance for the subscribe and the unsubscribe.
Another change you might consider making is to use a different Timer instance for each control. Then you don't have to subscribe or unsubscribe as the mouse events occur; just subscribe during initialization.
Finally, especially as this is WPF code, you really should consider storing the appended text in an observable property (e.g. DependencyProperty, or implement INotifyPropertyChanged), and bind it to the txtTest.Text property rather than manipulating that property directly.
I'm assuming when you say:
This is the method the buttons execute with the MouseOver event:
You might mean the MouseEnter event?
From what I see:
You have one central timer
It will start the elapsed count down on the first button you enter
You have not stopped the timer if you leave that button before it elapses
You seem only to add delegates to the event without removing any
The code segment myTimer.Elapsed += delegate { keysHoverOK(sender); }; adds another delegate to the list of already added delegates. It does not replace the list with just one delegate.
If you leave the button before the timer elapses you need to remove the delegate from the timer elapsed event using the minus-equal operator (myTimer.Elapsed -= ....), and then stop the timer. Here you have a problem that you've created an anonymous method so you'd need:-
Research into removing anonymous methods
or
Research into removing all event handlers
or possibly the simplest menthod
Stop and destroy any running timer and create a new timer instance each time you enter the button.
Related
So I've been following the Visual Studio tutorials that microsoft has available (more specifically the math quiz one found at https://msdn.microsoft.com/en-us/library/vstudio/dd492172.asp)
but I deviated a bit from the tutorial because I wanted to see if I could create an event and call it using the EventHandler delegate though it might not be the best solution.
public event EventHAndler quizStarted;
here is the code for creating the event.
now in the method
public Form1()
{
this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted);
InitializeComponent();
}
I have initialized my event with an instance of the EventHanlder that points to my showThatTheQuizStarted method.
public void showThatTheQuizStarted(object sender, EventArgs e)
{
MessageBox.Show("Quiz Has Started");
}
and finally when the start button is pressed I call the quizStarted event as shown below.
private void startButton_Click(object sender, EventArgs e)
{
startButton.Enabled = false;
quizStarted(this, new EventArgs());
this.StartTheQuiz();
}
in this order the message box goes away after hitting okay once, also in StartTheQuiz() nothing calles a message box directly or indirectly.
but if I place the this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted); line into the startButton_Click method, the message box appears twice one right after the other.
Though I found a solution I would like to know why this happens if I place this line of code out of the constructor.
If...
this.quizStarted += new System.EventHandler(this.showThatTheQuizStarted);
... gets called multiple times, as would happen if you move it inside a button Click event handler method, then you are in fact adding and registering a new event handler every time.
In other words, when quizStarted is invoked, it may call multiple event handlers, depending on how many you choose to register. If you register the same event handler multiple times, then it will get called as many times.
That's why you want to leave the registration in a place where you are guaranteed to register the event handler once and only once.
I need help on firing an event within C#
Basically I have a onclick event that fires when you click on a checkbox
void OnClick(object sender, RoutedEventArgs e)
{
...
}
I need help on firing an event within C#
Basically I have a onclick event that fires when you click on a checkbox
void OnClick(object sender, RoutedEventArgs e)
{
...
}
However, I need to fire this event once another event has been fired, so within this new event, is it possible I can fire the above one?
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
switch(dataGrid.Name)
{
case "Customer"
//fire OnCLick Event
break;
}
}
I have tried something like
??? += new MouseEventHandler(OnClick);
But I am not sure if this will actually work.
Yes you can, but only if the event is in your own class. You can't even raise a base class' event. You have a put a method in the base class to raise the event, and then call that.
The code you put there is adding another event handler, not raising an event; you don't need to do that.
If it's a button, use btnDoSomething.PerformClickEvent (winforms)
If the handler is in your code, you can call it without raising the event (commenters assume that this is what you want to do but in reaslity there are many cases where you'd need more than this) btnDoSomething_Click(null, null) - null usually works because handler code rarely cares about the sender or arguments and if you don't reference them, you don't need them.
If you can use #4, you can also refactor as mentioned. Usually not needed. But usually so easy to do you it's worth doing for clarity anyway.
For objects that map from Windows widgets of anysort, check out the SendMessage and PostMessage API calls. Wayyyy beyond the scope of this answer, though. Doesn't apply to non-windows-backed objects (but your sample implies windows).
Assuming program logic changes a button tag based on something random - but using the UI thread.
Is the Button Tag value reliable to use in a click event? i.e. Will it be the same value as-at the time of the event handler as it was at the time of the click?
If not, what is the best method to pass an event specific parameter into a button click event that will be safe?
Update
Added an example as requested. (Remember this is just theoretical).
Windows.Forms.Timer timer = new Timer();
timer.Interval = 1;
timer.Tick += new EventHandler(timer_tick);
timer.Start();
void timer_tick(object sender, EventArgs e)
{
this.button.Tag = Random.NextInt(100).ToString();
}
void button_click(object sender, EventArgs e)
{
string s = (string)((Button)sender).Tag;
Console.WriteLine("Tag value as at button push: " + s);
}
Put another way, the question boils down to: can events be wedged into the GUI event queue that allow the state of the button to be changed between the button being pushed and the click event handling the push?
Assuming that you are using winforms.
IMHO you can use Tag property to pass control specific parameters but you must also remember that windows forms controls are not thread safe. To make a thread-safe call, you must use InvokeRequired. The following link gives an example to set text for TextBox in a thread-safe way but it should not be very different for Tag property.
http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx
Hope this helps.
I'm not sure if it's a good idea, and this is probably going to end up as more of an academic exercise, so bear with me:
Let's say I was making a UserControl (as I am), that will every X seconds (using a Timer) look for a text file and display that information within this UserControl. Let's call this control MyUserControl.
To rehash the basic:
I would have this Timer as a member variable of this control, and have code that looks like:
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
And an event handler method that looks like this:
private void timer1_Tick(object sender, EventArgs e)
{
// Read text-file and do lots of goodness.
}
Cool, so now I can set a public property of MyUserControl to some elapsed time, and just by plopping this control on my Form, I have a nice little control updating itself. Neat.
Here's the thing that got me curious:
The Form I'm going to plop it on already has a Timer. So why have two competing timers? I'd love for there to be a way for MyUserControl to only use it's internal timer if it's needed (for example, plopping it on another Form that doesn't already have a Timer).
I suppose I can make the MyUserControl timer1_Tick public like this here:
public void timer1_Tick(object sender, EventArgs e)
{
// Read text-file and do lots of goodness.
}
And then, because these things are multicast, do something like this here in the form:
this.theFormTimer.Tick += new System.EventHandler(this.theFormTimer_Tick);
this.theFormTimer.Tick += new System.EventHandler(MyUserControlObject.timer1_Tick);
And also set a member variable of MyUserControl to not enable itself.
But is there some slicker way of doing this? Can I determine somehow that the event handler method in MyUserControl was already wired to the Form Timer's tick event? Or use some other type of approach than what I'm suggesting?
Thanks! (apologies if this ends up being a duplicate, my google-fu may be bad today)
Not discussing if you should or shouldn't do this, but to answer the question, I'd do it this way:
//add a handler
timer.Tick += timer_tick;
//...
//check if timer_tick is wired to Tick
if (timer.Tick.GetInvocationList().Contains (timer_tick))
{
//do something
}
I haven't tested it, but that's what I'd try.
Have a simple application with a start/stop button that I want to do different things depending on it's current state. If button is in Start state, execute code then change to stop state and change OnClick event to StopButton_Click and vice versa.
Can't seem to change the on-click property of the button, so using code below which works, but keeps adding instances of the event. First click executes once, second click executes twice, third executes four times, ad infinitum.
StartButton.Click += new System.EventHandler(StartButton_Click);
alternates with
StartButton.Click += new System.EventHandler(StopButton_Click);
Is there a way to REPLACE the OnClick handler instead of adding to it?
Try removing the event handler before adding a new one:
StartButton.Click -= StartButton_Click;
One option is to remove the previous event handler before adding another, but a simpler option is to just use a single event handler. The event handler can look at some internal state field to determine what to do; this will likely be easier than constantly adding/removing event handlers.
It may look something like this:
private void buttonClick(object sender, EventArgs args)
{
if(buttonState == MyStateEnum.Start)
PerformStartAction();
else if(buttonState == MyStateEnum.Stop)
PerformStopAction();
}
Then instead of adding/removing event handlers you just need to assign a different value to buttonState.
+= works with events as it does anything else. It adds to what is already there.
Try removing the existing event handler with -= and then adding the new one.