Reuse Storyboard in Windows Phone app - c#

I've got a Storyboard used in my Windows Phone app:
<Canvas x:Name="myCanvas" Grid.Row="1">
<Canvas.Resources>
<Storyboard x:Name="sb">
<ColorAnimationUsingKeyFrames
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
AutoReverse="True">
<EasingColorKeyFrame KeyTime="00:00:0" Value="Black" />
<EasingColorKeyFrame KeyTime="00:00:0.25" Value="Red" />
</ColorAnimationUsingKeyFrames>
</Storyboard>
</Canvas.Resources>
</Canvas>
I've got multiple Rectangles that will be using this Storyboard, but it only seems to work the first time.
For example, the following code is used to display four different Rectangles but only the first one is displayed. The code does not error, but the last 3 Rectangles do not turn red and it appears the Storyboard doesn't even run.
sb.Stop();
sb.SetValue(Storyboard.TargetNameProperty, myRect1.name);
sb.Begin();
sb.Stop();
sb.SetValue(Storyboard.TargetNameProperty, myRect2.name);
sb.Begin();
sb.Stop();
sb.SetValue(Storyboard.TargetNameProperty, myRect3.name);
sb.Begin();
sb.Stop();
sb.SetValue(Storyboard.TargetNameProperty, myRect4.name);
sb.Begin();
Can anyone see what I'm doing wrong, or know how to get my Storyboard to be re-usable?

First, what you're doing wrong:
The storyboard execution is asynchronous. When you call the Storyboard.Begin method, the storyboard begins in background and your code continues to execute. Therefore, You're calling Storyboard.Stop right after starting it! The only one you don't stop is the last one, and that's why it's the only rectangle which color changes.
If you want to chain your animations, you have to subscribe to the Completed event to know when the storyboard ends, then restart it for the next controls. Here is one way of doing that:
private Rectangle[] ControlsToAnimate;
private int CurrentIndex;
private void Button_Click(object sender, RoutedEventArgs e)
{
this.ControlsToAnimate = new[] { this.Rectangle1, this.Rectangle2 };
this.Storyboard1.Completed += StoryboardCompleted;
this.AnimateNextControl();
}
private void AnimateNextControl()
{
if (this.CurrentIndex >= this.ControlsToAnimate.Length)
{
this.CurrentIndex = 0;
return;
}
var nextControl = this.ControlsToAnimate[this.CurrentIndex];
this.CurrentIndex++;
this.Storyboard1.Stop();
this.Storyboard1.SetValue(Storyboard.TargetNameProperty, nextControl.Name);
this.Storyboard1.Begin();
}
private void StoryboardCompleted(object sender, EventArgs e)
{
this.AnimateNextControl();
}
Now, you're going to face two problems:
When assigning the storyboard to a new control, the color of the previous control will return to its original value (before the storyboard was started). If you want it to keep its new value, you've got to save it:
private void AnimateNextControl()
{
if (this.CurrentIndex > 0)
{
var brush = (SolidColorBrush)this.ControlsToAnimate[this.CurrentIndex - 1].Fill;
brush.Color = brush.Color;
}
if (this.CurrentIndex >= this.ControlsToAnimate.Length)
{
this.CurrentIndex = 0;
return;
}
var nextControl = this.ControlsToAnimate[this.CurrentIndex];
this.CurrentIndex++;
this.Storyboard1.Stop();
this.Storyboard1.SetValue(Storyboard.TargetNameProperty, nextControl.Name);
this.Storyboard1.Begin();
}
You can't animate two controls at the same time using a single storyboard. If you want to animate all your rectangles at the same time, you need to use one storyboard per control.

Once again I must make my comment an answer since I still cannot comment apparently.
Are you attempting to run these all concurrently? I do believe that only a single instance of a XAML defined storyboard exists so you may not be able to reuse it on multiple controls at the same time.
If all else fails you can just create a UserControl with a rectangle containing the storyboard which you CAN reuse. That code sample I provided on your previous question is from a Tile that flips using a storyboard. Since its a usercontrol, I can have as many tiles as I want flipping at the same time.

Related

Cannot apply animation to parent frame from page WPF c#

