When does a PhoneApplicationPage get disposed? - silverlight

For example, if I have a page like this:
public partial class Page1 : PhoneApplicationPage
{
DispatcherTimer timer = new DispatcherTimer();
public Page1()
{
InitializeComponent();
timer.Interval = TimeSpan.FromSeconds(5);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
MessageBox.Show("timer tick");
}
}
In the app, I navigate to this page, it will pop up a message box every 5 seconds.
Then I press the "Back" button on the phone and navigate back to the previous page.
But the weird thing is that it still pops up a message box every 5 seconds.
I know I can stop the timer in the OnNavigatedFrom method, but why is this happening?
Isn't a page disposed after you press the back button on it?
Thanks

It will get disposed by the GC when nothing is keeping it awake. This DispatcherTimer keeps it awake, even though it was created by the page. My guess in the past has been that the DispatcherTimer is being referenced by the Dispatcher itself, and so can't clean up, or something along those lines.
To demonstrate add a finalize method
#if DEBUG
~MyPage() {
System.Diagnostics.Debug.WriteLine("Killing MyPage");
}
#endif
Then add a button somewhere on the main page to force a GC.Collect()
If you shut down the timer in OnNavigatedFrom your page will get cleaned up, if you do not, it will not.
I haven't tested this yet with Mango to see if it is smarter, but with the 7.0 tools I had to do a bit of work to get around this.

I think Because the dispatcher timer has long life time than the page ,and a good habbit is stop or cancel the eventhandler, so the page has the memory leak.I studying the gc,it's a little diffcult topic.....

Related

WPF OutOfMemory Exception after displaying images a couple of times

I created a voting system using wpf and capitalizing the wpf container ship model. In the system, a series of user controls that displays the candidates pictures is being used. after the end-user finishes voting, the system will automatically log out and wait for the next one to log in. After several consecutive vote castings, the pictures start to get missing one by one randomly. When I checked, it was because of the out of memory exception. As I continue to stress test it, more and more pictures start to disappear. I have a hunch it was because the system doesn't dispose the previous windows and user controls and pictures that has been displayed. What can I do to dispose the window or at least make sure the pictures that has been displayed be disposed after the closing of the window?
A few things you can do. First would be use a memory profiler. I personally love RedGate ANTS Memory Profiler it will tell you if your objects are getting cleaned up and I believe they have a free trial you can use.
Also how are your images being displayed? Does the window load them from disk? If there are a small number of images, I would suggest putting them in a ResourceDictionary like so:
<BitmapImage UriSource="/MyApp.Client;component/Images/Pinned.png" x:Key="Pinned" PresentationOptions:Freeze="True" />
<BitmapImage UriSource="/MyApp.Client;component/Images//Unpinned.png" x:Key="Unpinned" PresentationOptions:Freeze="True" />
Then in your XAML you can use:
<Image Source="{StaticResource: Unpinned}"/>
This will ensure your image is loaded only once. Everything that needs it gets a reference to it.
Also remember if you have any events subscribed on your window it can prevent your window from closing. You should always dereference your events when the window is closing. For instance:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += DoStuff();
}
private RoutedEventHandler DoStuff()
{
//some code here
}
protected override void OnClosing(CancelEventArgs e)
{
base.OnClosing(e);
Loaded -= DoStuff();
}
}
Beware generic methods. For instance if you did this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += (o, e) =>
{
//Do stuff here
};
}
}
There is no way to dereference that because that method is generic so there is nothing to put in your closing to get rid of it. This is a very common problem for memory leaks and preventing windows from closing.
Another common one often overlooked is the Timer class. If you start a timer and then close the window without stopping the timer, the Timer class will hold a reference to your window preventing it from being closed. Always ensure your timers are stopped when the window closes.

WinForm Automatically Close After Time Expires?

In Framework 4.0, I have a WinForm that is opened from another form, displays some stuff and a progress bar, and then sits there. I would like to close that "pop up" form after n secods if the user does not close it manually. What's the smartest way to do that?
Thanks.
Start a timer with the desired interval and then when it ticks the first time, close the form.
Something like this
private Timer _timer;
public PopupForm()
{
InitializeComponent();
_timer = new Timer();
_timer.Interval = 5000; // interval in milliseconds here.
_timer.Tick += (s, e) => this.Close();
_timer.Start();
}
Actually the smartest way would probably putting this in its own StartCountdown() method that takes the time as a parameter. Logic like this normally shouldn't be in a constructor strictly speaking...

