Animation Methods, Simplification and Repairing - c#

Context
I'm kinda new at animating WPF stuff, but I've played around with a library or two and I "had" an animation that I used with the Window control in WPF, this is an example of that method, keep in mind that this method works:
public void AnimateFadeWindow(object sender, double opacity, double period)
{
//Tab item is a enw tab item (the sender is casted to this type.)
Window win = (Window)sender;
win.Opacity = 0;
//using the doubleanimation class, animation is a new isntancem use the parameter opacity and set the period to a timespan.
DoubleAnimation animation = new DoubleAnimation(opacity, TimeSpan.FromSeconds(period));
//begin the animation on the object.
win.BeginAnimation(Window.OpacityProperty, animation);
}
Problem
Like I said previously, This code works, the problem with this code was, of course, it's only suited to the Window control, it won't work with other controls, for instance, TabItem, Button or any other control I wanted to use it for, so I "Upgraded" my method and this is my CURRENT method:
public void AnimateFade(object sender, double opacity, double period)
{
//using the doubleanimation class, animation is a new isntancem use the parameter opacity and set the period to a timespan.
DoubleAnimation animation = new DoubleAnimation(opacity, TimeSpan.FromSeconds(period));
Object obj = sender.GetType();
if (obj is TabItem)
{
TabItem tab = (TabItem)sender;
tab.BeginAnimation(TabItem.OpacityProperty, animation);
}
else if (obj is Label)
{
Label lab = (Label)sender;
lab.BeginAnimation(Label.OpacityProperty, animation);
}
else if (obj is Window)
{
Window win = (Window)sender;
win.Opacity = 0;
win.BeginAnimation(Window.OpacityProperty, animation);
}
}
This method doesn't work. I don't really know what I'm doing wrong here, so I wondered if someone could possibly help out.
ALSO, is there an easier way to do this using something like the PropertyInfo class or a reflection class?
Thanks Stack.

Your issue is nothing to do with Animation.The problem is you are comparing sender.Type while you should compare sender itself i.e.
use if (sender is TabItem) instead of if (obj is TabItem).
Moreover, There is no need to compare sender with TabItem, Lable, Window and etc one by one, they are all UIElements! and Since UIElement implements IAnimatable, you just need to cast sender to UIElement and you have a general method that applies your animation to any control :
public void AnimateFade(object sender, double opacity, double period)
{
UIElement element = (UIElement)sender;
element.Opacity = 0;
DoubleAnimation animation = new DoubleAnimation(opacity, TimeSpan.FromSeconds(period));
element.BeginAnimation(UIElement.OpacityProperty, animation);
}

Related

How to position a borderless form under buttons

I have created 4 buttons dynamically and placed them horizontally using c# win forms.Now i want show a custom tooltip(actually its a borderless form) under each of the 4 buttons on mouse hover event.But how do i position my tooltip form under the buttons??
I have tried the code below but it does not work the desired way.
tooltip.Location = new System.Drawing.Point(b.Left, b.Top);
Where 'tooltip' is tooltip form object & 'b' is the dynamic button.Please advise with some code snippet.
private void B_MouseHover(object sender, EventArgs e)
{
var b = sender as Button;
//MessageBox.Show(Cursor.Position.ToString());
if(b!= null)
{
if (tooltip == null)
{
tooltip = new frmSecQStatToolTipDlg();
}
tooltip.Location = new System.Drawing.Point(b.Left, b.Bottom);
tooltip.data(b.Tag.ToString());
tooltip.Show();
}
}
The way you named it is a bit misleading. As I understand, what you call a tooltip is just a Form. You need to consider 2 things
(1) Form.StartPosition must be set to FormStartPosition.Manual
(2) Form.Location must be in screen coordinates. Note that the Button.Location you are trying to use is in button's parent client coordinates. Control.PointToScreen has to be used for conversion.
In your case, it should be something like this
tooltip.StartPosition = FormStartPosition.Manual;
var topLeft = b.PointToScreen(new Point(0, 0));
tooltip.Location = new Point(topLeft.X, topLeft.Y + b.Height);
When you show the tooltip you can control its location, check show method overloads: https://msdn.microsoft.com/en-us/library/system.windows.forms.tooltip.show.aspx

WPF- Horizontal and Vertical position of a Run Element in a FlowDocument

