Panning through mouse in windows 8 app? - c#

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.

Related

MouseWheel event not registering minor movements

I thought I would improve my program by adding granular scrolling, hence I am studying up on the mouse wheel delta.
I have a new Logitech mouse with smooth scrolling enabled. Testing the scrolling on Chrome and I barely have to touch the scroll wheel and the page scrolls up one pixel. Nice!
I have a test program in c# with the MouseWheel event connected to a form with a few labels. Nothing fancy:
private void MainForm_MouseWheel(object sender, MouseEventArgs e)
{
int lines = e.Delta / 120;
if (lines > MaxLines)
MaxLines = lines;
else if (lines < MinLines)
MinLines = lines;
deltaResult.Text = e.Delta.ToString();
linesResult.Text = lines.ToString();
minLinesResult.Text = MinLines.ToString();
maxLinesResult.Text = MaxLines.ToString();
}
Trouble is this event only gets called when the delta reaches 120 (+/-). I understand that the mouse should be firing more events with lower value deltas, which it apparently works for Chrome, but for the test code, I have to move the scroll wheel considerably further before the event is fired.
I have come to the conclusion that either granular scrolling is not possible in C#; or that there is another event I should be calling; or it is something that needs to be enabled within the program; or that there is another method somewhere adding up the Deltas until they reach 120 and then firing the MouseWheel event.
My specific question:
How do I get the MouseWheel event to fire with only a minute rotation of the mouse wheel such as Chrome is obviously doing? If this is not possible, what event should I be calling?

Scrolling part of a control (Form or Panel) [duplicate]

