I made two buttons which controls scrolling on a DataGrid OnClick. I'll like to execute the code managing the scroll when users stay press on it.
I tried on MouseDown() but the code is execute only one time.
Need help.
When you get a mouse down event, set a timer to start calling a "scroll" callback function every 200ms or so (random guess on the time).
In the timer callback, scroll by one "notch" (however much you make it.)
When you get a mouse up event, stop the timer.
If you don't want to use the timer, you can always spawn a thread when needed. You only have to be careful to use Invoke() mechanism when using the UI controls, which are on the other thread.
Code:
private bool mouseDown = false;
private void buttonScrollUp_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = true;
new Thread(() => {
while (mouseDown)
{
Invoke(new MethodInvoker(() => [DO_THE_SCROLLING_HERE));
Thread.Sleep([SET_AUTOREPEAT_TIMEOUT_HERE);
}
})
.Start();
}
private void buttonScrollUp_MouseUp(object sender, MouseEventArgs e)
{
mouseDown = false;
}
Code snippet above of course lacks some sanity and error cheks.
LP,
Dejan
Main idea is to implement timer, for example every 100ms, and do your logic in tick event. Algorithm can look like:
Capture mouse and start timer in MouseDown event
In MouseMove detect is cursor still over button, if no set flag
In timer tick check flag is mouse over button, and do your scroll logic
Release mouse capture and stop timer in MouseUp event
Related
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 noticed strange behaviour of left-click event for NotifyIcon.
I have a code like this:
private void notifyIcon2_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Console.WriteLine("Hello!");
}
}
The problem is that upon clicking notifyicon in tray string "Hello" is not shown immediately, it takes about 0.5 seconds (half of a second) to react. That is why I can not add some sort of variable-counter for each click of the icon - it just reacts too slow to catch all clicks and increment my variable.
Is there any solution to the problem? I tried MouseClick, MouseDown, MouseUp and Click events, and all of them have such a slow reaction.
Thank you!
I think it is related to this little comment they make here (I know this is not this NotifyIcon).
Note that the LeftClickCommand fires after a short delay (as opposite to the DoubleClickCommand that fires immediately). This is because there is a time span between a first click and a second click for the OS to consider the mouse action a double-click. The NotifyIcon is smart enough to wait this period in order to make sure the LeftClickCommand is only fired if the user does not click a second time within that period.
I tried it and this delay is present on the Form itself as well. This is just how this event works.
Implementing a handler for the DoubleClick event was not a solution in my case where I wanted only the single click to open the NotifyIcon's popup.
I found the NoLeftClickDelay property in the code completion that makes things to work as wanted.
TaskbarIcon tbIcon = (TaskbarIcon)FindResource("MyNotifyIcon");
tbIcon.NoLeftClickDelay = true;
In my WP8 app that controls Lego Mindstorms I have a Button with UIElement.Hold Event that triggers method runMotor() When I release the Button motor keeps on going but I would like it to stop. Method for stopping is stopMotor(), I've already tried to assign it to KeyUp Event but it doesn't work. Any solutions?
You can try to call stopMotor() in ManupulationCompleted event. Note that ManipulationCompleted event will get invoked after any gesture manipulation including Tap, Double Tap, Hold, and other gesture. Take that into account. If application scenario is still simple, checking if motor already running before calling stopMotor in ManipulationCompleted event handler maybe enough :
private void MyButton_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
{
if(isMotorRunning) stopMotor();
}
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
I tried this XAML:
<Slider Width="250" Height="25" Minimum="0" Maximum="1" MouseLeftButtonDown="slider_MouseLeftButtonDown" MouseLeftButtonUp="slider_MouseLeftButtonUp" />
And this C#:
private void slider_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
sliderMouseDown = true;
}
private void slider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
sliderMouseDown = false;
}
The sliderMouseDown variable never changes because the MouseLeftButtonDown and MouseLeftButtonUp events are never raised. How can I get this code to work when a user has the left mouse button down on a slider to have a bool value set to true, and when the mouse is up, the bool is set to false?
Sliders swallow the MouseDown Events (similar to the button).
You can register for the PreviewMouseDown and PreviewMouseUp events which get fired before the slider has a chance to handle them.
Another way to do it (and possibly better depending on your scenario) is to register an event handler in procedural code like the following:
this.AddHandler
(
Slider.MouseLeftButtonDownEvent,
new MouseButtonEventHandler(slider_MouseLeftButtonDown),
true
);
Please note the true argument. It basically says that you want to receive that event even if it has been marked as handled. Unfortunately, hooking up an event handler like this can only be done from procedural code and not from xaml.
In other words, with this method, you can register an event handler for the normal event (which bubbles) instead of the preview event which tunnels (and therefore occur at different times).
See the Digging Deeper sidebar on page 70 of WPF Unleashed for more info.
Try using LostMouseCapture and GotMouseCapture.
private void sliderr_LostMouseCapture(object sender, MouseEventArgs e)
private void slider_GotMouseCapture(object sender, MouseEventArgs e)
GotMouseCapture fires when the user begins dragging the slider, and LostMouseCapture when he releases it.
I'd like to mention that the Slider doesn't quite swallow the entire MouseDown event. By clicking on a tick mark, you can get notified for the event. The Slider won't handle MouseDown events unless they come from the slider's... slider.
Basically if you decide to use the
AddHandler(Slider.MouseLeftButtonDownEvent, ..., true)
version with the ticks turned on, be sure that the event was handled previously. If you don't you'll end up with an edge case where you thought the slider was clicked, but it was really a tick. Registering for the Preview event is even worse - you'll pick up the event anywhere, even on the white-space between ticks.