Continuously updating an OpenTK GLControl in WindowsFormsHost in WPF - How?

I have an OpenTK GLControl embedden in a WindowsFormsHost in my WPF application.
I want to continuously update and render it.
In Winforms a solution would be to attach the UpdateAndRender method to the Application.Idle event, but there is no such thing in WPF.
So what would be the best way to do (60FPS) updating of my scene and GLControl ?
You can use a System.Timers.Timer to control how often your render code is called. In your window containing the GLControl-in-WindowsFormsHost, declare a private System.Timers.Timer _timer;, then when you're ready to start the rendering loop, set the timer interval and it's event handler, then start it up, as in the following example:
private void btnLoadModel_Click(object sender, RoutedEventArgs e)
{
LoadModel(); // do whatever you need to do to prepare your scene for rendering
_timer = new System.Timers.Timer(10.0); // in milliseconds - you might have to play with this value to throttle your framerate depending on how involved your update and render code is
_timer.Elapsed += TimerElapsed;
_timer.Start();
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
UpdateModel(); // this is where you'd do whatever you need to do to update your model per frame
// Invalidate will cause the Paint event on your GLControl to fire
_glControl.Invalidate(); // _glControl is obviously a private reference to the GLControl
}
You'll clearly need to add using System.Timers to your usings.
You can use Invalidate() for it. This causes the GLControl to redraw it's content.
If you call it at the end of Paint() you may blocking some UI rendering of the other WPF controls.
WPF provides a per frame render event: CompositionTarget.Rendering. This event is called before WPF wants to render the content. Subscribe from it and call Invalidate:
public YourConstructor()
{
//...
CompositionTarget.Rendering += CompositionTarget_Rendering;
}
void CompositionTarget_Rendering(object sender, EventArgs e)
{
_yourControl.Invalidate();
}
You need to unsubscribe if you don't use it anymore (to avoid memory leaks).
Here is a How to: Render on a Per Frame Interval Using CompositionTarget from MSDN.
I use GLControl with that method and it works fine. I did not checked how much FPS I have but it feels smooth.
You may also have a look on this: Why is Frame Rate in WPF Irregular and Not Limited To Monitor Refresh?

idle state detection silverlight 4 application

What's the best way to detect idle state for a silverlight application? I have read quite a few articles on the net by now and usually they are either for wpf/mobile apps etc.
I have created a DispatcherTimer which locks the screen after 5 minutes and it seems that I will have to go to every widget in every screen(my application has around 4-5 screens) and add a mousebuttondown or mouseenter eventhandler to reset this timer. This doesn't seem to be efficient but just adding the handler to the layroot is not helping either.
Any helpful suggestions?
Thanks
You don't need to modify every control. If you add the following code on startup:
Application.Current.RootVisual.MouseMove += new MouseEventHandler(RootVisual_MouseMove);
Application.Current.RootVisual.KeyDown += new KeyEventHandler(RootVisual_KeyDown);
With the following event handlers:
private void RootVisual_KeyDown(object sender, KeyEventArgs e)
{
idle = false;
}
private void RootVisual_MouseMove(object sender, MouseEventArgs e)
{
idle = false;
}
Where idle is the variable you use in your DispatcherTimer Tick event to check if things are happening or not.
As events bubble up the tree this should work for all your controls.
Handled events will not bubble up to root control. Instead you should use the AddHandler method with handledEventsToo = true.

In WPF how to show changed state on controls before and after a time-intensive process completes?

The LoadIt() method below takes 5-10 seconds to complete.
I want the message area to display "Loading..." before LoadIt() starts and display "Reloaded" after it finishes.
How can I do that?
The following code doesn't work. It seems to not update the label until everything is finished, at which point it just displays "Reloaded" again.
private void Button_Click(object sender, RoutedEventArgs e)
{
lblMessage.Text = "Loading...";
LoadIt();
lblMessage.Text = "Reloaded";
}
There's more than one solution discussed here:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/6fce9b7b-4a13-4c8d-8c3e-562667851baa/
you could move LoadIt to a separate thread, or you could simulate the WinForms Application.DoEvents but this is quite a hack (http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!526.entry)
You can use the Dispatcher object to start tasks in the background thread:
public delegate void LoadItDelegate();
this.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Background,
new LoadItDelegate(LoadIt));
Make sure its in the background, because the UI thread has more priority, so the UI gets updated.. and also move your "I am done message" to the end of your LoadIt method :)

Resources