I have a page within a Frame.What i am trying to do is to apply an storyboard/animation from the page to the parent Frame. Generally, from user controls, i used the following code to get the parent :
var parent = (Frame)this.Parent;
But if i use the same code in my page to get the parent frame and apply the animation :
private void Goback_MouseDown(object sender, MouseButtonEventArgs e)
{
Storyboard sb = new Storyboard;
sb = this.FindResource("HideMainframe");
var parent = (Frame)this.Parent;
Storyboard.SetTarget(sb, parent);
sb.Begin();
}
Storyboard
<Storyboard x:Key="HideMainframe" Storyboard.TargetProperty="Opacity" >
<DoubleAnimation Duration="0:0:0.5" To="0" >
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseIn"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
i get an exception : No target was specified for 'System.Windows.Media.Animation.DoubleAnimation'..On the web,i came to know about VisualTreeHelper class but before i'ld go for that, i want to know,why my code is not working ? Or to be specific,why can't i get the parent frame from the page within ?
So it looks like Pages navigated in frames don't contain a "Parent" value, which is why the method you specified doesn't work, but walking the VisualTreeHelper does. What you are trying to achieve can be easily simplified by passing the Frame through the Page Constructor though.
Your Pages Code Behind
Frame f;
public Page1(Frame frame)
{
f = frame;
InitializeComponent();
}
Your button logic
private void Goback_MouseDown(object sender, MouseButtonEventArgs e)
{
Storyboard sb = (Storyboard)this.TryFindResource("HideMainframe");
Storyboard.SetTarget(sb, f);
sb.Begin();
}
So when navigating the frame, you'll just pass it through.
myFrame.Navigate(new Page1(myFrame));

Inconsistent dispatcher.Invoke behavior