I have a Form with the following properties:
Background Image
Scrollable Panel with a transparent background, and Dock = DockStyle.Fill
PictureBox with a large Width and Height which shows scroll bars
Now all controls are set to DoubleBuffered including the form itself. Everything works as expected except when scrolling the Panel for the PictureBox, the form background image scrolls with it repeating itself showing vertical and horizontal tearing although its static image that fits the form's size, and when you stop scrolling it shows properly. This only happens when dragging the scrollbars, if i click on any point in the scrollbars to move it, it shows properly.
As per my understanding Double Buffering should eliminate such cases, but even with double buffering its the same, maybe a little bit better but still its a huge problem when scrolling.
I tried to place all controls inside another panel instead of using form background image and place this panel on the form but it didn't make any difference.
You are doing battle with a Windows system option, named "Show window content while dragging". It is turned on for all modern versions of Windows. Turning it off is not a realistic goal, since it is a system option it affects all windows of all apps. There is no back-door to selectively bypass this option.
With it enabled, the OS optimizes the scrolling of a window. It performs a fast bitblt to move the pixels in the video frame buffer and generates a paint message for only the part of the window that is revealed by the scroll. Like the bottom few rows of pixels when you scroll down. Underlying winapi call is ScrollWindowEx(). Intention is to provide an app with a more responsive UI, a lot less work has to be done to implement the scroll.
You can probably see where this is heading, ScrollWindowEx() also moves the pixels that were painted by the form's BackgroundImage. You can see that. Next thing you see is the side-effect of the optimized paint, it only redraws the part of the window that was revealed. So the moved background image pixels don't get redrawn. Looks like a "smearing" effect.
There is a simple workaround for that, just implement an event handler for the panel's Scroll event and call Invalidate(). So the entire panel gets redrawn again:
private void panel1_Scroll(object sender, ScrollEventArgs e) {
panel1.Invalidate();
}
But now you'll notice the side-effect of the paint no longer being optimized. You still see the pixels getting moved, then overdrawn. How visible that is depends a great deal on how expensive the BackgroundImage is to draw. Usually never cheap because it doesn't have the optimal pixel format (32bppPArgb) and doesn't have the right size so needs to be rescaled to fit the window. The visual effect resembles the "pogo", rapid jittering on one edge of the panel.
Pretty unlikely you'll find that acceptable or want to do the work to optimize the BackgroundImage. Stopping ScrollWindowEx() from doing its job requires a pretty big weapon, you can pinvoke LockWindowUpdate(). Like this:
using System.Runtime.InteropServices;
...
private void panel1_Scroll(object sender, ScrollEventArgs e) {
if (e.Type == ScrollEventType.First) {
LockWindowUpdate(this.Handle);
}
else {
LockWindowUpdate(IntPtr.Zero);
panel1.Update();
if (e.Type != ScrollEventType.Last) LockWindowUpdate(this.Handle);
}
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool LockWindowUpdate(IntPtr hWnd);
Works pretty well, the background image pixels are now rock-steady. Any other pixels, well, not so much. Another visual effect, lets call it a "wrinkle". Getting rid of that artifact can be done by putting the window into compositing mode. Which double-buffers the entire window surface, including the child controls:
protected override CreateParams CreateParams {
get {
const int WS_EX_COMPOSITED = 0x02000000;
var cp = base.CreateParams;
cp.ExStyle |= WS_EX_COMPOSITED;
return cp;
}
}
Only remaining artifact is the side-effect of this not being very cheap code. It probably doesn't look that smooth when you scroll. Which otherwise tells you why windows were designed to be opaque 28 years ago.
It is not easy but it is doable and the following worked fine for me though took me 2 hours to discover:
First you need to make sure the column gets null value without the default 'null' icon, before add it to the grid:
DataGridViewImageColumn imagecol = new DataGridViewImageColumn { ImageLayout = DataGridViewImageCellLayout.Stretch };
imagecol.DefaultCellStyle.NullValue = null;
grid.Columns.Add(imagecol);
Then you need to delete this column value for all rows within ANY event that resizes or moves the rows of that particular column (the example here is for scroll event):
private void DataGridViewScrollEventHandler(object sender, ScrollEventArgs e)
{
if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
{
DataGridView grid = (DataGridView)sender;
foreach (DataGridViewRow row in grid.Rows)
{
row.Cells[1].Value = null;
}
}
}
Finally you need to fill the image value again for all rows you delete it at paint event:
private void DataGridViewPaintEventHandler(object sender, PaintEventArgs e)
{
DataGridView grid = (DataGridView)sender;
foreach (DataGridViewRow row in grid.Rows)
{
row.Cells[1].Value = myImage;
}
}
If you have a lot of rows you need to do this for the visible rows only for performance. There is a property for this so it would be doable.
The best solution is to set the form's background image again on the control scroll event
private void panel1_Scroll(object sender, ScrollEventArgs e) {
/*
Your Code if any exists
*/
//reset the form's background image again in the scroll event
this.BackgroundImage = Properties.Resources.your_background_image;
}

How to get coordinates of a Mouse when it's Clicked

I'm beginner in c# and need some help. After loading Form I want to display on Form coordinates of a Mouse when it's Clicked. Click can be made outside of the Form. For example in Browser. Can someone help me with this.
Maybe the most simple way is setting Capture property of a form to true, then handle click event and convert the position (that is position related to top left point of form) to screen position using PointToScreen method of form.
For example you can put a button on form and do:
private void button1_Click(object sender, EventArgs e)
{
//Key Point to handle mouse events outside the form
this.Capture = true;
}
private void MouseCaptureForm_MouseDown(object sender, MouseEventArgs e)
{
this.Activate();
MessageBox.Show(this.PointToScreen(new Point(e.X, e.Y)).ToString());
//Cursor.Position works too as RexGrammer stated in his answer
//MessageBox.Show(this.PointToScreen(Cursor.Position).ToString());
//if you want form continue getting capture, Set this.Capture = true again here
//this.Capture = true;
//but all clicks are handled by form now
//and even for closing application you should
//right click on task-bar icon and choose close.
}
But more correct (and slightly difficult) way is using global hooks.
If you really need to do it, you can take a look at this links:
Processing Global Mouse and Keyboard Hooks in C#
Low-Level Mouse Hook in C#
Application and Global Mouse and Keyboard Hooks .Net Libary in C#
I think you can't handle the mouse click outside your Form at least easily.
inside the form using MouseEventArgs it can simply be handled.
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
// e.Location.X & e.Location.Y
}
Learn more about this topic at Mouse Events in Windows Forms.
I hope it helps.
Cursor.Position and Control.MousePosition both return the position of the mouse cursor in screen coordinates.
The following articles deal with capturing Global mouse click events:
Processing Global Mouse and Keyboard Hooks in C#
Global Windows Hooks
You need a global mouse hook.
See this question

