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.
Related
I would want to launch a KeyDown Event on Form_Load however its taking me somewhere else in the Form_Load event.
Form_Load:
int static_int = 0;
private void Form1_Load(object sender, EventArgs e)
{
if(condition == true)
{
txtInput.Text = "something";
txtInput.Focus();
SendKeys.Send("{Enter}");
int somegeneratednubmer = 20;
static_int = static_int + somegeneratednumber;
//somemore code here
}
}
KeyDown:
private void txtInput_KeyDown(object sender, KeyEventArgs e)
{
if(e.KeyCode == Keys.Enter)
{
static_int = 10;
//somemore codes here too
}
I would like to get the SUM of static_int and somegeneratednumber which is 30. However, after Debugging, I'm getting its initialized value of 0. From what I understood, after SendKeys.Send("{Enter}") the KeyDown event should proceed.
Why is it not??
How would I get the correct result? I really should do the KeyDown event on Form_Load, a conditional event...
or What am I doing wrong here?
Note: originally static_int is initialized on a Class
No, the KeyDown even will proceed at the earliest possible moment, which is when the appropriate message is executed from the form's message queue. That cannot happen before the Load event finishes, because that also on the message queue. Even if that weren't the case, SendKeys doesn't wait for the action to be processed. It just sends the message and returns immediately.
Another problem is that SendKeys sends the virtual keys to the currently active window. That can never be your window, since your window isn't even shown yet! When something behaves weird, a good first step is to read the documentation.
So, why is the value of static_int zero, instead of 20 or 30? Well, the likeliest case is an unhandled exception, and I'm pretty sure that's exactly what happens when you do tbxInput.Focus. The control doesn't quite exist yet, and it can't be made the input focus. If you have trouble understanding all this, you might want to find some book on the basics of how Windows windows work - there's nothing .NET can do about it, and it's places like this where the (very pretty) .NET abstraction leaks a lot. If you're planning to do any Windows UI development, you really need to know at least the basics.
However, that's completely unnecessary anyway. You don't have to execute a KeyDown event. Just make a method that's called from both the Load event handler and the KeyDown event handler.
try adding this event instead
Form1 isn't loaded yet so no events yet.
private void Form1_Shown(Object sender, EventArgs e)
{
SendKeys.Send("{Enter}");
}
But truly this design is wrong
I have a question when programing in c#. I want to call an event from another event like this.
private void button1_Click(object sender, EventArgs e)
{
Form2 formulario = new Form2();
formulario.ShowDialog();
// here i call an event from the second form. that event is radiobutton_checkedchange
formulario.radioButton1_CheckedChanged(sender, e);
The problem is that i look everyware how to solve this problem... They said me that an event is like a method but i think is not the same, because when i call that event like a method it looks like i call it just once. The event dont recognize the checked change.
am I wrong ? is this posible in c#. Thanks to all, i'm new in programing with events. And sorry for my bad english
First, radioButton1_CheckedChanged is not an event, it is probably an event handler. In the end, it is still a method like all others.
You shouldn't directly call the event handler of an event, just create another method and what the call there. Put this in your Form2:
private void radioButton1_CheckedChanged(object sender, EventArgs e)
{ // private, only accessible from the class itself
this.SetState();
}
public void SetState()
{ // public, accessible from anywhere
// put your original code from radioButton1_CheckedChanged here
}
In that way, you don't have to call event specific code, but you can write that away in a separate method, which is easier to use and clearer in its purpose.
If both forms share the same data object / view model, you can set the value from there. That would be better from a OOP perspective. Look into MVVM or MVC for good design patterns to do so.
It sounds like you have logic in radioButton1_CheckedChanged that you also want to call when the button is clicked. If that's the case, then move the common logic to a new method and call it from both places.
If you want to change the checked status of radioButton1 then just change its status. Event hnadlers respond to UI changes, they do not generate them.
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.
I'm new to c# so apologies if this is a noob question. I'm trying to get clarity around the syntax or pattern for handling events in c#.
So I have a Form object Form1 and a Button button1 in the form. I handle a Click event with a method like this in Form1.cs:
private void button1_Click(object sender, EventArgs e)
{
Debug.WriteLine("click!");
}
which works fine. Now in another form Form2 I have a TreeView treeView1, and I want to handle the BeforeExpand event. So I assumed it would be:
private void treeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
{
Debug.WriteLine("Hello!");
}
which in fact doesn't work: this method is never called when I expand a node. But several SO answers imply this is the way to do it, e.g this one.
Anyway I found an alternative approach which does work for me. In the form constructor bind the event handler like this:
treeView1.BeforeExpand += new TreeViewCancelEventHandler(anyMethodNameYouLike);
So what's the difference between these two approaches? Why doesn't the _event syntax work for a treeview? Is there some difference between the event types?
Thanks
I assume you doubleclicked the button in the Visual Studio designer. The button1_Click handler got added automatically, just like you created the BeforeExpand handler by hand.
Check your Form1.Designer.cs file, you'll find a line something like this:
this.button1.Click += new System.EventHandler(this.button1_Click);
You need both things:
1) a method that can handle the type of event in question. For a TreeViewCancelEventHandler(MSDN) delegate the correct method signature is:
public void MyMethodNameGoesHere(Object sender,TreeViewCancelEventArgs e)
{
// do some impressive stuff here
}
2) you have to register for the event:
treeView1.BeforeExpand += new TreeViewCancelEventHandler(MyMethodNameGoesHere);
You could also use just the method name:
treeView1.BeforeExpand += MyMethodNameGoesHere;
And as a last alternative you might use this 'inline' syntax for small functions:
treeView1.BeforeExpand += (sender, e) =>
{
// do a bit of magic here
};
What is possibly good to know is that handler registrations don't 'stack' (sorry for the poor wording, suggestions for improvement are very welcome!).
Meaning when you do the following you will not receive further events after the last line has executed:
treeView1.BeforeExpand += MyMethodNameGoesHere;
treeView1.BeforeExpand += MyMethodNameGoesHere;
treeView1.BeforeExpand += MyMethodNameGoesHere;
treeView1.BeforeExpand -= MyMethodNameGoesHere; // note the MINUS sign
What you have is correct. You need to add the the handler to the event as you have done in the second case.
There must be a line like this:
button1.Click += button1_Click;
(possibly with a new EventHandler() wrapper) somewhere in your Form1 code, most probably in the .designer.cs file.
The first syntax, which isn't syntax at all, is just a naming convention for event handlers. What you're doing with the second syntax is setting up a delegate to an event handler and adding it to that event.
If you check Form1, and click select the Button and look at its events properties, the event is most likely hooked up through the designer. You could do the same with the TreeView on its form, through the designer.
The C# event handling system does not work on any sort of naming convention (I think you may believe this to be the case?). A method called treeView1_BeforeExpand will not be called on treeview1's BeforeExpand event unless you tell it to call that method on that particular event.
The code below says "when the BeforeExpand event is fired, invoke the anyMethodNameYouLike method.
treeView1.BeforeExpand += new TreeViewCancelEventHandler(anyMethodNameYouLike);
You have to write your anyMethodNameYouLike method.
In WinForms, you can bind event handlers from the designer using the "properties" toolbox and clicking the lightning button at the top. This will open the events tab. Just double click on the name of an event to generate an handler in your code or select an existing method with the right signature from the dropdown. This will actually generate the "+=" code for you in the .designer file.
The name Control_Event is automatically generated. However, you can use any name for your handlers and even register more than one.
That is the correct way to subscribe to an event. The designer automatically adds that line to the .designer.cs file when you subscribe to the event via it.
Either way, this line of code is getting written somewhere. The difference is one event is being subscribed to via the designer, while the other you are subscribing to manually.
I am working on a C# WinForm application.
I want to trigger some processing once the form has been "shown" and the layout of the form is complete.
I am using the "_Shown" event, but this seems to trigger before the layout of the form has completed. Is there event I can use that fires once the layout is complete?
Put Application.DoEvents() at the start of the form's Shown event handler. This will force all the controls to be rendered.
I don't see an event after Shown you can use for this purpose. Could you not use a timer to delay your processing in the Shown event?
An old trick in VB6 used to be to use the Paint event:
bool firstShown = false;
void form_Paint(Object sender, EventArgs e) {
if ( !firstShown ) {
YourMethodThatNeedsToRunOnShown();
firstShown = true;
}
//the rest of your paint method (if any)
}
It is a little hacky, but it does work
This works for me and is much less "hacky" than other suggestions:
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if(someControl == null)
return; // be careful of OnLayout being called multiple times
// otherwise, do some stuff here, set control sizes, etc.
}
AS far as I can remember the event order is something like
Form.Load
Form.Layout
Form.VisibleChanged
Form.GotFocus
Form.Activated
Form.Shown
So if something is still happening after Form.Show it's because of the way you coded it.
Are you maybe creating the form dynamically?
The best solution is the Shown() event: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.shown.aspx
"The Shown event is only raised the first time a form is displayed; subsequently minimizing, maximizing, restoring, hiding, showing, or invalidating and repainting will not raise this event."
Try using Form.GotFocus (inherited from control)..
something like this.
private void Form1_Load(object sender, EventArgs e)
{
this.GotFocus += new EventHandler(Form1_gotFocus);
this.Focus();
}
private void Form1_gotFocus(object sender, EventArgs e)
{
// You will need to Switch focus from form at the end of this function,
//to make sure it doesnt keep Firing.
}
According To msdn , the following happens:
When you change the focus by using the keyboard (TAB, SHIFT+TAB, and so on), by calling the Select or SelectNextControl methods, or by setting the ContainerControl..::.ActiveControl property to the current form, focus events occur in the following order:
Enter
GotFocus
Leave
Validating
Validated
LostFocus