Is there anyway to get the horizontal position(pixel) and vertical position(pixel) of a Run element in a FlowDocument?
Edit:
All i need to do is scroll to that position and make it the top line of the FlowDocument.
To Answer Your Question
The code needed to get the position of a content element in a document is all internal to .NET and not publically exposed. You would need access to an IContentHost implementation, which the built-in document viewers do not publically expose. So, there is no supported way to do what you are asking.
To Solve Your Actual Problem
There is a way to achieve your desired result of scrolling the element to the top of the view. What you want to do is scroll to the end of the document, then call BringIntoView on the element you want to have at the top.
There are multiple ways a FlowDocument can be displayed in an application. How you handle the scrolling depends on which control you are using to present the FlowDocument.
In a RichTextBox, use the ScrollToEnd method.
In a FlowDocumentScrollViewer, you will need to get its internal ScrollViewer and call ScrollToBottom on it. (You have to wait until the control is loaded before you can get a template part from it.)
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
mScrollViewer = mViewer.Template.FindName("PART_ContentHost", mViewer) as ScrollViewer;
}
In a FlowDocumentReader, the process is a bit more complex.
When the control is loaded, register for changes to the ViewingMode property and run the handler once to account for the starting value:
private void MyControl_Loaded(object sender, RoutedEventArgs e)
{
var descriptor = DependencyPropertyDescriptor.FromProperty(FlowDocumentReader.ViewingModeProperty, typeof(FlowDocumentReader));
descriptor.AddValueChanged(mReader, (s, a) => Reader_ViewModeChanged());
Reader_ViewModeChanged();
}
In the handler, dig in to find the ScrollViewer. It will only be present when the ViewingMode is set to Scroll:
private void Reader_ViewModeChanged()
{
mScrollViewer = null;
if (mReader.ViewingMode == FlowDocumentReaderViewingMode.Scroll)
{
var contentHost = mReader.Template.FindName("PART_ContentHost", mReader) as DependencyObject;
if (contentHost != null && VisualTreeHelper.GetChildrenCount(contentHost) > 0)
{
var documentScrollViewer = VisualTreeHelper.GetChild(contentHost, 0) as FlowDocumentScrollViewer;
if (documentScrollViewer != null)
{
documentScrollViewer.ApplyTemplate();
mScrollViewer = documentScrollViewer.Template.FindName("PART_ContentHost", documentScrollViewer) as ScrollViewer;
}
}
}
}
Once you have the ScrollViewer, you can call ScrollToBottom on it when desired.
Now, scroll to the bottom of the document, then call BringIntoView on your Run, and it should be at the top of the view.
Does not bring it to the top but just call BringIntoView on the Run. Save a reference to the Run.
It may be late but i still want to share the way i DID it in WPF.
You need an offset to do so.
As the above said: Flow gave you:
flow.ScrollToHome(); // Bottom
But also gave: ScrollToVerticalOffset (get from Rect)
if you have index (offset of the char/line) - you can find it in you saved data or get the TextPointer with flow.Selection.Start/End
TextPointer t_st = flow.Selection.Start;
double offset = flow.Document.ContentStart.GetOffsetToPosition(t_st);
private void gotoOffset(double offset)
{
TextPointer myTextPointer1 = flow.Document.ContentStart.GetPositionAtOffset((int)offset);
flow.Selection.Select(myTextPointer1, myTextPointer1);
flow.Focus();
Rect screenPos2 = myTextPointer1.GetCharacterRect(LogicalDirection.Forward);
double offset2 = screenPos2.Top;
Thread.Sleep(100);
flow.ScrollToVerticalOffset(offset2);
flow.Focus();
}
As the code above, We get the Rect from TextPointer, the Textpointer and get from Offset.
The focus just to make sure to place the cursor in right place.
Sometime the issue happen when you jump to many offset.
I recomment to trigger flow.ScrollToHome(); Before jump (because this ScrollToVerticalOffset true from the start, not any line)

Animate (smoothly) ScrollViewer programmatically

