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.
Related
I am following this walkthrough on MSDN: Creating a Custom Tab by Using the Ribbon Designer
Looking at steps 3 and 4:
In step 3 it adds an event handler to the ribbon_Load function, basically adding a click event to a button in the ribbon:
private void MyRibbon_Load(object sender, RibbonUIEventArgs e)
{
this.button1.Click += new RibbonControlEventHandler(this.button1_Click);
}
Then, in step 4 they add another event handler in the way that I am more used to, like so:
private void button1_Click(object sender, RibbonControlEventArgs e)
{
MergeReportInterface ui = new MergeReportInterface();
ui.ShowDialog();
}
I am not really understanding the purpose of this, because all it does is cause the event to fire twice. If I comment out the event handler that was added to the load function the event occurs once.
Could someone please explain to me what the point of this is? if there is any, or if there is some error on the MSDN site. What should be the proper way to handle a ribbon click event?
private void button1_Click(object sender, RibbonControlEventArgs e)
{
MergeReportInterface ui = new MergeReportInterface();
ui.ShowDialog();
}
This is not adding an event handler. This is the method that your event will call.
this.button1.Click += new RibbonControlEventHandler(this.button1_Click);
This is saying 'When button1 fires its Click event, call this.button1_Click'.
Your code only sets up one event handler, it should only fire once.
However, it's likely you created the button1_Click method by double clicking a button on your form designer. This, behind the scenes, adds an additional event handler. This is why you're getting the event fired twice.
So you have two options:
Go back into the IDE and remove the click handler via your form designer. Go to your code and manually write the method button1_Click.
OR
Remove this line: this.button1.Click += new RibbonControlEventHandler(this.button1_Click);, as VisualStudio is doing that for you automatically.
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.
IDE: Visual Studio 2010
Language: c# .net
I am generating events for buttons manually from properties. But, its becoming very lengthy process if there are suppose 20 buttons doing the same task like 'Mouse Hover' and 'Mouse Leave' . So, is there a way to copy events for all the other buttons ?
You can subscribe all your buttons to same event handler:
foreach(var button in Controls.OfType<Button>())
{
button.MouseHover += Button_MouseHover; // add handler
button.MouseLeave += Button_MouseLeave;
}
In that handler you can determine which exact button raised even by casting event source to button type:
private void Button_MouseHover(object sender, EventArgs e)
{
var button = (Button)sender; // sender is one of buttons
// use button.Name
}
Code above subscribes to events of all buttons. But if you want to filter them (e.g. by name) you can add filtering:
Controls.OfType<Button>().Where(b => b.Name.StartsWith("foo"))
Buttons can all share the same event, there's no need to have a seperate event for each button if they're doing similar tasks. (The object sender parameter will give you the Control which was clicked.
IF you select all the buttons (by keeping the ctrl key pressed) in the designer, you can then easily assign 1 event to all 20 buttons
In life you will not find shortcuts for everything,
In short there is no shortcut, but yes as mentioned in other post if you have same event handler and same action to be taken then this will help you reduce your work.
You don't have to do this manually, you can add event handlers from code as well. Also, if the logic is quite similar for all the buttons then you can use single event handler for all of them. Every event handler has sender property what will be set to the button that caused event.
Simple example would be something like this:
//at first assign event handlers
button1.Click += new EventHandler(Button_Click);
button2.Click += new EventHandler(Button_Click);
//single event handler
private void Button_Click(object sender, System.EventArgs e)
{
// Add event handler code here.
Debug.WriteLine("You clicked: {0}", sender);
}
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.
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