Get target control from DoubleAnimation Completed event in WPF? - wpf

I'm hoping someone can help me with what I thought would be a relatively straight forward problem.
I am setting a fadeout animation in code using a DoubleAnimation object. It fades out an image, and then fires off the Completed event when it's done.
I would like to get the name of the control that the fadeout animation was applied to from within the event handler, but I can't find a way.
Any help appreciated. Thanks.
DispatcherTimer timer = new DispatcherTimer();
public MainWindow()
{
InitializeComponent();
image1.Visibility = System.Windows.Visibility.Visible;
image2.Visibility = System.Windows.Visibility.Collapsed;
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void FadeOut(UIElement element)
{
DoubleAnimation FadeOut = new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(0.5)));
FadeOut.Completed += new EventHandler(FadeOut_Completed);
element.BeginAnimation(OpacityProperty, FadeOut);
}
void FadeOut_Completed(object sender, EventArgs e)
{
// How to find out which control was targeted?
}
void timer_Tick(object sender, EventArgs e)
{
if (image1.Visibility == System.Windows.Visibility.Visible)
{
FadeOut(image1);
//image1.Visibility = System.Windows.Visibility.Collapsed;
//image2.Visibility = System.Windows.Visibility.Visible;
}
}

The following code gives you the target of completed animation. Place it in FadeOut_Completed() handler:
DependencyObject target = Storyboard.GetTarget(((sender as AnimationClock).Timeline as AnimationTimeline))
However this will only work if animation target object is specified. To do it add the following to FadeOut() method:
Storyboard.SetTarget(FadeOut, element);

Related

How to correctly coordinate Windows load animations and C# events?

My WPF application has a window load animation which I created using Blend. The actual animation works fine, but if I add logic to my window load event (using C#) the animation skips to end when the window finally renders.
My initial plan was to use Threading to solve this, but this too didn't work:
private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 30);
dispatcherTimer.Start();
lstRecipients.Visibility = Visibility.Hidden;
windowAdorner = new TransparentAdorner(BorderGrid);
if (!StaticHelpers.AWSConfigurationExists())
{
this.IsEnabled = false;
GettingStarted gettingStarted = new GettingStarted(this);
gettingStarted.Owner = this;
gettingStarted.ShowDialog();
this.IsEnabled = true;
}
else
{
Task SetAWSLabelsTask = new Task(new Action(() => SetAWSLabels()));
SetAWSLabelsTask.Start();
}
Task bounceHandler = new Task(new Action(() => processBounce()));
bounceHandler.Start();
//processBounce();
Task unSubscribeHandler = new Task(new Action(() => handleUnsubscriptions()));
unSubscribeHandler.Start();
}
I'm assuming the system is so busy creating the threads, and the creation is handled by the UI thread, that the animation has already finished by the time the Window is rendered.
What I'm missing is a good way to coordinate the animation, so that any business logic I have in MyWindow_Loaded occurs only after the animation has finished loading.
Is this possible?
EDIT: I also tried a thread sleep, and this too didn't work.
Ok Solved the problem.
In the XAML added a new event handler to the storyboard:
<Storyboard x:Key="SESLogoLoad" Completed="StoryCompleted">
Then created a method manually using C#:
private void StoryCompleted(object sender, EventArgs e)
{
//Windows onload stuff goes here...
}

How to detect a Touch Press and Hold gesture in a WPF application?

