Use ManipulationDelta event of webbrowser control to scroll a listbox - c#

I have multiple webbrowser controls inside a listBox (Hierarchy: DataBoundListBox->Multiple ExpandableItems->Each expandable item shows a webbrowser control on expansion.).
The problem is, the webbrowser control captures the drag gesture and it is impossible to scroll the listbox once the webbrowser control is expanded. If the content of the webbrowser is too long, the user is stuck and cannot scroll anymore as the webbrowser fill the entire screen. Now, the webbrowser only needs to respond to simple tap events (buttons).
The webbrowser control captures all touch gestures and events internally, but it seems that there is a border element where touch events can be intercepted before the touch gestures reach the tilehost and is gone forever:
Border border = WebViewer.Descendants<Border>().Last() as Border;
To capture the touch gesture, I added a ManipulationDelta event to the border as it does not allow me to add a Gesture Listener event like DragStarted, DragDelta, DragCompleted.
Now I need to get this ManipulationDelta event data to the scrollviewer inside the listbox where the browser is located.
Is this a valid conceptual approach and is somebody able to help me on how to get this running?
P.S.: I tried it the other way around (sending tap gesture events from a canvas overlay to a javascript inside the webbrowser control page and simulate a tap on the page) but this does not seem to work very well as the touch coordinates and the javascript coordinates have a unknown offset.
Thanks for help!

It turns out that the last method with javascript was actually working quite well, the problem was the double x and y value which was passed to the javascript. After converting it to integer, it detected the underlying html element pixel precise.
If you want to know how you can transfer tap events to a website in a webbrowser element, you need to do the following:
Add "LinqToVisualTree" to your project and your document (f.e.: using LinqToVisualTree;)!
Add this javascript code to your website (besides jquery):
function simulateClick(x, y) {
jQuery(document.elementFromPoint(x, y)).click();}
Add silverlight toolkit (search for GestureService for info) to your solution and add this event to the overlay canvas (the canvas element which is placed over the webbrowser element) in the XAML file (example, note the canvas I put also in there. You would have to replace it with your own canvas or grid or whatever:
<Canvas>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="GestureListener_Tap"/>
</toolkit:GestureService.GestureListener>
</Canvas>
Add this to your xaml file codebehind c# (note that I did a little recalculation of the original coordinates from the tap, you may have to change the number 1.3499. This is the static offset factor of the buttons on the emulator.):
private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
Canvas cv = sender as Canvas;
//!!! You need to change the following search for the webbrowser to your own xaml structure.
// It uses "LinqToVisualTree" to find the element! Add it to your document!
WebBrowser wb = cv.Ancestors<Grid>().First().ElementsBeforeSelf<WebBrowser>().First() as WebBrowser;
var gesture = e.GetPosition(cv);
var gx = gesture.X;
var gy = gesture.Y;
tapBrowser(wb, gx, gy);
}
private static void tapBrowser(WebBrowser wb, double x, double y)
{
GeneralTransform gt = wb.TransformToVisual(Application.Current.RootVisual as UIElement);
Point offset = gt.Transform(new Point(0, 0));
double rx = (x / 1.3499);
double ry = (y / 1.3499);
int ix = (int)rx;
int iy = (int)ry;
wb.InvokeScript("simulateClick", ix.ToString(), iy.ToString());
}
Make sure your canvas has the same size as the webbrowser element!
Thats it. You now have a tap enabled browser but you can keep the rest of the gestures inside the native app and the browser won't catch them! Theoretically, you can redirect more gestures, but you need to have a javascript/jquery function which can "resimulate" the gesture you want to pass on.

Related

Panning through mouse in windows 8 app?