Is there a way to smoothly animate a ScrollViewers vertical offset in Windows Phone 8.1 Runtime?
I have tried using the ScrollViewer.ChangeView() method and the change of vertical offset is not animated no matter if I set the disableAnimation parameter to true or false.
For example: myScrollViewer.ChangeView(null, myScrollViewer.VerticalOffset + p, null, false);
The offset is changed without animation.
I also tried using a vertical offset mediator:
/// <summary>
/// Mediator that forwards Offset property changes on to a ScrollViewer
/// instance to enable the animation of Horizontal/VerticalOffset.
/// </summary>
public sealed class ScrollViewerOffsetMediator : FrameworkElement
{
/// <summary>
/// ScrollViewer instance to forward Offset changes on to.
/// </summary>
public ScrollViewer ScrollViewer
{
get { return (ScrollViewer)GetValue(ScrollViewerProperty); }
set { SetValue(ScrollViewerProperty, value); }
}
public static readonly DependencyProperty ScrollViewerProperty =
DependencyProperty.Register("ScrollViewer",
typeof(ScrollViewer),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(null, OnScrollViewerChanged));
private static void OnScrollViewerChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = (ScrollViewer)(e.NewValue);
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset(mediator.VerticalOffset);
}
}
/// <summary>
/// VerticalOffset property to forward to the ScrollViewer.
/// </summary>
public double VerticalOffset
{
get { return (double)GetValue(VerticalOffsetProperty); }
set { SetValue(VerticalOffsetProperty, value); }
}
public static readonly DependencyProperty VerticalOffsetProperty =
DependencyProperty.Register("VerticalOffset",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(0.0, OnVerticalOffsetChanged));
public static void OnVerticalOffsetChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
if (null != mediator.ScrollViewer)
{
mediator.ScrollViewer.ScrollToVerticalOffset((double)(e.NewValue));
}
}
/// <summary>
/// Multiplier for ScrollableHeight property to forward to the ScrollViewer.
/// </summary>
/// <remarks>
/// 0.0 means "scrolled to top"; 1.0 means "scrolled to bottom".
/// </remarks>
public double ScrollableHeightMultiplier
{
get { return (double)GetValue(ScrollableHeightMultiplierProperty); }
set { SetValue(ScrollableHeightMultiplierProperty, value); }
}
public static readonly DependencyProperty ScrollableHeightMultiplierProperty =
DependencyProperty.Register("ScrollableHeightMultiplier",
typeof(double),
typeof(ScrollViewerOffsetMediator),
new PropertyMetadata(0.0, OnScrollableHeightMultiplierChanged));
public static void OnScrollableHeightMultiplierChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var mediator = (ScrollViewerOffsetMediator)o;
var scrollViewer = mediator.ScrollViewer;
if (null != scrollViewer)
{
scrollViewer.ScrollToVerticalOffset((double)(e.NewValue) * scrollViewer.ScrollableHeight);
}
}
}
and I can animate the VerticalOffset property with DoubleAnimation:
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.EnableDependentAnimation = true;
da.From = Mediator.ScrollViewer.VerticalOffset;
da.To = da.From + p;
da.Duration = new Duration(TimeSpan.FromMilliseconds(300));
da.EasingFunction = new ExponentialEase() { EasingMode = EasingMode.EaseOut };
Storyboard.SetTarget(da, Mediator);
Storyboard.SetTargetProperty(da, "(Mediator.VerticalOffset)");
sb.Children.Add(da);
sb.Begin();
Mediator is declared in XAML.
But this animation is not smooth on my device (Lumia 930).
You should stick with ChangeView for scrolling animations regardless of whether data virtualization is on or not.
Without seeing your code where the ChangeView doesn't work, it's a bit hard to guess what's really going on but there are a couple of things that you can try.
First approach is to add a Task.Delay(1) before calling ChangeView, just to give the OS some time to finish off other concurrent UI tasks.
await Task.Delay(1);
scrollViewer.ChangeView(null, scrollViewer.ScrollableHeight, null, false);
The second approach is a bit more complex. What I've noticed is that, when you have many complex items in the ListView, the scrolling animation from the first item to the last (from the ChangeView method) isn't very smooth at all.
This is because the ListView first needs to realize/render many items along the way due to data virtualization and then does the animated scrolling. Not very efficient IMHO.
What I came up with is this - First, use a non-animated ListView.ScrollIntoView to scroll to the last item just to get it realized. Then, call ChangeView to move the offset up to a size of the ActualHeight * 2 of the ListView with animation disabled (you can change it to whatever size you want based on your app's scrolling experience). Finally, call ChangeView again to scroll back to the end, with animation this time. Doing this will give a much better scrolling experience 'cause the scrolling distance is just the ActualHeight of the ListView.
Keep in mind that when the item you want to scroll to is already realized on the UI, you don't want to do anything above. You simply just calculate the distance between this item and the top of the ScrollViewer and call ChangeView to scroll to it.
I already wrapped the logic above in this answer's Update 2 section (thanks to this question I realized my initial answer doesn't work when virtualization is on :p). Let me know how you go.
I think that question has already been answered here:
Animated (Smooth) scrolling on ScrollViewer
There is also the WinRT XAML Toolki, which provides "a way to scroll a ScrollViewer to specified offset with animation":
http://winrtxamltoolkit.codeplex.com/
With ScrollToVerticalOffset deprecated/obsolete in newer builds of Windows 10 (leaving the ScrollViewOffSetMediator extension control no longer working), and the new ChangeView method not actually providing smooth or controllable animation, a new solution is needed. Please see my answer here which allows one to smoothly animate and zoom the ScrollViewer and its contents to any desired position, regardless of where the application's end user has the scrollbars initially positioned:
How to scroll to element in UWP
I believe this article is what you're looking for and it seems the method he used is working for you.
Quick Way:
Add offset dependency parameter manually to scrollviewer.
Duplicate your scrollviewer
Use an animator.