It is possible to detect a touch press and hold gesture with the MouseRightButtonDown event. Unfortunately it fires not until I release my finger from the screen. This is to late!
Does anyone have ideas? Thanks in advance.
It is possible to do that in an awaitable fashion. Create a timer with specific interval. Start it when user tapped and return the method when timer elapsed. If user release the hand, return the method with false flag.
public static Task<bool> TouchHold(this FrameworkElement element, TimeSpan duration)
{
DispatcherTimer timer = new DispatcherTimer();
TaskCompletionSource<bool> task = new TaskCompletionSource<bool>();
timer.Interval = duration;
MouseButtonEventHandler touchUpHandler = delegate
{
timer.Stop();
if (task.Task.Status == TaskStatus.Running)
{
task.SetResult(false);
}
};
element.PreviewMouseUp += touchUpHandler;
timer.Tick += delegate
{
element.PreviewMouseUp -= touchUpHandler;
timer.Stop();
task.SetResult(true);
};
timer.Start();
return task.Task;
}
For more information, read this post.
Great piece of code. I add just an example usage for completeness:
private async void btn_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (await TouchHold(btn, TimeSpan.FromSeconds(2)))
{
// todo: long press code goes here
}
}
And from XAML:
<Button Name="btn" PreviewMouseDown="btn_PreviewMouseDown">Press long</Button>
Use the Hold gesture provided by Blake.NUI toolkit
Either a button or label or image, we can use the MouseDown and MouseUp for starting the delay and Stopping the delay.
For MouseDown,
// Declaration of timer and timercount
int timerCount = 0;
DispatcherTimer dt = new DispatcherTimer();
public myConstructor()
{
dt.Interval = new TimeSpan(0, 0, 1);
}
// Mouse Down Event
private void EnterHoldState(object sender, TouchEventArgs e)
{
timerStarted();
}
//Mouse Up event
private void ExitHoldState(object sender, RoutedEventArgs e)
{
timerStopped();
}
// Stops the timer and resets the timer count to 0
private void timerStopped()
{
dt.Stop();
timerCount = 0;
}
// Starts the timer and sets delayCounter function for counting the delay seconds and acts on it
private void timerStarted()
{
dt.Tick += delayCounter;
dt.Start();
}
//Once delay timer reaches 2 seconds, the button navigates to nextpage.
private void delayCounter(object sender, EventArgs e)
{
timerCount++;
if (timerCount == 2)
{
this.NavigationService.Navigate(new nextPage());
}
}
I recently had to implement a Button where I needed to it to trigger after being pressed for five seconds.
To do this I created an attached behavior. I have the five seconds backed into the behavior as I did not need this configurable but easily done with a Dependency Property and it is hooked up to be used with a Command in an MVVM way but it could easily be changed to trigger Click.
<Button Command="{Binding Path=ButtonCommand}">
<i:Interaction.Behaviors>
<behaviors:PressAndHoldBehavior />
</i:Interaction.Behaviors>
</Button>
public sealed class PressAndHoldBehavior : Behavior<Button>
{
private DispatcherTimer dispatcherTimer;
protected override void OnAttached()
{
dispatcherTimer = new DispatcherTimer {Interval = TimeSpan.FromSeconds(5)};
dispatcherTimer.Tick += OnDispatcherTimerTick;
AssociatedObject.PreviewMouseLeftButtonDown += AssociatedObjectPreviewMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp += AssociatedObjectPreviewMouseLeftButtonUp;
}
protected override void OnDetaching()
{
dispatcherTimer.Stop();
dispatcherTimer.Tick -= OnDispatcherTimerTick;
dispatcherTimer = null;
AssociatedObject.PreviewMouseLeftButtonDown -= AssociatedObjectPreviewMouseLeftButtonDown;
AssociatedObject.PreviewMouseLeftButtonUp -= AssociatedObjectPreviewMouseLeftButtonUp;
}
private void AssociatedObjectPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
dispatcherTimer.Start();
e.Handled = true;
}
private void AssociatedObjectPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
dispatcherTimer.Stop();
e.Handled = true;
}
private void OnDispatcherTimerTick(object sender, EventArgs e)
{
AssociatedObject.Command.Execute(null);
}
}

Loading Screen Animation Stutter

I use a background worker to load a very large set of items while displaying a loading animation. On the run worker completed event, I set all those loaded items to an observable collection and then toggle off the loading screen. The problem is the progress bar is animating smoothly until the items are set to the observable collection, then it just stops animating. I imagine this is because the rendering of those items interfere with the animation. Is there any way to make the animation smooth during the rendering phase? When running the application the times I get are:
Loading = 1000 ms
Set properties = 43 ms
Rendering = 5083 ms
The rendering improves to 19 ms when I turn virtualizing on. I leave it turned off to demonstrate a scenario of long rendering times.
public ObservableCollection<string> Items { get; set; }
private IEnumerable<string> _items;
private BackgroundWorker _worker;
private long _loadTime;
private long _renderTime;
private long _setPropertiesTime;
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<string>();
_itemsGrid.DataContext = this;
_worker = new BackgroundWorker();
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
_renderLabel.Content = string.Empty;
_loadLabel.Content = string.Empty;
_loadingBorder.Visibility = Visibility.Visible;
_worker.RunWorkerAsync();
}
void DoWork(object sender, DoWorkEventArgs e)
{
_items = LoadItems();
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var setItemsTimer = Stopwatch.StartNew();
Items.Clear();
foreach (var item in _items)
{
Items.Add(item);
}
setItemsTimer.Stop();
_setPropertiesTime = setItemsTimer.ElapsedMilliseconds;
var timer = Stopwatch.StartNew();
Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
{
timer.Stop();
_loadLabel.Content = string.Format("Loading took {0} ms, Set properties took {1} ms", _loadTime, _setPropertiesTime);
_renderLabel.Content = string.Format("Rendering took {0} ms", timer.ElapsedMilliseconds);
_loadingBorder.Visibility = Visibility.Collapsed;
}));
}
IEnumerable<string> LoadItems()
{
var timer = Stopwatch.StartNew();
var items = new List<string>();
for (int i = 0; i < 5000; i++)
{
items.Add("Testing");
}
Thread.Sleep(1000);
timer.Stop();
_loadTime = timer.ElapsedMilliseconds;
return items;
}
One way is to create an overlay Window on top of your current Window running under a different UI thread and have the loading animation displayed in this Window and set the appropriate properties to make the Window look part of the loading Window.
This is in essence a splash screen.