I am developing a windows 8 app and have to enable panning on the image through mouse.It is working on the Tablet but somehow I need to enable it from the mouse also.
And I am writing pointer wheel changed event for zooming the image but it is calling just once.
This is my code for Zooming
private void MyScrollView_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
MyScrollView.ChangeView(MyScrollView.HorizontalOffset + 40, MyScrollView.VerticalOffset + 20, MyScrollView.ZoomFactor + 0.1f);
}
Any help would be grateful to me.
I encountered the same problem while trying to do the same thing. Your code will work as long as the Scrollviewer is not handling scroll event internally.
To explain more closely:
When all of the content of the Scrollviewer is visible (ie no need to scroll), the Scrollviewer will not handle any scroll event internally and the event will fire and you handle it in your code.
If, however, there is content that is larger than the visible area of the Scrollviewer, the scrollbars will show up. At this point the Scrollviewer will handle scroll event (so that when you scroll, you move the scrollbar). This is the expected behaviour, as it is in web browsers and other applications with content that's larger than the viewport or visible area. The event will no longer fire and your handler will not run because the internal logic stops the event from ever reaching your code.
You can capture the event by setting e.Handled to true like so:
private void MyScrollView_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
e.Handled = true; // Tell the event system that the event has been handled and it should not execute any more handlers.
MyScrollView.ChangeView(MyScrollView.HorizontalOffset + 40, MyScrollView.VerticalOffset + 20, MyScrollView.ZoomFactor + 0.1f);
}
But I found it to be a bad experience. The zooming is not cenetered on the pointer and moves the content in a way that's confusing and jarring. I found no good way to override this.
As a last point, I want to mention that there's built in zoom functionality in the Scrollviewer by using Ctrl+Scroll. Works nicely and it's the same shortcut used in browsers.

Win 8.1 ScrollViewer issues with a GridView

