I have a map on which I dynamically display pushpins, and would like to interact with the pushpins without interacting with the map (so I can't just disable the map by setting IsEnabled="False", nor can I use an image of the map).
I've tried the techniques depicted here: Handling tap on Pushpin in a fixed Map, which work great for zooming, and panning.
Extending this idea I've set up event handlers for all the events that would seem to match:
Hold="map_Hold"
MapPan="map_MapPan"
MapZoom="Map_MapZoom"
ManipulationStarted="map_ManipulationStarted"
ManipulationDelta="map_ManipulationDelta"
ManipulationCompleted="map_ManipulationCompleted"
MouseLeftButtonDown="map_MouseLeftButtonDown"
MouseLeftButtonUp="map_MouseLeftButtonUp"
TargetViewChanged="map_TargetViewChanged"
ViewChangeStart="map_ViewChangeStart"
ViewChangeEnd="map_ViewChangeEnd"
ViewChangeOnFrame="map_ViewChangeOnFrame"
(in the event handlers I simply set e.Handled = true;)
Yet you can still pan the map by making flicking motions on it (like how you would flick to the next image in the photo app).
Is there a way to disable the map panning altogether?
Or is there a way to interact with the pins if IsEnabled="False"?
This post was a while ago so you probably have solved the problem by now. But I had this problem today and this is the solution I had. (it's not very elegant, but seems to work fine).
For me this problem occurred when letting go of a pushpin after dragging and dropping it - Even though I had disabled the map_pan event when dragging a pushpin, the map would still move from the 'flick' gesture which is created when letting go of a push pin after dragging and dropping it.
First of all, create a page scoped DespatchTimer and an int which will be our counter:
DispatcherTimer dti = new DispatcherTimer();
int counter =0;
Next, in the pushpin's MouseLeftButton up event, disabled your map, start the despatch timer with an interval of 1. Also create the Tick event handler for it.
private void pushpin_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Map.IsEnabled = false;
dti.Interval = new TimeSpan(0, 0, 0, 0, 1); // 1 Seconds
dti.Start();
dti.Tick += new EventHandler(dti_Tick);
//other code logic
}
Next, create dti_Tick event. Within the tick event, stop the despatch timer when it is the second iteration into the tick event and re-enable the map. Reset the counter for future use.
void dti_Tick(object sender, EventArgs e)
{
counter++;
if (counter>= 1)
{
dti.Stop();
Map.IsEnabled = true;
counter= 0;
}
}
This process will have stopped any 'flick' from occuring after your drag and drop pushpin logic. Clearly this code can be further optimised, but hope it is of some help to you.
Related
What I am trying to do: I am currently hosting an OpenTK glControl (through a WindowsFormsHost) in my WPF application. The application has many buttons, but we will focus on the pause and play buttons. I use VBO's and this is how I animate GL.DrawArrays(PrimitiveType.LineStrip, 0, frameCount );. It is drawing the range of vertices from 0 to framecount, so when I hit play, it just starts incrementing framecount, which starts animating. When just using a mouse, everything works perfectly.
The Problem: My app needs to work with a touch screen also (FWIW, when I say touch, WPF is seeing it as a stylus, not touch). When the app is NOT animating, touch works like it should, I don't handle touch events so touching buttons just raised the Click event. If we aren't animating, I don't have any problems. So when I use my mouse Click the play button, the UI still responds to my Mouse (clicks work, hovering changes colors as it should, etc), but then touches seemingly get ignored for some period of time. I have to touch a button 3 to 5 times before it does what it is supposed to (like pause). Lets go back and now we aren't animating, this time, if I TOUCH the play button to raise the Click event, it will start animating, but my touches now have the same problem of having to touch multiple times to raise Click event, and on top of that, the UI now does not respond to the mouse (the WindowsFormsHost still reacts to mouse events properly). Clicking on buttons or hovering over buttons doesn't do anything. It is not until I can get the animation to pause again that the UI starts responding to Mouse again.
What I have tried and what (I think) I know: When the UI stops responding to mouse input and touch input is messsed up, if I use MOUSE or TOUCH to click anywhere outside of my app, it works as expected. This leads me to believe it is not a touch screen driver problem or anything. I also used Snoop to see what events were being raised (or not) to see the stylus and the mouse being captured and released and what has focus. I could not find an instance where one was not released. If anyone would like, I can post Snoop results showing differences between mouse and touch clicking. I tried putting a preview mouse down on the MainWindow, but after TOUCHING play, no Mouse clicks raise this event, and the first touch after touching the play button touches randomly will fire this event. I also tried hiding the WindowsFormsHost, and mouse and touch clicks all work like they should minus the glControl. Because I hide the WindowsFormsHost, the paint event never gets fired, leading me to believe there might a problem with the paint function. I am aware of this bug WPF Touch Bug, however I don't really think that this is my problem. Something tells me the interop is what is giving me problems but I am not sure.
My Code:
private void playFwdFunc()
{
//disable undrawing and enable drawing
undraw = false;
draw = true;
//unpause animation
paused = false;
//enable/disable appropriate buttons
pauseBtn.IsEnabled = true;
stepBackBtn.IsEnabled = false;
stepFwdBtn.IsEnabled = false;
clearStart.IsEnabled = true;
clearCurrent.IsEnabled = true;
//refresh control
glControl1.Invalidate();
}
private void playFwdClick(object sender, RoutedEventArgs e)
{
playFwdFunc();
}
In my glPaint event:
if (frameCount < vertices.Length && !paused)
{
//draw more vertices
if (draw)
{
if (0 < (int)(vertices.Length / (25000 / speedTrack.Value)))
frameCount += (int)(vertices.Length / (25000 / speedTrack.Value));
else
frameCount++;
}
//draw less vertices
else if (undraw)
{
if (0 < (int)(vertices.Length / (25000 / speedTrack.Value)))
frameCount -= (int)(vertices.Length / (25000 / speedTrack.Value));
else
frameCount--;
}
//make sure we dont have a negative framecount (null pointer)
if (frameCount < 0)
{
paused = true;
frameCount = 0;
// manageCodeBox();
}
//make sure we dont exceed # of vertices (null pointer)
else if (frameCount > vertices.Length)
{
frameCount = vertices.Length;
paused = true;
// manageCodeBox();
}
}
My Question: Why is clicking a button with mouse making the UI react differently than touching a button, even though they raise the same event? Any input is appreciated.
So I found what was causing the behavior, in my paint function I was constantly raising an event that had a glControl1.Invalidate().
My Best Guess: My only real guess is that certain events like touches which occur on the stylus thread some how gets into a live-lock with the UI thread when calling glControl1.Invalidate(). I think that that is why some touches got accepted, but the UI thread never reached mouse event listeners. As I said: best guess.
My Workaround: I originally made a DispatcherTimer to call glControl1.Invalidate(). Mouse events worked fine, but because touches got promoted to mouse clicks, there was a delay unless I handled the touch event (which I did not want to do for each button on my UI). My next solution seems to be working so far.
In my windows constructor: CompositionTarget.Rendering += invalidateProcessor;.
The function:
private void invalidateProcessor(object sender, EventArgs e)
{
Dispatcher.BeginInvoke(new Action(() => { if (!paused) glControl1.Invalidate(); }), DispatcherPriority.Background);
}
I chose DispatcherPriority.Background because the higher priorities cause a delay in the touches again.
I have an MdiClient derived from Form and I use the surface of this control for GDI+ drawing. I run into troubles implementing my own scrolling for this control. I set both AutoScroll and AutoSize properties to false and try to use form's own horizontal/vertical scrollbars instead of placing my own. Observed form's behavior is quite confusing. To begin with there are two properties (A) HScroll and (B) HorizontalScroll that also allows access to Visible attribute.
I ended up setting HorizontalScroll.Visible = true and leaving HScroll = false (same for vertical) but am curious why there are two of them. Documentation implies that both control visibility of horizontal scroll bar but they do not appear to access the same data. Besides, it looks like HScroll is being reset on every paint. At the moment I ignore existence of HScroll/VScroll. Is it OK for my application?
What is more critical for me is the ability control placement of the thumb on scroll bars. I set VerticalScroll attributes Minimum = 0, Maximum = 100, and Value = 50 but when form is displayed thumb is positioned at the start of scrollbar not in the middle. Why? Also when user clicks on horizontal scrollbar an event handler for horizontal scrolling is invoked but meanwhile form has already reset VerticalScroll.Value to 0 (without raising vertical scroll event). What is going on?
I probably don't understand how framework expects me to implement what I need. Can someone shed some light.
Credit goes to LarsTech who pointed me towards good solution. Setting large AutoScrollMinSize automatically does whatever needs to be done enabling and controlling form scrollbars. There is one potential trap to watch for. Be aware that programmatic attempts to set AutoScrollPosition will be ignored until form is shown. So if you want your form to open with scrollbars not in the default (0,0) position then place your code inside form_shown event handler.
Just set the AutoScrollMinSize to your desired canvas.
Quick example:
using System.Drawing;
using System.Drawing.Drawing2D;
private void Form1_Load(object sender, EventArgs e)
{
this.AutoScrollMinSize = new Size(1200, 1200);
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.Clear(SystemColors.Window);
using (Matrix mx = new Matrix(1, 0, 0, 1, this.AutoScrollPosition.X, this.AutoScrollPosition.Y))
{
e.Graphics.Transform = mx;
e.Graphics.FillEllipse(Brushes.Red, new Rectangle(250, 250, 100, 100));
}
}
See these links: Understanding Windows Forms AutoScroll and How to back-track the mouse to the virtual page.
I am trying to build a simple memory game. Clicking on a card that has not been "flipped" simply "flips" the card over (revealing the underside of the image).
When a card is already showing and a second card is flipped over, I would like to pause for one second. Then, if the card that was flipped over matches the first card, I remove it from the board, if it does not match, I would like to flip both cards back to their hidden stage.
I have the "flipping" coded, I just want to know how I can pause it for one second after the second card is flipped.
I've tried:
System.Threading.Thread.Sleep(1000)
and
Dispatcher.BeginInvoke(() => System.Threading.Thread.Sleep(1000));
But it doesn't work like I want. This is my first WP7 and Silverlight project, so not sure what I'm doing wrong.
Any advise would be greatly appreaciated!
use the DispatcherTimer class:
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0,0,0,1);
timer.Tick += SomeTickMethod;
timer.Start();
private void SomeTickMethod(obejct sender, EventArgs e) {
FlipBackCards();
//remember to stop it :)
((DispatcherTimer)sender).Stop();
}
then in your Tick method you flip over the card. You could make the timer a class member in which case (based on some of your own logic) you could Stop() the timer from firing at any time.
Hope that helps
(note I quickly just typed up this code, may not be 100%, should be close)
On a WP7 device I have a canvas. When the user touches anywhere on the canvas an image is displayed at that position.
I want to add a feature where if a user touches and holds the screen with one finger and then touches the screen in another place with a different finger an image is also displayed. So basically I want to be able capture and respond to the second touch in the simplest possible way. Any ideas?
Have you taken a look at the GestureService? The Pinch* events let you handle two simultanious touches.
See example.
what you need is just the GestureListener which lies in the Microsoft.Phone.Controls namespace, which can handle a couple gestures like
Flick
Pinch
Drag
Swipe
etc.
You can use it like so
var gestureListener = GestureService.GetGestureListener(myCanvas);
//registering the Events
gestureListener.PinchStarted += new EventHandler<PinchStartedGestureEventArgs>(PinchStartedHandler);
gestureListener.PinchDelta += new EventHandler<PinchGestureEventArgs>(PinchDeltaHandler);
gestureListener.PinchCompleted += new EventHandler<PinchGestureEventArgs>(PinchCompletedHandler);
In the approriate Hanler-Methods you do your rotate- and scale- transformations.
Since you are clearly in Silverlight, this post shows you how to implement multitouch for yourself - http://mine.tuxfamily.org/?p=111
Register for touches
Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
Then handle those touches:
void Touch_FrameReported(object sender, TouchFrameEventArgs e)
{
// if there are more than one finger on screen
if (e.GetTouchPoints(myCanvas).Count == 2)
{
TouchPointCollection tpc = e.GetTouchPoints(myCanvas);
// use tpc[0].Position
// use tpc[1].Position
}
}
Alternatively, if you want to use ready-build Gestures, then consider using the latest Silverlight Toolkit - see this blog post for information - http://3water.wordpress.com/2011/03/09/wp7-gesture-recognition-2/
I am developing an kind of outlook calendar application where I need to make the appointment resizable from mouse.
My first try with a thumb did not work properly so I tried another way.
What I did is that:
1) on the botton of the appointmennt panel I added a rectangle to figure out the resize zone (the thumb). The appointment panel is put on a grid panel.
2) I intercept down event on the rectangle and send event to this code:
private Point startPoint;
private void OnResizeElementMouseDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
this.MouseMove += new MouseEventHandler(ResizeEndElement_MouseMove);
this.MouseLeftButtonUp += new MouseButtonEventHandler(OnResizeElementMouseUp);
// some code to perform new height computation
Mouse.Capture(this);
}
where this is the appointment panel that own the thumb.
Decreasing height works well.
But increasing is more difficult. If I move the mouse very very slowly it's OK, if I speed it up a little bit it tends to leave out the appointment panel and then all MouseMove event are lost.
I thought Mouse.Capture() was propose to solve this kind of problem, but in fact not.
Does anybody know what is wrong in my code?
You should be using an actual Thumb control. Check out MSDN for help:
How to: Use a Thumb to Enable Dragging
you should use a thumb, but to play with mouse capture, override the protected override void OnLostMouseCapture(MouseEventArgs e) method, then you will know if you have lost the capture.