Silverlight 4:How to delay Mouseenter event

i have a situation where : User moves mouse over the image .
If user keeps mouse on that image for specific time ex. 2 seconds then only i have to proceed
further in mouseenter event otherwise don't.
I have already refred to http://forums.silverlight.net/t/86671.aspx/1 but looks like mine is different case.
One option is to use a DispatchTimer to determine the length of the mouse over.
bool isMouseOverImage = false;
public void Image_MouseEnter(object sender, MouseEventArgs e)
{
this.isMouseOverImage = true;
var timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += (object timerSender, EventArgs timerArgs) =>
{
if(this.isMouseOverImage)
{
// write your code
}
// stop the timer
timer.Stop();
};
timer.Start();
}
public void Image_MouseLeave(object sender, MouseEventArgs e)
{
this.isMouseOverImage = false;
}
If you have multiple images, you should create a re-usable Behavior and attach it to each image. I can define code for that if that would help.

After playing a MediaElement, how can I play it again?

I have a variable MediaElement variable named TestAudio in my Silverlight app.
When I click the button, it plays the audio correctly.
But when I click the button again, it does not play the audio.
How can I make the MediaElement play a second time?
None of the tries below to put position back to 0 worked:
private void Button_Click_PlayTest(object sender, RoutedEventArgs e)
{
//TestAudio.Position = new TimeSpan(0, 0, 0);
//TestAudio.Position = TestAudio.Position.Add(new TimeSpan(0, 0, 0));
//TestAudio.Position = new TimeSpan(0, 0, 0, 0, 0);
//TestAudio.Position = TimeSpan.Zero;
TestAudio.Play();
}
I found it, you just have to stop the audio first, then set the position:
TestAudio.Stop();
TestAudio.Position = TimeSpan.Zero;
The MediaElement has no built-in support for looping playback. You can use the MediaEnded event and simply set the media position to zero or you can call the Stop() method. Either one will put you at the beginning of your video/audio for playback.
public partial class MainPage : UserControl
{
public MainPage()
{
// Required to initialize variables
InitializeComponent();
// Used for loopback.
MyME.MediaEnded += new RoutedEventHandler(MyME_MediaEnded);
}
// MediaElement has no looping capabilities so need to set the position back
// to the begining after the video finishes in order to play again.
// Or you can use the stop method
void MyME_MediaEnded(object sender, RoutedEventArgs e)
{
//MyME.Position = TimeSpan.Zero;
MyME.Stop();
}
private void BtnPlay_Click(object sender, RoutedEventArgs e)
{
MyME.Play();
}
private void BtnPause_Click(object sender, RoutedEventArgs e)
{
MyME.Pause();
}
private void BtnStop_Click(object sender, RoutedEventArgs e)
{
MyME.Stop();
}
}
I found the above did not work for me, and the only way I could get it working was to create a mediaelement dynamically. Heres the code I used - I copied the values from a MediaElement named mePlayClick I initially put in the XAML but you may not need to do this.
private void Play_MediaSound()
{
// Create new media element dynamically
MediaElement mediaElement = new MediaElement();
// Reuse settings in XAML
mediaElement.Volume = mePlayClick.Volume;
mediaElement.Source = mePlayClick.Source;
mediaElement.AutoPlay = mePlayClick.AutoPlay;
// WHen the media ends, remove the media element
mediaElement.MediaEnded += (sender, args) =>
{
LayoutRoot.Children.Remove(mediaElement);
mediaElement = null;
};
// Add the media element, must be in visual ui tree
LayoutRoot.Children.Add(mediaElement);
// When opened, play
mediaElement.MediaOpened += (sender, args) =>
{
mediaElement.Play();
};
}

Resources