Client/Screen Coordinate Conversion - c#

I've fallen down a deep rabbit hole. There are actually two issues that I've dealt with. I don't think anyone will have a solution for the first issue (mentioned in the next paragraph), so I guess the real question is how the heck to use PointToScreen/PointToClient reliably.
I want to use balloons to show validation failures for a user control I've written. The way to do this is to use a tooltip with IsBalloon set. In my validation error handler, I call Show() on the tooltip with my user-control as the parameter. This works if I click on another control, but not if I use tab to leave the control. I've even tried hacks like queuing a task on another thread to sleep a bit, then try to show the balloon. Nothing works.
So, I decided to try to go around this and use the main window ("this") as the parameter and specify coordinates, i.e.:
_balloon.Show(text, this, pos);
I can't seem to calculate the position (pos), though.
I'd like for the position to be the bottom right corner of the control, so here that is:
var clientBottomRight = new Point(_ctrlCallbackPhone.Width, _ctrlCallbackPhone.Height);
Then, I try to get the actual screen coordinates of that spot like so:
var screenPosition = _ctrlCallbackPhone.PointToScreen(clientBottomRight);
Finally, I try to get the coordinates on the main form ("this") like so:
var mainPosition = this.PointToClient(screenPosition);
Then I show the balloon. You have to do it twice because of a Windows bug.
_balloon.Show(string.Empty, this, mainPosition);
_balloon.Show(text, this, mainPosition);
The result is well above and slightly to the left of where it should be. Although the margin of error looks like about the size of the title bar, I don't see how that's it. I'm getting the screen coordinates of a spot, and asking for the client coordinates of that same spot.
Now, of course, if someone has a real solution to making the balloon work when I tab away from the control, I'd stop caring about the screen coordinates thing. I imagine this is some sort of Windows weirdness, though, that I won't be able to solve.

I don't fully understand why this works, but this results in the correct coordinates:
var mainPosition = this.PointToClient(screenPosition);
mainPosition = new Point(
mainPosition.X + SystemInformation.VerticalResizeBorderThickness,
mainPosition.Y + SystemInformation.CaptionHeight + SystemInformation.HorizontalResizeBorderThickness);

Related

Unexpected output from checking if mouse within control

I am implementing a custom drag and drop interface with winForm Buttons and after viewing several solutions on how to obtain mouse position and check it against control bound have not been able to get it to work.
I have tried:
button.ClientRectangle.Contains(PointToClient(Cursor.Position))
and
button.ClientRectangle.Contains(PointToClient(Control.MousePosition))
Both of these have failed to work. Checking the mouse bounds seem like a simple operation, but I really am stumped.
My speculation of the unexpected values are:
Process of obtaining cursor position may be in wrong corner of cursor image
Method/Function does not work on Buttons for some reason
You are using the wrong object reference, calculating the mouse position relative to the form instead of the button. And you are writing it in a way that make this very hard to debug. Fix:
var pos = button.PointToClient(Cursor.Position);
System.Diagnostics.Debug.WriteLine(pos); // Now it is easy
if (button.ClientRectangle.Contains(pos)) {
// etc...
}

How to give a different color to my form text programatically

i would like to give the my username in red color in the centre of the my form. Please see my image. I already have my application name on the left hand side and my username in the centre.. I just need to make my username in red color
How can i give this??
Codes:-
var someNewStr1 = new string(' ', 100);
this.Text = "Publisher 1.0.0.0" + someNewStr1 + "USERNAME1";
//i need the user name to be shown in red colour how can i do this
This is an extremely fiddly problem and cant be done out of the of the box
You'll either have to try and draw on the non-client area of the form and capture all the redraw messages, which i'm not sure will even work 100% (however it might)
or
Resort to even more extreme tactics and use a borderless form and redraw the title bar from and border from scratch
Additionally the second approach is prone to all sorts of issues with system settings and implementing the standard title bar features, i.e. you'll have to implement all the default windows styles and default behaviors
At this point you really do have to ask your self whether your coding time is going to be better spent in other areas, or with a slightly different solution
I.e. Why not put the client name in a status bar, or right aligned in a tool bar
However just for academic purposes, there is a codeplex article that does show you all the hacks needed to achieve what you desire (there are too many and too much code for the purposes of this answer)
Please note : while the below link is slightly off topic it does touch the areas you need to do both scenarios
Extending Form with Non-Client Area Painting
I hope this helps or at least convinces you, its probably not worth your while