UserControl not rendering within FlowLayoutPanel when dock changed

When I add my UserControls to a FlowLayoutPanel, they display properly. When I change the Dock or Anchor properties on the UserControls before adding them, they are still added but do not render.
According to "How to: Anchor and Dock Child Controls" this should be possible.
I can tell that the controls are added (despite not drawing) because adding enough of them causes a vertical scrollbar to appear.
Setting the "Dock" property of the UserControls to "Left" or "None" will cause them to render, but none of the other options.
Setting the "Anchor" property on the UserControls to anything but Top | Left does not render.
Setting the dock before or after adding the control makes no difference (Add, Dock vs. Dock, Add).
The FlowLayoutPanel is itself is docked (Fill), has FlowDirection set to TopDown, has WrapContents set to false, has AutoScroll set to true, and is otherwise default.
I am using .NET 3.5.
In answer to a comment, the two commented lines are the locations I tried to change the dock. The second spot definitely makes more sense, but I tried the other because it couldn't hurt.
public void CreateObjectControl( object o )
{
ObjectControl oc = new ObjectControl();
oc.MyObject = o;
//This was a spot I mentioned:
//oc.Dock = DockStyle.Fill;
ObjectDictionary.Add( o, oc );
flowLayoutPanel1.Controls.Add( oc );
//This is the other spot I mentioned:
oc.Dock = DockStyle.Fill;
}
try using SuspendLayout and Resumelayout function for the controls before making any amendments which need rendering for proper viewing.
You could see the code from Designer.cs for that particular control
Syntax
control.SuspendLayout();
{Your code for designer amendments}
control.resumeaLayout();
I think I may have found a workaround (read: dirty trick) ... this answer helped to point me in the right direction. Here's an excerpt from the MS article that you also linked to:
For vertical flow directions, the FlowLayoutPanel control calculates the width of an implied column from the widest child control in the column. All other controls in this column with Anchor or Dock properties are aligned or stretched to fit this implied column.
The behavior works in a similar way for horizontal flow directions. The FlowLayoutPanel control calculates the height of an implied row from the tallest child control in the row, and all docked or anchored child controls in this row are aligned or sized to fit the implied row.
This page does not specifically mention that you can't Dock/Anchor the tallest/widest control. But as this control defines the layout behaviour of the FlowLayoutPanel, and thus influences the way all other sibling controls are displayed, it is well possible that Dock and Anchor don't work properly for that 'master control'. Even though I can't find any official documentation regarding that, I believe it to be the case.
So, which options do we have? At runtime, we could add a panel control of height 0 and width of the FlowLayoutPanel client area before you add your usercontrol. You can even set that panel's visibility to false. Subscribing to some Resize/Layout events of the FlowLayoutPanel to keep that panel's size will to the trick. But this does not play nicely at design time. The events won't fire and thus you can't really design the surface the way you want it to look.
I'd prefer a solution that "just works" at design time as well. So, here's an attempt at an "invisible" control that I put together, to fix the controls resizing to zero width if no other control is present. Dropping this as first control onto the FlowLayoutPanel at design time seems to provide the desired effect, and any control subsequently placed on the FlowLayoutPanel is anchorable to the right without shrinking to zero width. The only problem is that, once this invisible control is there, it seems I can't remove it anymore via the IDE. It probably needs some special treatment using a ControlDesigner to achieve that. It can still be removed in the form's designer code though.
This control, once placed onto the FlowLayoutPanel, will listen for resize events of it's parent control, and resize itself according to the ClientSize of the parent control. Use with caution, as this may contain pitfalls that didn't occur to me during the few hours I played with this. For example, I didn't try placing controls that were wider than the FlowLayoutPanel's client area.
As a side note, what will still fail is trying to anchor to the bottom, but that wasn't part of the question ;-)
using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
namespace ControlTest
{
public sealed class InvisibleControl : Control
{
public InvisibleControl()
{
TabStop = false;
}
#region public interface
// Reduce the temptation ...
public new AnchorStyles Anchor
{
get { return base.Anchor; }
set { base.Anchor = AnchorStyles.None; }
}
public new DockStyle Dock
{
get { return base.Dock; }
set { base.Dock = DockStyle.None; }
}
// We don't ever want to move away from (0,0)
public new Point Location
{
get { return base.Location; }
set { base.Location = Point.Empty; }
}
// Horizontal or vertical orientation?
private Orientation _orientation = Orientation.Horizontal;
[DefaultValue(typeof(Orientation), "Horizontal")]
public Orientation Orientation
{
get { return _orientation; }
set
{
if (_orientation == value) return;
_orientation = value;
ChangeSize();
}
}
#endregion
#region overrides of default behaviour
// We don't want any margin around us
protected override Padding DefaultMargin => Padding.Empty;
// Clean up parent references
protected override void Dispose(bool disposing)
{
if (disposing)
SetParent(null);
base.Dispose(disposing);
}
// This seems to be needed for IDE support, as OnParentChanged does not seem
// to fire if the control is dropped onto a surface for the first time
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
ChangeSize();
}
// Make sure we don't inadvertantly paint anything
protected override void OnPaint(PaintEventArgs e) { }
protected override void OnPaintBackground(PaintEventArgs pevent) { }
// If the parent changes, we need to:
// A) Unsubscribe from the previous parent's Resize event, if applicable
// B) Subscribe to the new parent's Resize event
// C) Resize our control according to the new parent dimensions
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
// Perform A+B
SetParent(Parent);
// Perform C
ChangeSize();
}
// We don't really want to be resized, so deal with it
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
ChangeSize();
}
#endregion
#region private stuff
// Make this a default handler signature with optional params, so that this can
// directly subscribe to the parent resize event, but also be called without parameters
private void ChangeSize(object sender = null, EventArgs e = null)
{
Rectangle client = Parent?.ClientRectangle ?? new Rectangle(0, 0, 10, 10);
Size proposedSize = _orientation == Orientation.Horizontal
? new Size(client.Width, 0)
: new Size(0, client.Height);
if (!Size.Equals(proposedSize)) Size = proposedSize;
}
// Handles reparenting
private Control boundParent;
private void SetParent(Control parent)
{
if (boundParent != null)
boundParent.Resize -= ChangeSize;
boundParent = parent;
if (boundParent != null)
boundParent.Resize += ChangeSize;
}
#endregion
}
}