I have created a wallboard application for a service desk team, which uses WPF for front-end and the Cisco database of the phones in the back-end. The application is made of two screens that show different information, and these are displayed in the same screen and change between each other with a System.Timers.Timer.
The application is made so that if WindowA is visible, WindowB is shown and then WindowA is hidden. The moment one of the Windows becomes visible, that Window's timer become active again which resumes the database calls, while the other Window's timer becomes disabled:
private static void InterfaceChanger_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (WindowA.Visibility == Visibility.Visible)
{
WindowAEnabled = false;
ChangeVisibility(Visibility.Visible, WindowB);
WindowBEnabled = true;
WindowB_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowA);
}
else
{
WindowBEnabled = false;
ChangeVisibility(Visibility.Visible, WindowA);
WindowAEnabled = true;
WindowA_Elapsed(null, null); // force the call of the timer's callback
ChangeVisibility(Visibility.Collapsed, WindowB);
}
}
private static void ChangeVisibility(Visibility visibility, Window window)
{
window.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
{
window.Visibility = visibility;
}, null);
}
The problem is that this works perfectly... at most 90% of the time. The problem is that sometimes, if for example WindowA's visibility is changed to Visible and WindowB's visibility is changed to Collapsed, WindowB collapses but WindowA takes 2-3 seconds to become visible, while most times WindowA becomes visible and it's not seen when WindowB collapses. This (when it doesn't work) results in the Desktop being visible instead of the application.
I originally used DispatcherPriority.Background but that resulted in the screen changer working 70-80% of the time, so I decided to change it for DispatcherPriority.Normal (DispatcherPriority.Sendresults basically in the same situation as Normal).
Questions:
Is this the normal behavior to be expected by the Dispatcher, taking into account this is running in x64 mode in a quad-core CPU?
Knowing that the queries are performed in async methods not awaited, shouldn't the Dispatcher take priority over the methods?
Is there another way (without using the Dispatcher, or using another Window property) to accomplish what I'm looking for?
This is the code used to access/start the Windows:
//WindowA:
<Application x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="WindowA.xaml">
//WindowA class:
public static WindowA WindowAInstance;
public WindowA()
{
// unnecessary code hidden
WindowAInstance = this;
WindowB b = new WindowB;
}
// WindowB class
public static WindowB WindowBInstance;
public WindowB()
{
// unnecessary code hidden
WindowBInstance = this;
}
// this is the code that starts the timers
public static void StartTimersHandling()
{
Database.RemoveAgents();
InterfaceChangerTimer = new System.Timers.Timer();
InterfaceChangerTimer.Interval = ApplicationArguments.InterfaceChangerTime;
InterfaceChangerTimer.Elapsed += InterfaceChanger_Elapsed;
InterfaceChangerTimer.AutoReset = true;
InterfaceChangerTimer.Start();
WindowATimer = new System.Timers.Timer();
WindowATimer.Interval = 1000;
WindowATimer.Elapsed += WindowATimer_Elapsed;
WindowATimer.AutoReset = true;
WindowATimer.Start();
WindowBTimer = new System.Timers.Timer();
WindowBTimer.Interval = 1000;
WindowBTimer.Elapsed += WindowBTimer_Elapsed;
WindowBTimer.AutoReset = true;
WindowBTimer.Start();
}
It sounds like you're writing a kiosk application (ie. full-screen, non-interactive). If this is the case I think you would be better off having a single window and switching the views inside it, rather than switching between two separate windows. Also, you need to separate the database query work from the refreshing of the window content. Furthermore, I think it would help if the views knew nothing about each other: at the moment your first window is tightly coupled to your second, which is not really a good idea.
In my opinion, if you changed your architecture a little, a lot of the problems you are having would disappear. Here's what I would recommend:
First, just go with a single window. Create two user controls (Project > Add User Control), and move your XAML layout from your existing windows into these two new controls. Then make your main window look something like this:
<Window x:Class="StackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:StackOverflow"
WindowState="Maximized" WindowStyle="None">
<Grid>
<my:UserControl1 x:Name="_first" Panel.ZIndex="1" />
<my:UserControl2 Panel.ZIndex="0" />
</Grid>
<Window.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard AutoReverse="True" RepeatBehavior="Forever">
<ObjectAnimationUsingKeyFrames BeginTime="0:0:5" Duration="0:0:5"
Storyboard.TargetName="_first"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0:0:0"
Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
</Window>
This is a full-screen window with no chrome that contains your two user controls (essentially the contents of your existing windows). They are layered in a Grid element so that one sits on top of the other: I'm using the Panel.ZIndex property to force the first control to the top of the pile. Finally, I'm using an animation (triggered when the window loads) that toggles the visibility of one of the controls to hide it after a certain period of time. The animation is set to repeat and auto-reverse, the effect of which is to hide one of the controls, then make it visible again. You can change the Duration attribute value to control how long each control "stays" visible; it's set to 5 seconds in this example, which means a 10 second delay between switches.
The key to this working is that the first user control, when visible, must fully obscure the other user control that lies beneath it. This is easy to accomplish by setting the background colour of the control.
Your user controls can contain anything that a window would contain. Here's the example user control XAML that I used:
<UserControl x:Class="StackOverflow.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Background="White" Padding="40">
<TextBlock Text="{Binding Number}" FontSize="60"
TextAlignment="Center" VerticalAlignment="Top" />
</UserControl>
As you can see it's just a TextBlock element whose Text property binds to a Number property defined in the user control's code-behind. I used the same XAML for both user controls, just varying the VerticalAlignment of the text so that I could tell which control was visible at any given time.
The code-behind looks like this (it's the same for both, with the exception of the class name):
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Threading;
namespace StackOverflow
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public UserControl1()
{
InitializeComponent();
DataContext = this;
_timer = new DispatcherTimer
{ Interval = TimeSpan.FromSeconds(5), IsEnabled = true };
_timer.Tick += (sender, e) => Task.Run(async () => await DoWorkAsync());
}
readonly DispatcherTimer _timer;
readonly Random _random = new Random();
public event PropertyChangedEventHandler PropertyChanged;
public int Number
{
get
{
return _number;
}
private set
{
if (_number != value)
{
_number = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Number"));
}
}
}
}
int _number;
async Task DoWorkAsync()
{
// Asynchronous code started on a thread pool thread
Console.WriteLine(GetType().Name + " starting work");
_timer.IsEnabled = false;
try
{
await Task.Delay(TimeSpan.FromSeconds(_random.Next(4, 12)));
Number++;
}
finally
{
_timer.IsEnabled = true;
}
Console.WriteLine(GetType().Name + " finished work");
}
}
}
It basically contains a single Number property (which implements INotifyPropertyChanged) that gets incremented by a "worker" method. The worker method is invoked by a timer: here, I'm using a DispatcherTimer, but as I'm not changing any UI elements directly any of the .NET timers would have done.
The worker is scheduled to run on the thread pool using Task.Run, and then runs asynchronously. I'm simulating a long-running job by waiting for a period of time with Task.Delay. This worker method would be where your database query gets called from. You can vary the gap between successive queries by setting the timer's Interval property. There's nothing to say that the gap between queries need be the same as the refresh interval of your UI (ie. the speed at which the two views are switched); indeed, as your query takes a variable amount of time, syncing the two would be tricky anyway.
Try to use Dispatcher.CurrentDispatcher instead of window.Dispatcher and BeginInvoke:
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, new Action(() =>
{
window.Visibility = visibility;
}));
Updated
Switch your timer to DispatcherTimer:
timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
timer.Tick += (sender, args) => InterfaceChanger_Elapsed();
timer.Start();

How can I use a C# function to access the WPF control that called it?

