Storyboard.Completed Event Handler Preventing Code From Executing - c#

I am trying to create a simulation program that animates based on the user's input. I am running into an error when I try and create an eventhandler for mystoryboard.completed event. I have read numerous different API articles and forum posts on eventhandling and storyboards but I can't seem to find the cause of my error.
My code compiles and the window displays but anything after the line where I set up the eventhandler doesn't get executed. My MainWindow where I set everything up is shown below.
public MainWindow()
{
InitializeComponent();
titleTextBlock.Text = "MainWindow()";
//this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
mainSystem = new BalanceSystem(3);
leftBlock = new SystemComponents.Block(0, 100, 150, 100, mainSystem);
rightBlock = new SystemComponents.Block(1, 100, 150, 100, mainSystem);
balanceBeam = new SystemComponents.Bar(0, 0, 250, 150, 100, mainSystem);
mainSystem.addComponent(leftBlock, leftWeight);
mainSystem.addComponent(rightBlock, rightWeight);
mainSystem.addComponent(balanceBeam, balanceBar);
titleTextBlock.Text = "LOADED";
}
The constructor for "BalanceSystem" is when things start to go wrong. It steps into the constructor shown below:
public BalanceSystem(int count)
{
componentCount = count;
masterTimeline = new MovementTimeline(1);
}
After entering the constructor for "BalanceSystem" it moves on to the constructor for my custome class "MovementTimeline". The line that breaks everything is the creation and subscription of the eventhandler for masterStoryboard.Completed.
class MovementTimeline
{
private Storyboard masterStoryboard;
private Duration systemDuration;
public MovementTimeline(int totalTime)
{
systemDuration = new Duration(TimeSpan.FromSeconds(totalTime));
masterStoryboard.Completed += new EventHandler(masterStoryboard_Completed);
}
void masterStoryboard_Completed(object sender, EventArgs e)
{
masterStoryboard.Children.Clear();
//masterStoryboard.Completed -= masterStoryboard_Completed;
}
}
Once the compiler or program hits the line where the new EventHandler is created it stops executing the rest of my code and loads the window as is. I cannot for the life of me figure out why this is happening.

it looks to me like you are adding an eventhandler without ever creating a StoryBoard object

Related

Open a new form and bring it to the front whilst the main form is running in the backround

My code takes screenshots of the screen and when it recognised certain words a new form should open but it only opens and is brought to the front when the main form is at the front. I want to be able to minimize the form and when it detects a word I want it to bring the new form to the front of the screen, alternatively be able to bring all the forms to the front.
My current code is as follows
public void OpenAutoNotes(string Word,string Text, string Note,string Name)
{
if (Find(Word, Text))
{
AutoNotes AddNote = new AutoNotes(Note, Name);
AddNote.Show();
AddNote.BringToFront();
}
}
The Find() function works perfectly and it opens the form but the form stays minimized or behind the current window
To way I'm calling the form to be opened is using a timer and each tick of the timer the OpenAutoNotes() function is run checking if the word is found.
Here is the rest of the relevant code
public void InitTimer()
{
timer = new Timer();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 3000;
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
if (SwitchScan == true)
{
SearchKeyWords();
}
}
public void SearchKeyWords()
{
Bitmap CurrentImage = SelectImageCapture(499, 499, 540, 200, 0, 0);
pictureBox1.Image = CurrentImage;
string MessageText = ImageToText(CurrentImage);
OpenAutoNotes("car", MessageText, "Needs car rented", "TestforText");
OpenAutoNotes("Car", MessageText, "Needs car rented", "TestforText");
OpenAutoNotes("cot", MessageText, "Needs a cot", "TestforText");
OpenAutoNotes("Cot", MessageText, "Needs a cot", "TestforText");
}
Real Simple change, hope this can help someone else since I couldn't find anything on here with a similar problem
if (Find(Word, Text))
{
AutoNotes AddNote = new AutoNotes(Note, Name);
AddNote.TopMost = true;
AddNote.Show();
}

Binding Writeable bitmap to canvas