Drag WPF Popup control

the WPF Popup control is nice, but somewhat limited in my opinion. is there a way to "drag" a popup around when it is opened (like with the DragMove() method of windows)?
can this be done without big problems or do i have to write a substitute for the popup class myself?
thanks
Here's a simple solution using a Thumb.
Subclass Popup in XAML and codebehind
Add a Thumb with width/height set to 0 (this could also be done in XAML)
Listen for MouseDown events on the Popup and raise the same event on the Thumb
Move popup on DragDelta
XAML:
<Popup x:Class="PopupTest.DraggablePopup" ...>
<Canvas x:Name="ContentCanvas">
</Canvas>
</Popup>
C#:
public partial class DraggablePopup : Popup
{
public DraggablePopup()
{
var thumb = new Thumb
{
Width = 0,
Height = 0,
};
ContentCanvas.Children.Add(thumb);
MouseDown += (sender, e) =>
{
thumb.RaiseEvent(e);
};
thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
}
There is no DragMove for PopUp. Just a small work around, there is lot of improvements you can add to this.
<Popup x:Name="pop" IsOpen="True" Height="200" Placement="AbsolutePoint" Width="200">
<Rectangle Stretch="Fill" Fill="Red"/>
</Popup>
In the code behind , add this mousemove event
pop.MouseMove += new MouseEventHandler(pop_MouseMove);
void pop_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
pop.PlacementRectangle = new Rect(new Point(e.GetPosition(this).X,
e.GetPosition(this).Y),new Point(200,200));
}
}
Building off of Jobi Joy's answer, I found a re-useable solution that allows you to add as a control within xaml of an existing control/page. Which was not possible adding as Xaml with a Name since it has a different scope.
[ContentProperty("Child")]
[DefaultEvent("Opened")]
[DefaultProperty("Child")]
[Localizability(LocalizationCategory.None)]
public class DraggablePopup : Popup
{
public DraggablePopup()
{
MouseDown += (sender, e) =>
{
Thumb.RaiseEvent(e);
};
Thumb.DragDelta += (sender, e) =>
{
HorizontalOffset += e.HorizontalChange;
VerticalOffset += e.VerticalChange;
};
}
/// <summary>
/// The original child added via Xaml
/// </summary>
public UIElement TrueChild { get; private set; }
public Thumb Thumb { get; private set; } = new Thumb
{
Width = 0,
Height = 0,
};
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
TrueChild = Child;
var surrogateChild = new StackPanel();
RemoveLogicalChild(TrueChild);
surrogateChild.Children.Add(Thumb);
surrogateChild.Children.Add(TrueChild);
AddLogicalChild(surrogateChild);
Child = surrogateChild;
}
}
Another way of achieving this is to set your Popup's placement to MousePoint. This makes the popup initially appear at the position of the mouse cursor.
Then you can either use a Thumb or MouseMove event to set the Popup's HorizontalOffset & VerticalOffset. These properties shift the Popup away from its original position as the user drags it.
Remember to reset HorizontalOffset and VerticalOffset back to zero for the next use of the popup!
The issue with loosing the mouse when moving too fast, could be resolved
This is taken from msdn:
The new window contains the Child content of Popup.
The Popup control maintains a reference to its Child content as a logical child. When the new window is created, the content of Popup becomes a visual child of the window and remains the logical child of Popup. Conversely, Popup remains the logical parent of its Child content.
In the other words, the child of the popup is displayed in standalone window.
So when trying to the following:
Popup.CaptureMouse() is capturing the wrapper window and not the popup itself. Instead using Popup.Child.CaptureMouse() captures the actual popup.
And all other events should be registered using Popup.Child.
Like Popup.Child.MouseMove, Popup.Child.LostCapture and so on
This has been tested and works perfectly fine
Contrary to what others have stated about this, I agree 100% with Jobi Joy's answer (which should honestly be the accepted answer). I saw a comment stating that the solution in the answer would cause memory fragmentation. This is not possible as creating new structs cannot cause memory fragmentation at all; in fact, using structs saves memory because they are stack-allocated. Furthermore, I think that this is actually the correct way to reposition a popup (after all, Microsoft added the PlacementRectangle property for a reason), so it is not a hack. Appending Thumbs and expecting a user to always place a Popup onto a canvas, however, is incredibly hacky and is not always a practical solution.
Private Point startPoint;
private void Window_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
startPoint = e.GetPosition(null);
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point relative = e.GetPosition(null);
Point AbsolutePos = new Point(relative.X + this.Left, relative.Y + this.Top);
this.Top = AbsolutePos.Y - startPoint.Y;
this.Left = AbsolutePos.X - startPoint.X;
}
}
This works for dragging my window, but like it was told if i move the mouse to fast, it would get out of window and stop raising the event. Without mentioning the dragging is not smooth at all. Does anyone knows how to do it properly, nice and smooth dragging, without loosing it when dragged too fast??? Post a simple example if possible, other than a whole tutorial that would get beginners like me lost in code. Thanks!

Categories

Resources