MonoGame - Working With Specific Gestures for Different Purposes

Part of my particular dilemma is that I would like to be able to get the initial position of a drag gesture. Depending on the initial position of that drag gesture, the application would either 1) pan the view or 2) display a menu, but not perform these gestures at the same time (which is where part of struggle lies).
For example, if the user initiated a drag from the very left side of their screen and dragged inwards, a menu would pop in instead of the view panning.
I would also like to be able to execute a double tap gesture without also activating a tap gesture, if that's at all possible. I've tried working with boolean flags - for example,
// ...
if (gesture.GestureType == GestureType.DoubleTap)
{
isDoubleTap == true;
}
// ...
public static Vector2 Tap
{
get
{
if (!isDoubleTap)
return gesture.Position;
// ...
But that doesn't work.
I've tried using TouchCollection - if anyone would like me to elaborate on my issues with that I can, but for now I'll say what I tried hasn't worked. It's entirely possible I may have just goofed as I am a novice when it comes to working with touch input.
I've been working on this for a few days and have done as much searching as I can, and nothing I've found has alleviated my issue - if I happened to have missed something, I apologize.
Thank you for your time!
Concerning start position of a drag:
There is a gesture for a drag ending, so if you receive a drag and its the first one since the last drag ended, thats the initial position.
Concerning tap/doubletap:
MonoGame works the same way as XNA as documented here:
The user tapped the screen twice in quick succession. This
always is preceded by a Tap gesture.
This sounds like a input-bindings design problem more than a technical question imo. Consider also what you can move to instead occur on press or release rather than only making use of gestures.

WP7 Pivot control and a WebBrowser control

I have a Pivot which contains a WebBrowser control that practically takes up the whole page (appart from the Pivot header of course).
I would like to figure out how to make the WebBrowser control allow for the user to swipe left/right to activate the Pivot control. Currently it just pans the WebBrowser control left/right
Can this be done??
Thank
While I cannot tell you exactly how to pass the swipes to the pivot, I can tell you how to do a part of the job: how to catch/analyze/disable custom gestures over the WebBrowser.
If I remember correctly, in the 7.0:
the WebBrowser component consisted almost only of an internal TileHost wrapped in some grids/borders
the TileHost did all the work related to processing touch events
the TileHost did it completely internally (in the native layer), without the .Net seeing any manipulation-events (I think), or at least it ignored all the attempts to handle/override the manipulation-event on the upper layer. The WebBrowserInterop class was mostly empty in these matters.
Now, in the 7.5 that I have (maybe on 7.1 too, I dont know), it seems that the MS is working really hard on some WebBrowser manipulation problems --- I think they are working towards having the scrolling/swiping fully processed by the .Net layer. They have written a special class PanZoomContainer and injected them into the VisualTree of WebBrowser's internal template. The WebBrowserInterop was greatly enriched with many tunnels for event notifications. The WebBrowserInterop hooks into PanZoomContainer's ManipulationEvents, then passes them to the native layer. Also, it listens to events/commands from the native layer, called for example "ZoomAndScroll" or "ShowSIP" - and mostly passes them back to the PanZoomContainer. The idea is crystal clear right? They have rewired the event handling from completely-internal to a bit of spaghetti, but have achieved passing them through the PanZoomC.
Now, whats in that for me/us?
It is the PanZoomContainer, whose Mani-Events are inspected. The TileHost does not capture them now. In this version of the WebBrowser control, it's VisualTree consists of some borders, grids, a PanZoomContainer and a TileHost (the renderer). It is similar to that:
WebBrowser
PanZoom
ContentPresenter
Border/Name="border" <- you want this one
TileHost
I've skipped a few Borders and Grids, they are mostly irrelevant to the problem. Now, if the PanZoomContainer's Mani-Events are listened to, let's block them!
Using VisualTreeHelper, just dig deeper and deeper until you find a FrameworkElement.Name=="border". This is the border that wraps the TileHost that is the "renderer" that takes 99% space of the control. Be warned that there's a ContentPresenter, so you may have to wait until the controltemplate gets instantiated (ie. Loaded/LayoutUpdated).
Once you have your hands on that Border, attach all Mani-Event handlers to it: started, delta and completed. PanZoom is a normal xaml/silverlight/.net/etc control, so it actually obeys e.Handled = true :) Now, if you want to disable ie. vertical scrolling, filter the Delta and Completed events for Translation.Y<>0. If you want to disable tapping but leave srolling/panning - filter X==0&Y==0.
And that was the easy part.
The hard part is to experiment with filtering on different Start/Delta/Stop and adjusting the behaviour to your likes.
Although it might look very nice and tempting, this will NOT get you any real/nice results easily. For example, I wrote "if you want to disable vertical scrolling, then set a filter 'if y==0 then e.handled=true' ". Great? easy? Not!
Assume we want to "disable bouncy horizontal panning" while leaving "vertical scrolling". or vice versa, whatever, it is only an example:
Here's a small rectangular device with a sensitive touchscreen. Please make such a vertical swipe/pan/drag on the screen, that the resulting X-compound will be ZERO. If you set such filter, it will be almost impossible to it properly. Your users will want to kill you for forcing them to retry-that-vertical-scrolling for five or more times, until they make a perfect vertical swipe.
Of course you can make it not ==0, but leave some small margin. ok. But if you make the margin too big, the control will catch the intermediate offaxis movement and make a tiny horizontal pan also.. After a few unlucky vertical swipes, the total horizontal pan may accumulate from those small leftovers will accumulate and the diplacement maybe will be noticeable.
But there's some another vile side effects:
Saying shortly, you have commited e.Handled=true. The event is GONE. Dead. Deased. if you just wanted the WebBrowser to SKIP for example horizontal swipes, so that the outer (Pivot) control notices them and processes..... whoops. The event is GONE. Earlier, the TileHost/PanZoomC have extinguished the events, now you have it done yourself. Sounds like a bad joke, eh?
Fortunatelly:
since you have attached your handlers to the bottommost "border", they may not only block the events, but may also actually listen&publish them elsewhere. That is, if those handlers detect an interesting movement, they may e.Handled=true on it, but at the same time they can notify your custom objects about that discovery, and ie. start your storyboards.
mani-events are at hand, but there is also a second layer that listens to the manipulations: the GestureListener/GestureService from the Silverlight Toolkit. It reports events after they are handled by mani-events, but it reports them with no regard to any e.Handled=true that were set on them. It is completely separate gesture-listening mechanism, and you can also use it to detect manipulations that were 'cancelled'
.. and so the fun goes like that and maybe even a little further.
This is similar to putting a Map inside a Pivot - which is discussed here - http://mine.tuxfamily.org/?p=111 - already mentioned in quite a few questions - https://stackoverflow.com/search?q=mine.tuxfamily.org
In general, the advice seems to be usability based:
try to avoid putting controls which use Touch inside the PivotItem's
As an aside, if you are just using the web browser control for a very small amount of static html (so you don't need any scrolling at all) then you could just remove HitTesting from the web browser.
I do not know WP7 Pivot, but are there any Preview* events on the Pivot control that allow you to handle the touches and mark them as processed?
Call the below method and pass your parameter as PivotControl x:name and WebBrowserControl x:name to this method.
Here the WebBrowserControl is placed in second pivot item i.e. Pivot Index is 1 and I am trying to swipe left or right and reach to pivot index 2 or 1 respectively.
public static void SwipteLeftRight(Microsoft.Phone.Controls.Pivot pivotControl, Microsoft.Phone.Controls.WebBrowser webBrowserControl)
{
var gesListener = GestureService.GetGestureListener(webBrowserControl);
gesListener.Flick += ((sen, args) =>
{
if (args.Direction == System.Windows.Controls.Orientation.Horizontal)
{
if (args.HorizontalVelocity < 0)
{
if (((Microsoft.Phone.Controls.PivotItem)(pivotControl.SelectedItem)).Header.ToString().Trim() == "Pivot Item name")
{
pivotControl.SelectedIndex = 2; //Next Pivot item
}
}
else if (args.HorizontalVelocity > 0)
{
if ((Microsoft.Phone.Controls.PivotItem)(pivotControl.SelectedItem)).Header.ToString().Trim() == "Pivot Item name")
{
pivotControl.SelectedIndex = 0; // Previous Pivot Item
}
}
}
});
}
It worked for me. Cheers

Control.PointToScreen gives different results - why?

I have some Label controls sitting on Panel controls on a Form. I want to get the labels' positions relative to the form's origin so that at run time I can hide the panel and the labels and draw some other text in their place directly onto the form.
Basically, I'm doing the following calculation: Get the absolute screen position of a label with Control.PointToScreen() and convert it back to a relative position with Control.PointToClient(), so either:
Dim newloc As Point = Me.PointToClient(ctl.PointToScreen(Point.Empty))
or
Dim newloc As Point = Me.PointToClient(ctl.Parent.PointToScreen(ctl.Location))
I have found that the two methods sometimes give me different results - putting my new point out of the visible area with negative values! - but haven't been able to determine why. I would have thought they should be identical (and they are, most of the time).
Reading the docs didn't help the first time around, but perhaps I skipped over something... Anyway, I'd be thankful for any help before I start tearing my hair out.
Or, on the other hand, is there a better way to do this?
Edit: Sample results
So, here's a real example.
Label1 at {X=4,Y=6} on Panel1; Label2 at {X=163,Y=6} on the same parent, Panel1. Obviously I'm expecting different X-positions, but Y should be identical for both.
When I run the project both ctl.PointToScreen(Point.Empty) and ctl.Parent.PointToScreen(ctl.Location) give me the same screen location for Label1 at {X=959,Y=119} (the absolute values here can vary, of course, depending on the position of the form itself) and therefore the correct location relative to the form when Me.PointToClient is applied (at {X=5,Y=32}).
The very next lines do the same calculations for Label2 (remember, same Parent, same Y-value within the parent (6)), but the results are totally off: ctl.Parent.PointToScreen() gives me {X=1114,Y=63}. X is almost correct (959-4+163=1118), but Y is nowhere near the 119 I got for Label1. And then ctl.PointToScreen() gives me {X=166,Y=29} - translated back to Form-Coordinates (Me.PointToClient) {X=-784,Y=-2}.
These numbers are calculated and printed to the debug window directly after each other, without moving anything around... Madness.
OK, this is getting rather messy, but I still hope someone has a simple explanation. Thanks!
OK, I found the solution.
I happened to be calling Control.PointToScreen before the control was created: Control.IsHandleCreated = False.
If I ensure that the control is created first (Control.CreateControl) both methods work equally well.
The reason I had differing results on subsequent calls is that the first call to Control.PointToScreen also causes the control to be created (and therefore its parent and any other controls sited on the parent), meaning the second succeeds.
Well, I'm sure glad to be done with this :-)
I think it's the sleep. :) Instead of:
ctl.PointToScreen(Point.Empty)
ctl.Parent.PointToScreen(ctl.Location)
try:
ctl.PointToScreen(Point.Empty)
ctl.PointToScreen(ctl.Location) // Note no .Parent!
instead and you'll see the difference in the x/y coordinates.
Also, try using Control.TopLevelControl or Control.FindForm() to get the outermost Form when doing your PointToScreen math.

Categories