I'm trying to write a simple WPF program to implement Conway's Game of Life.
My Window consists of a toolbar and a canvas with and embedded Image, on which I'm trying to display a writeable bitmap.
I have a button on the toolbar which, when clicked, updates a single generation and then displays the resulting bitmap on the canvas image. I update the image directly with
img.Source = wbmp;
This works without a problem.
However, I'd like to show the ongoing updates, without having to click the "Update" button each time. So I tried implementing a loop of 10 iterations. However, the updated image is only shown after the 10th iteration is completed (i.e. I don;t see the first 9 generations)
My understanding is that I need to bind the Image control to the Writeable bitmap in order to "force" an update each generation. I've tried this with the code below - but now nothing displays at all. Initially I found that the PropertyChanged event didn't seem to be firing but I had no method assigned so, I added PropertyChanged = delegate {}; (I did this because the Internet told me to!)
I am really not sure where I'm going wrong. I'm rather clueless about WPF and binding in particular. (Much of my code is adapted copy-pasta.) Any help would be greatly appreciated!
<Canvas Name ="canvas" Grid.Row="1" Background="LightGray">
<Image Name="img" Source="{Binding GoLImage}"/>
</Canvas>
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private ImageSource golImage; //writeable bitmap;
public ImageSource GoLImage
{
get { return golImage; }
set
{
golImage = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GoLImage)));
}
}
}
private void Button_Run10_Click(object sender, RoutedEventArgs e)
{
BitmapPixelMaker bmp = new BitmapPixelMaker(1200, 800);
for (int i = 0; i < 10; i++)
{
goL.UpdateLifeGrid(); //goL is an instance of my class implementing the Game of Life
goL.LifeGridTobmp(bmp);
WriteableBitmap wbmp = bmp.MakeBitmap(96, 96);
//Trying to display bitmap
MyViewModel golDisplay = new MyViewModel();
golDisplay.GoLImage = wbmp; //This doesn't automatically display on each iteration
}
}
The for loop in your Button Click handler blocks the UI thread. And you have not assigned the view model instance to the DataContext property of the view.
Use a DispatcherTimer with a Tick event handler like shown below.
Do not create a new view model instance and a new WriteableBitmap in each cycle, but just modify the existing one - you should therefore change the view model property declaration to public WriteableBitmap GoLImage so that the ModifyBitmap method can access it.
private readonly MyViewModel golDisplay = new MyViewModel();
private readonly DispatcherTimer timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(0.1)
};
public MainWindow()
{
InitializeComponent();
golDisplay.GoLImage = WriteableBitmap(1200, 800, 96, 96, PixelFormats.Default, null);
DataContext = golDisplay;
timer.Tick += OnTimerTick;
timer.Start(); // optionally, call Start/Stop in a Button Click handler
}
private void OnTimerTick(object sender, EventArgs e)
{
var bmp = new BitmapPixelMaker(1200, 800);
goL.UpdateLifeGrid();
goL.LifeGridTobmp(bmp);
ModifyBitmap(bmp); // write directly to golDisplay.GoLImage
}
An alternative to a DispatcherTimer might be an simple loop over an asynchronous and awaited update call.
Something like shown below, where the Update method would perform the CPU-intensive calculations and then return the new pixel buffer
private async void OnWindowLoaded(object sender, RoutedEventArgs e)
{
while (true)
{
var buffer = await Task.Run(() => game.Update());
bitmap.WritePixels(new Int32Rect(0, 0, game.Width, game.Height),
buffer, game.Stride, 0);
}
}

Silverlight Global Mouse Down Event Handler

I am desperately trying to implement a "Session Inactivity Timeout" for an IT security policy.
sss
However, my inactivity reset logic isn't firing. What am I doing wrong? I can't attach a breakpoint due to Visual Studio crashing on attaching to Silverlight binary. I am wondering if the problem is something very subtle, such as at the time the App constructor is called, there is no "current RootVisual" yet. Tagging with #WPF as well since #Silverlight is dead/obsolescent. Sample code below.
namespace TestApp
{
public partial class App : Application
{
System.Windows.Threading.DispatcherTimer sessionTimer = new System.Windows.Threading.DispatcherTimer();
public App()
{
ResetSessionTimer();
this.RootVisual.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(rootVisual_MouseLeftButtonDown), true);
//this.RootVisual.MouseLeftButtonDown += rootVisual_MouseLeftButtonDown;
InitializeComponent();
}
private void ResetSessionTimer()
{
sessionTimer.Stop();
sessionTimer.Tick -= sessionTimer_Tick;
sessionTimer = new System.Windows.Threading.DispatcherTimer();
sessionTimer.Interval = TimeSpan.FromMinutes(1);
sessionTimer.Tick += sessionTimer_Tick;
sessionTimer.Start();
}
private void sessionTimer_Tick(object sender, EventArgs e)
{
System.Windows.Browser.HtmlPage.Document.GetElementById("LogoutButton").Invoke("click", null);
}
private void rootVisual_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// added this alert to see if code is even firing.
System.Windows.Browser.HtmlPage.Window.Invoke("alert", "hello, world");
ResetSessionTimer();
e.Handled = false;
}
}
}
I figured this out - somewhere in the code, the previous developer was overriding the this.RootVisual element with a new MainPage() in order to enable their homegrown dependency injection / MVVM architecture. I moved the logic from the App() constructor to right after this assignment operator, and it works like magic.

RelayCommand from ViewModel & DispatchTimer

