wpf: capturing mouse does not work - wpf

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.

Related

Highlighting text in a Winforms textbox

For a Winforms textbox, when you click and drag over the text, it highlights it. Is there a way to determine which direction the user dragged over?
There is no way to get this info using the Windows TextBox selection API. For example, the EM_GETSEL message defines the starting and ending character positions of the selection, but in a predefined (sorted) order.
However you could get this info by handling the control's MouseMove event. For example:
textBox1.MouseMove += new MouseEventHandler(textBox1_MouseMove);
void textBox1_MouseMove(object sender, MouseEventArgs e)
{
Control tbCtrl = sender as Control;
// the mouse coordinate values here are relative to the coordinates of the control that raised the event
int mouseX = e.X;
...
}
By applying some logic to mouseX you could potentially discover the average direction the cursor is moving. It would work best if the user is making a linear motion. You could also handle the textbox's drag-and-drop event for similar mouse information if you only wanted the event raised while the user was dragging the mouse.

disable map movement from flicks

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.

How to implement my own scrolling in windows form

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.

WPF - Expand Window to the Left

I have a WPF window with expandable panel (via Expander). The panel is on the left side of the window, and when expanded the window grows to fit the content.
By default, windows are anchored to the top-left, so my window grows to the right. I'd like the window to grow to the left.
I tried to do the following in the Window.SizeChanged event:
private void onWindowSizeChanged(object sender, SizeChangedEventArgs e)
{
Left -= (e.NewSize.Width - e.PreviousSize.Width)
}
and it works, but the growth is jerky, and I'd like to find a smoother solution.
I managed to overcome this using a simple solution: Hide & Show.
Here's the code:
protected override void OnRenderSizeChanged(SizeChangeInfo sizeInfo)
{
if (!sizeInfo.WidthChanged)
{
base.OnRenderSizeChanged(sizeInfo);
return;
}
Hide();
base.OnRenderSizeChanged(sizeInfo);
Left -= (sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width);
Show();
}
I replaced the event handler for Window.SizeChanged with this override of FrameworkElement.OnRenderSizeChanged.
I haven't tried to make a Window grow to the left like what you're requesting, but if all else fails, I would consider templating a button to look like the expander button. Then instead of trying to make your Window grow to the left, make a new Window grow to the left of your primary Window using Transforms.
UPDATE
Well, the poor rendering performance could be video card related, layout (overly complex) related, or both. I've got an idea that might do the trick for you. Jeff Prosise blogged about a magnifying glass in Silverlight that uses a WriteableBitmap to achieve the desired effect. I thought, "why not use a WriteableBitmap to create a screenshot of your layout to the right of the Expander, and cover up the other elements with it?". I think that if you do this and hide the underlying elements (so they don't get adjusted), rendering performance will be much improved.
I got Jeff's code to work in WPF with little modification.
http://www.wintellect.com/CS/blogs/jprosise/archive/2009/10/29/more-fun-with-silverlight-3-s-writeablebitmap.aspx
Solution 1
Try to use Window property: SizeToContent="width" this will scale your window to the size of your content and you can scale your content using animation and easing, this will make scaling of the window nice and smooth.
Solution 2
You could create a window which is bigger than it's content and make your background transparent. You still have to add background to some element.
Here is an example of how it may look like:
You may put your expander in a grid (where the column size can change) and then set the ExpandDirection property of your expander to left ?

How can I change the way InkCanvas draws?

I've searched for examples for this, but the ones I've ran across seem to focus on simpler stuff like setting the InkCanvas DefaultDrawingAttributes such as Width, Height, Color etc. Doesn't seem like there's a lot of material for this.
For example, if I hold down the mouse button I can see it drawing lines. What if I want to draw ellipses instead of lines, or draw ellipses around sampled points between the start and end of the line?
I know I can get new points with the StrokeCollected event, but beyond that I have no idea where to go. This guy seemed like he got msdn's code working, but I couldn't do it. I only know how to build the interface using XAML, and there doesn't seem to be a sample either.
edit
Created a StrokeCollection class variable called thisIsNotNice, initialized in the constructor and did this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = thisIsNotNice;
foreach (StylusPoint p in e.Stroke.StylusPoints)
{
StylusPointCollection spc = new StylusPointCollection();
spc.Add(p);
Stroke s = new Stroke(spc);
s.DrawingAttributes.Height = 3;
s.DrawingAttributes.Width = 3;
thisIsNotNice.Add(s);
}
e.Handled = true;
}
But it doesn't work as it should. The ellipses are drawn, but the lines drawn by the mouse are still there. Also, for some reason, the first time it works as it should, drawing just the ellipses, but afterward it draws both the ellipses and the lines. But, if I do this instead:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
myInkCanvas.Strokes = new System.Windows.Ink.StrokeCollection();
e.Handled = true;
}
The lines aren't kept on the screen. So, I don't understand why they aren't being erased in the above code.
If I do this:
private void InkCanvas_StrokeCollected(object sender, InkCanvasStrokeCollectedEventArgs e)
{
foreach (Stroke s in myInkCanvas.Strokes)
System.Diagnostics.Trace.WriteLine(s);
e.Handled = true;
}
I can also see that the canvas contains the line strokes.
While erasing the strokes after they have been added to the collection is far from ideal, it at least does what I want. I could set up the line color to be the same of the background, but then I wouldn't be able to retrieve just the ellipses. I could copy them to a separate collection too, but that's just awful.
It sounds like you want to customize the way strokes appear on your inkCanvas. There are two separate things to consider here:
1) The way they look as the ink flows off the pen, before it is lifted (the DynamicRenderer, who runs on another thread to ensure that ink is always fast, is responsible for this. It sounds like you're happy with your solution to this already.
2) The way the eventual stroke sitting on the canvas looks. To customize this you might try subclassing Stroke, overriding:
protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes);
Each time you get a strokeCollected (and here's the same horrible thing you were worried about but there you go), you remove the incoming stroke from the canvas and replace it with your custom implementation, stealing the stroke data from the incoming one.
Your implementation of DrawCore would look something like (pseudocode):
foreach(sp in this.StylusPoints)
drawingContext.DrawEllipse(RADIUS, sp.X, sp.Y)
And so as not to get the lines that normally happen you would not call base.DrawCore(context,attributes) at any point.

Resources