How to determine the keyboard offset

I had this issue yesterday and it seems a lot of people have had similar issues in the past, so I figured I would pose my question & the solution I ended up coming up with. Microsoft has cleaner solutions to this in the 8.1 SDK, but the vast majority of WP app users are on 8.0 and below, so I imagine this will still be helpful.
When you open the virtual keyboard in a Windows Phone 7/8 Silverlight app, and the text box that caused the keyboard to open is on the lower half of the screen (that would be covered by the keyboard), it scrolls the entire page up. How can you determine how much it has scrolled, in case there was content at the top that you need displayed?
It's a little clunky, but you can get the amount the page was scrolled up by looking at the offset of the root frame.
Since this is animated into position, the question becomes "when". What I found that works is, when a text box's GotFocused event is fired, subscribe to the LayoutUpdated event, and when LayoutUpdated is fired, grab the offset from there. If you weren't already subscribed to that event, you can unsubscribe in the LostFocus event. That way as it moves, you'll get the change.
double lastOffset = 0;
private void TextBox_GotFocus(object sender, RoutedEventArgs e)
{
LayoutUpdated += MyControl_LayoutUpdated;
}
private void MyControl_LayoutUpdated(object sender, EventArgs e)
{
// Grab the offset out of the root frame's RenderTransform object
PhoneApplicationFrame root = App.Current.RootVisual as PhoneApplicationFrame;
TransformGroup transform = root.RenderTransform as TransformGroup;
double offset = transform.Value.OffsetY;
if (offset != lastOffset)
{
// Do your logic here if the offset has changed
lastOffset = offset;
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
// Unsubcribe to updates and reset the offset to 0
LayoutUpdated -= MyControl_LayoutUpdated;
lastOffset = 0;
}
After you have this offset, you can alter your controls as needed. You can either shrink the height of a control by that amount, or if you have something small at the top, like a header, you can apply a TranslateTransform by the inverse of the offset to just move it downward.

WP7 (Windows phone 7) Lock phone orientation in XAML or C#

Is it possible to manualy lock the phone orientation in Windows phone 7 ?
Because I'm using the accelerometer to handle buttons' rotation with a fixed UI.
I've tried that :
In the XAML
SupportedOrientations="Landscape" Orientation="LandscapeLeft"
OrientationChanged="PhoneApplicationPage_OrientationChanged"
And in the code behind :
private void PhoneApplicationPage_OrientationChanged(object sender, OrientationChangedEventArgs e)
{
//Orientation locking
//Do nothing
}
But the UI is still shifting between landscape left and landscape right...
Thanks.
There is no way to prevent the shifting between LandscapeLeft and LandScapeRight. This is by design.
As a work around, you can manually rotate/transform your UIElements in the OnOrientationChanged so that the user doesn't see a difference.
I've used this technique to keep a "background" image from appearing to rotate regardless of orientation but then having a separate control which appears like a popup but which does respond to orientation changes show on top of the image.
Hi I found a solution by overriding OnOrientationChanged method. It works for me. That do not affect system tray and application menu, but page stay in the selected orientation.
protected override void OnOrientationChanged(OrientationChangedEventArgs e)
{
if (e.Orientation == PageOrientation.LandscapeLeft)
base.OnOrientationChanged(e);
}
add this this.SupportedOrientations = SupportedPageOrientation.Portrait; after InitializeComponent(); in MainPage() constructor to lock the orientation in Portrait mode. It works fine for me.

Categories