I'm working on a game level editor in WPF using C#.
I have a series of image controls for choosing textures, and I want each image to be clickable, with some visible feedback to show which one is selected.
Here's one of the image controls, along with a green highlight border that shows up when it's clicked:
<Image x:Name="tile_image1" Source="as asphalt_test.png" Stretch="Fill" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" Height="50" MouseDown="texture_click" Margin="0,93,69,0" RenderTransformOrigin="0.16,2.04"/>
<Border x:Name="tile_border" BorderBrush="Lime" BorderThickness="3" HorizontalAlignment="Right" Height="54" Margin="0,91,65,0" VerticalAlignment="Top" Width="54" Visibility="Hidden" />
My question involves the "texture_click" function.
I want to re-use the same function for each image control, which I can easily assign using the MouseDown attribute in XAML. However, what I don't know is how to tell from within the function which control called it, or how to access that control's property's, such as ".Source". I want to be able to grab the file name of the image, as well as move the coordinates of the green border behind the new selection.
Right now, I just have it hard-coded to the first image control. Clicks on the other images will call the function, but the function will only select the first image (not the one that was actually clicked).
// click on tile 1
private void texture_click (object sender, MouseButtonEventArgs e)
{
tile_border.Visibility = Visibility.Visible;
current_tilefile = tile_image1.Source;
string source_string = Convert.ToString (tile_image1.Source);
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
I tried using "sender", since I thought that might be the object that called the function, but that returned an error. I also tried calling the function with "texture_click (this)", but that was also no good. These were, admittedly, complete shots in the dark, so I wasn't surprised.
I'm still pretty new to this software, so any insight you guys can give would be great.
You just have to cast the sender parameter to the control type (Image in this case):
private void texture_click (object sender, MouseButtonEventArgs e)
{
//tile_border.Visibility = Visibility.Visible;
var image = sender as Image;
if (image != null)
{
current_tilefile = image.Source;
string source_string = image.Source.ToString();
int last_slash = source_string.LastIndexOf ('/');
current_tile = source_string.Substring (last_slash + 1, 3);
}
}
Of course, this doesn't give you access to the associated border. One thing you can do is to just dump the border into the Image's Tag property:
<Image x:Name="tile_image1" ... Tag="{Binding ElementName=tile_border}" />
<Border x:Name="tile_border" ... />
Then you can retrieve it, again by casting:
private void texture_click (object sender, MouseButtonEventArgs e)
{
var image = sender as Image;
if (image != null)
{
var border = image.Tag as Border;
if (border != null)
{
border.Visibility = Visibility.Visible;
}
// ...
}
}
Note that this (manipulating UI elements from code-behind) is not the ideal way to write a WPF application. Typically you would do something like this by using an existing control (like a ToggleButton), and re-writing its ControlTemplate so that its IsChecked visual state shows a border. But I realize that is a mouthful ...

Moving Object in ScrollViewer

I'm using the MVVM setup, for my app and I'm using an scrollViewer to scroll around an map. On this map I have an unit which I'd like to move around when I select it. However when I select the unit my ScrollViewer is still activated, is there an way to work around the scrollViewer or deactivate it such that I can move the unit around. I already tried changing the ManipulationModeProperty to Control, but thius makes the unit lag when I move it around.
My ScrollViewer is:
<ScrollViewer Width="768" Height="380" HorizontalScrollBarVisibility="Hidden">
<View:Map/>
</ScrollViewer>
The unit where I apply the manipulation is:
public void ManStart(ManipulationStartedEventArgs e)
{
myScrollViewer.SetValue(ScrollViewer.ManipulationModeProperty, ManipulationMode.Control);
public void ManDelta(ManipulationDeltaEventArgs e)
{
Point fingerPosition = e.DeltaManipulation.Translation;
Unit.x = fingerPosition.X + ChampModelSel.x;
Unit.y = fingerPosition.Y + ChampModelSel.y;
}
public void ManCompleted(ManipulationCompletedEventArgs e)
{
var myScrollViewer = FindParentOfType<ScrollViewer>(ChampViewModel) as ScrollViewer;
myScrollViewer.SetValue(ScrollViewer.ManipulationModeProperty, ManipulationMode.System);
}
I ended up finding a solution for the problem myself. Because I set the ManipulationMode to control, I guess I disable some propertys which end up giving an lag when you move your object. So I ended up trying to make a storyboard when ever I move my object, which fixed the problem and gives me a nice smooth motion when I select the object I wish to move. What I did was I went into my ManDelta:
public void ManDelta(ManipulationDeltaEventArgs e)
{
Point fingerPosition = e.DeltaManipulation.Translation;
Unit.x = fingerPosition.X + ChampModelSel.x;
Unit.y = fingerPosition.Y + ChampModelSel.y;
}
and added some storyboard from the code behind, using this http://www.buzzfrog.se/index.php/2013/06/create-storyboards-in-code-for-windows-phone/ as a guide line. The ManDelta occurs every time I select the object and activate the ManipulationDelta
Does the Unit have a property of Zindex? If yes, set Zindex of Scrollviewer and try setting the Unit ZIndex to a higher value than Scrollviewer.
How about something like creating a global Map variable lets say named MyMap.
When you navigate to a page that contains your map and do (App.Curren as App).MyMap = //your map on the page.
When you do MouseDown and it is being triggered in "when I select the unit" you block all moving allowances of the map in
(App.Current as App).MyMap.IsScrollable = false; //or something like that.
So that map couldn't scroll when your "UNIT" is in MouseDown state and only "UNIT" was movable. And on MouseUp in "UNIT" undo the:
(App.Current as App).MyMap.IsScrollable = true;

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