Good afternoon,
I'm trying to fire an ICommand in the viewmodel... FROM the viewmodel, instead of from the UI.
The command works fine from the UI xaml, however, in this different scenario, it does not.
private DispatcherTimer telTimer;
public RelayCommand StartT_Command { get { return new RelayCommand(Exe_StartT_Command); } }
void Exe_StartT_Command(object parameter)
{
if (telTimer != null && telTimer.IsEnabled)
{
telTimer.Stop();
return;
}
telTimer = new DispatcherTimer();
telTimer.Tick += new EventHandler(TelTimerTick);
telTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
telTimer.Start();
}
private void TelTimerTick(object sender, EventArgs e) //Every Tick
{
Data.Te(Td);
}
Like I said, it runs fine from the UI, however, when called (see below) it runs all the way through telTimer.Start(); and then ... doesn't.
void KeyDown(int vKey)
{
if (vKey == 0x6A) //Num Pad * Key
{
this.StartT_Command.Execute(null);
}
}
Any ideas??
Thanks in advance.
EDIT1: I checked .IsEnabled, and the timer IS enabled. However, TelTimerTick() is not running.
EDIT2: I didn't mention that KeyDown is being called from different Thread. Would that have an affect on the event hitting TelTimerTick()?
I'm not quite sure if I follow, but if you just want to invoke some command from your viewmodel?
As MvGarnagle points out in his answer, your are allocating a new command everytime, do what he does or:
private ICommand startCommand;
public ICommand StartTCommand
{
get { return startCommand ?? (startCommand = new RelayCommand(ExeStartTCommand)); }
}
EDIT
DispatcherTimer telTimer; // Not allocated
void ExeStartTCommand()
{
// May be null
if telTimer!=null && telTimer.IsEnabled)
{
telTimer.Stop();
return;
}
telTimer = new DispatcherTimer();
telTimer.Tick += TelTimerTick;
telTimer.Interval = new TimeSpan(0, 0, 0, 0, 10);
telTimer.Start();
}
private void TelTimerTick(object sender, EventArgs e) //Every Tick
{
Data.Te(Td);
}
In your viewmodel just call ExeStartTCommand directly, don't fire the command, it's no need for that.
Now If this was a DO like a custom control, you would have to fire Commands so the views using the controls would consume these commands or more common, routed events.
EDIT:
Now for the code
// how is this hooked up? W32 wrap?
void KeyDown(int vKey)
{
if (vKey == 0x6A) //Num Pad * Key
// Have the dispatchers in your viewmodelbaseclass, this is just for simplicity
App.Current.Dispatcher.BeginInvoke(new Action(ExeStartTCommand));
}
You should really have a Dispatcher in your baseclass that's set to the dispatcher you want it to run on, and use that property instead of the one above. If you are dealing with a threading issue I need more context from you, kind of shooting in the dark here :)
Cheers,
Stian

How to stop animation at particular state

I'm creating a fadeIn/fadeOut animation from code behind. I was trying to stop animation at a particular blink time and it is working fine. But I want to make sure that when my animation stops it should stop in fadeOut state. Below is my code:
public void AddAnimation(long blinkDuration = 0)
{
var fadeIn = new DoubleAnimation(0.3, 1, TimeSpan.FromSeconds(1), FillBehavior.HoldEnd)
{
BeginTime = TimeSpan.FromSeconds(0)
};
var fadeOut = new DoubleAnimation(1.0, 0.3, TimeSpan.FromSeconds(1), FillBehavior.HoldEnd)
{
BeginTime = TimeSpan.FromSeconds(0.5)
};
storyboard = new Storyboard();
Storyboard.SetTarget(fadeIn, this);
Storyboard.SetTarget(fadeOut, this);
Storyboard.SetTargetProperty(fadeIn, new PropertyPath("(Opacity)"));
Storyboard.SetTargetProperty(fadeOut, new PropertyPath("(Opacity)"));
storyboard.RepeatBehavior = blinkDuration == 0
? RepeatBehavior.Forever
: new RepeatBehavior(new TimeSpan(0, 0, Convert.ToInt32(blinkDuration)));
storyboard.Children.Add(fadeIn);
storyboard.Children.Add(fadeOut);
storyboard.Begin();
}
My question is how I will make my icon state fadeOut on Storyboard Stop after blink interval.
I have fixed it. I changed my Storyboard.Begin call and registered storyboard.Completed event, on which I'm calling storyboard.Stop().
Below is my changed code:
storyboard.Completed += StoryboardCompleted;
storyboard.Begin(this, true);
void StoryboardCompleted(object sender, EventArgs e)
{
storyboard.Stop(this);
}
You can use a FillBehaviour property value of HoldEnd which will keep the last value (or the To value) that you set in your Storyboard. This way, you can ensure what the end value will be. The only problem that you may have using this property is that you may need to run your animation several times.
To do that, you can attach a handler to the Completed Event and re-run the Storyboard after it finishes if a certain condition is met (number of times run, or seconds passed, or data has arrived, etc.):
private void OnStoryboardCompleted(object sender, EventArgs e)
{
if (someConditionIsTrue) storyboard.Start();
}

Categories