I have a VariableSizeGridView (aka GridView with a VariableSizedWrapGrid) and since the framework doesn't support ISupportIncrementalLoading on VariableSizedWrapGrid I implemented my own by "listening" to the GridView parent ScrollViewer (this specific case a Hub control)
Everything works fine, when I reach on the end of the scrollviewer my code calls a routine to get more data.
The problem occurs when the new data gets on the screen, the scrollviewer automatically scrolls to the end and the load process starts again, ending up in an infinite loop if I don't move the scrollbar elsewhere.
Is there any way to prevent that the scrollviewer automatically goes to the end after I added some items?
Thanks
You can create a Custom-control that overrides from VariableSizeGridView and listen, as you do, to the scrollviewer. Since you have access to the latest element of the GridView, after starting loading more elements, you can set the scrollviewer to the that latest position position. Get a visual reference to that element and then call this code:
FrameworkElement focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
GeneralTransform focusedVisualTransform = parent.TransformToVisual(_scrollViewer);
ApplyHorizontalScrolling(focusedElement, focusedVisualTransform);
private void ApplyHorizontalScrolling(FrameworkElement focusedElement, GeneralTransform focusedVisualTransform)
{
Rect rectangle = focusedVisualTransform.TransformBounds(new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
double horizontalOffset = _scrollViewer.HorizontalOffset + (rectangle.Left);
_scrollViewer.ChangeView(horizontalOffset, 0, _scrollViewer.ZoomFactor);
}
Using and tunning this code, will help you preventing the scrollbar to go to the latest position.

How to create hot spots on an image with mouse over and clickable area?

I have a scenario where i want to create hot spot on image.I want to let the hot spots on image be created by the user.Suppose user want to create rectangle on image then he just need to left click with mouse and create rectangle and this rectangle should be displayed with clickable area(Hot spot.).I need it because I need to create a page where a user can select specific area and should be the clickable in future.Can any one please suggest me the way.
here is the example image.I am now able to draw lines on image but i want to create rectangle which should be clickable with mouse over.
the background is an image and blue thin line is drawn by mouse.
First you need to wrap your image into a relative positioned DIV. Then add mousedown event listener for the image. In this handler store the click- coordinates and add mouseup listener for the image. In this mouseUp() store the mouseup-coordinates, remove all previous handlers and create a new DIV to the document. Then add click and mouseover event listeners for the new DIV, style the position absolute, actual position and size you can calculate from stored coordinates. Finally append newly created DIV to the image wrapper.
Instead of using a DIV as a hotspot , you can include an empty MAP element in your document, and then create new AREA(s) to this MAP.
EDIT
I'm really not sure what you need, but...
Creating a DIV in mouseUpHandler():
var clickableDiv = imgWrapper.appendChild(document.createElement('DIV'));
clickableDiv.style.position = 'absolute';
clickableDiv.style.top = calculatedY;
clickableDiv.style.left = calculatedX;
clickableDiv.style.width = calculatedWidth;
clickableDiv.style.height = calculatedHeight;
clickableDiv.style.backGround = '#fff';
clickableDiv.style.opacity = '0.5';
clickableDiv.addEventListener('click', doSomethingOnClick, false);
You're free to style the clickableDiv as you wish, but backGround and opacity (can be also 0) are needed to make the DIV clickable. doSomethingOnClick is a function name, which handles clicks on the clickableDiv.

Simulate Mouse Drag Programmatically

I have a Windows Forms application with a control. The control consists of a chart panel with a canvas on which I paint. What I would like to do is to programmatically mouse drag the panel so that I have a specific distance between the right edge of the canvas and the last item painted on the canvas. I have tried two approaches. The both work in the sense that the panel is dragged as desired BUT I cannot seem to be able to get the precision of movement I desire. I coded a mouse simulator and have tried two approaches.
Approach 1:
if(this.ChartControl.ChartPanel.CanFocus)
{
// ... Focus the chart panel to be adjusted.
this.ChartControl.ChartPanel.Focus();
// ... Move cursor to lastBarScreenCoordinates on the chart panel to be adjusted.
Cursor.Position = new Point(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.SetMouseDragThresholds();
// ... Move chart panel to required position.
MouseSimulator.LeftMouseButtonDown(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.MouseMove(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.LeftMouseButtonUp(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.ResetMouseDragThresholds(_cx_default, _cy_default);
// ... Redraw the chart panel.
this.ChartControl.ChartPanel.Refresh();
// ... Reset cursor to its starting position.
Cursor.Position = new Point(startingCursorX, startingCursorY);
}
Approach 2:
if(this.ChartControl.ChartPanel.CanFocus)
{
// ... Focus the chart panel to be adjusted.
this.ChartControl.ChartPanel.Focus();
// ... Move cursor to lastBarScreenCoordinates on the chart panel to be adjusted.
Cursor.Position = new Point(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
MouseSimulator.SetMouseDragThresholds();
// ... Move chart panel to required position.
MouseSimulator.LeftMouseButtonDown(lastBarScreenCoordinates.X, lastBarScreenCoordinates.Y);
Cursor.Position = new Point(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
WindowsCommunication.SendMessage(this.ChartControl.Handle, 0x200, IntPtr.Zero,IntPtr.Zero);
MouseSimulator.LeftMouseButtonUp(lastBarScreenCoordinates.X-positionShift,
lastBarScreenCoordinates.Y);
MouseSimulator.ResetMouseDragThresholds(_cx_default, _cy_default);
// ... Redraw the chart panel.
this.ChartControl.ChartPanel.Refresh();
// ... Reset cursor to its starting position.
Cursor.Position = new Point(startingCursorX, startingCursorY);
}
I am using SendInput for simulating mouse clicks. Here is sample left mouse button down code ...
public static void LeftMouseButtonDown(int x, int y)
{
INPUT mouseInput = new INPUT();
mouseInput.type = SendInputEventType.InputMouse;
mouseInput.mkhi.mi.dx = CalculateAbsoluteCoordinateX(x);
mouseInput.mkhi.mi.dy = CalculateAbsoluteCoordinateY(y);
mouseInput.mkhi.mi.mouseData = 0;
mouseInput.mkhi.mi.time = 0;
mouseInput.mkhi.mi.dwFlags = MouseEventFlags.MOUSEEVENTF_LEFTDOWN;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
And I calculate normalized absolute coordinates for the mouse as follows ...
private static int CalculateAbsoluteCoordinateX(int x)
{
return ((x * 65536) + GetSystemMetrics(SystemMetric.SM_CXSCREEN) - 1) /
GetSystemMetrics(SystemMetric.SM_CXSCREEN);
}
So here are the precision issues. If I use Approach 1 (mouse move), the measured distance between the last item painted and the right edge of the canvas is different from what I set in positionShift and the cursor position difference does not equal positionShift. I initially thought it was due to pointer ballistics issues so I tried using Approach 2. Approach 2 does give me precision in pointer positioning but I am still having difficulty in that the panel moves but the distance between the last bar painted and the right edge of the canvas does not equal the positionShift amount as it should. It always seems to be off. I have been working on this for a long time now and am at my wits end. I am not sure what is going on here. How to improve the precision in my canvas drag by simulated mouse drag?
Well what you can do is this, First of all I believe the SendInput API allows for an AbsoluteValue flag so there is no need to calculate those values which may be the issue but most likely not.
Although I am curious as to why you are using a Mouse Drag opperation for this. It seems like all you want to do is reposition the canvas on every draw by some specified amount. if this is the case why not just set it explicitly on the canvas itself. Also it is unclear if you are using pure WinForms or WPF. The unclear bit being Canvas which I am fairly certain is only usable with WPF enabled windows.
That being said
WPF fix,
Depending on the Object containing the canvas just set its margin appropriately for the situation, since I do not know the data you are working with I cant say much about that. But this is a relatively simple idea so let me know if that works, it should give you, at least close too, a pixel perfect alignment.
WinForms,
Just do the above for the "Canvas" object you were talking about, or use absolute coordinates of the object to move it around.
If you could supply a sample of what you were working on looked like roughly maybe we could have a better idea of what you mean.

Can I tell in code-behind if a popup has been moved relative to its placement target?

I'm working on a WPF project where I have a grid of thumbnails (each thumbnail is a SurfaceButton), and clicking on each thumbnail shows its own popup menu. The popup menu in each case has its 'sibling' photo thumbnail as its placement target. The thumbnail and popup sit alongside each other in a Grid.
I've designed the popup to emulate the kind of popout scrollable menu that you'd find in iOS, complete with a little triangle arrow that points up to the thumbnail the popup relates to (with the popup appearing below the thumbnail).
But, much to my pleasant surprise, if you click on a thumbnail that's at the bottom of the screen, WPF moves the popup above the thumbnail so that it's not off-screen.
This is great, but how can I compare the positions of the two elements and move the arrow accordingly (or hide it and show another), so that the popup above the thumbnail is pointing down to it?
Hope that makes some sense!
I've tried VisualTreeHelper.GetOffset, but that only returns the offset from the element's parent, and these seem to always be 0,0 or 1,1. I've also tried UIElement.PointToScreen, but the numbers I get back from the two elements don't seem to vary when the popup moves.
I'm sure there's a simple solution that I'm missing.
You can get your popup's actual placement like below
// ComboBox for example:
Popup popup = yourcombobox.Template.FindName("PART_Popup", yourcombobox) as Popup;
popup.Opened += (s, a) => {
// structure inside will be different by your templates
Dacorator decorator = (s as popup).Child as Decorator;
Border border = decorator.Child as Border;
Double y0 = yourcombobox.PointToScreen(new Point(0,0)).Y;
Double y1 = border.PointToScreen(new Point(0,0)).Y;
if(y0 > y1) {
// when shows at the top
yourcombobox.Background = Brushes.Red;
} else {
// when shows at the botttom
yourcombobox.Background = Brushes.Blue;
}
};
Popup doesn't have it's own size or position, so you have to catch the child's position only when popup is shown.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/popup-overview

Categories