I was wondering if there is a more efficient way of opening a fresh window in WPF than how presented in code below :
WindowConfigureDatabase windowConfigureDatabse;
private void ButtonConfigureDatabase_Click(object sender, RibbonControlEventArgs e)
{
if (windowConfigureDatabase == null)
{
windowConfigureDatabase = new WindowConfigureDatabase();
}
windowConfigureDatabase.Clear();
windowConfigureDatabase.Show();
windowConfigureDatabase.WindowState = WindowState.Normal;
}
Where windowConfigureDatabase is the new window I want to open. windowConfigureDatabase.Clear(); just resets all the values to default - there aren't many of them to reset. I was wondering whether or not this is the proper way of opening new windows in wpf. The other path I was thinking of was just simply creating a new window on each button click (that way I don't have to clear values each time...) but I'm afraid of allocating too much memory if a user opens the window and closes it a lot of times as I'm not quite sure if garbage collector picks the window up on OnClose event.
So basically my question is - does the garbage collector pick my windows up after I close them during Closing/Closed event? If not, what would be the proper way of managing the window's memory manually? Would adding a
windowConfigureDatabase = null
on Closed/OnClosing event do well?
does the garbage collector pick my windows up after I close them
during Closing/Closed event?
Yes, if unreachable. Read up on this for a better idea.
Would adding a
windowConfigureDatabase = null
on Closed/OnClosing event do well?
Yes. Failing to do this will prevent the window from being garbage collected until windowConfigureDatabase is overwritten or the object containing it is collected.
The memory used by a window depends on its dimensions and how much memory it allocates to do what it needs to do. You generally don't need to worry about this unless you're creating tons of windows(~30+) and/or large volumes of data.
The fastest way to allocate is to allocate up front(ideally at startup) and reuse when possible. Fortunately with windows this is relatively easy. The idea is to hide instead of close, and only close when truly no longer needed.
Like this:
// In each window class or as a base class:
private bool isClosable = false;
protected override void OnClosing(CancelEventArgs args)
{
// Prevent closing until allowed.
if (!isClosable) {
args.Cancel = true;
Hide();
}
base.OnClosing(args);
}
// Call this when you want to destroy the window like normal.
public void ForceClose()
{
isClosable = true;
Close();
}
Related
I inherited a WinForms application where we are using Dock management with WeifenLuo. I'm trying to use the SaveAsXml and LoadFromXml.
Which works fine if I am ok with a new form every time - a particular form needs to stay available right now and I was hoping to be able to just use the code to hide it on close.
The sample app just recreates the controls every time so I'm at a loss.
If I recreate the control it shows fine. However, trying to make it use the already created control never works.
Load XML - attempt doesn't work:
m_deserializeDockContent = new DeserializeDockContent(GetContentFromPersistString);
_dockPanel = null;
InitDockPanel(); //Initialize DockPanel to full screen
_dockPanel.LoadFromXml(filepath, m_deserializeDockContent);
private IDockContent GetContentFromPersistString(string persistString)
{
if (persistString == typeof(ExistingForm).ToString())
return existingForm;
throw new NotImplementedException();
}
At this point I tried the dispose around _dockPanel and handling the dockPanel of the ExistingForm, but nothing seems to change the outcome. Reading online I discovered maybe blocking the close may help, but still doesn't work.
Example of handling the Form close event:
public ExistingForm()
{
InitializeComponent();
HideOnClose = true;
this.FormClosing += ExistingForm_FormClosing;
}
private void ExistingForm_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
this.Hide();
}
Updating LoadXml code to include existingForm.DockHandler.Close();, existingForm.DockHandler.Dispose();, or _dockPanel.Dispose();
Which doesn't work at all and I get an error because the form is disposed of.
The only thing that seems to work is recreating the form
if (persistString == typeof(ExistingForm).ToString())
return new ExistingForm();
Just wondering if I'm missing some handling of the DockHandler (no pun intended).
So I have a simple class:
public class GridButton : Button
{
public event EventHandler OnClick;
public GridButton()
{
this.Clicked += GridButton_Clicked;
}
private void GridButton_Clicked(object sender, EventArgs e)
{
HandleTap();
}
Now as this is a view control I am not in charge of when this object is disposed.
Is the above code a memory leak or is the GarbageCollector clever enough to work out that the event handler is self referencing and so doesn't cause a leak.
If this is a memory leak what is the correct solution?
Should I be using a more WeakRefence like the one oulined here?
http://paulstovell.com/blog/weakevents
The .NET garbage collector will find and delete cycles (this -> OnClick -> this.GridButton_Clicked).
"Memory leaks" in .NET tend to occur when you have say a collection (be that a list, map, events, whatever) that will never be freed (e.g. a global/static variable, or up at the start of the stack which won't go out of scope during the program), and then you keep adding more items to that over time, but never remove them.
For example:
public GridButton()
{
Globals.OnRefreshClicked += Refresh;
}
private void Refresh()
{
...update text, state, whatever...
}
Where "Globals" is anything long-lived. Now even if you remove this button from the GUI, unless you also remove that event, the Globals.OnRefreshClicked -> this.Refresh reference will keep your object alive, at least as long as Globals, which could be until the program terminates.
I'm implementing INavigation in my MVVM Light Toolkit based Windows WPF application. This is implementation of NavigateTo:
private Frame GetFrame()
{
return (Application.Current.MainWindow as Generator.MainWindow).MainFrame; ;
}
public void NavigateTo(string pageKey)
{
CurrentPageKey = pageKey;
var obj = Activator.CreateInstance(_keyPageTypeMapping[pageKey]);
GetFrame()?.Navigate(obj);
}
When I navigate to new page, the new page is created and displayed. However, when I navigate away, I don't see that the old page's destructor has been called. Even after 10-20 navigations I don't see it. I only see desctructor is called after I exit application. Question is what is the best way to dispose old page?
What you are doing should be fine. If the Garbage Collector has not disposed of the pages yet then there hasn't been a need to do so yet. If you are concerned with if these old pages can be Garbage Collected, or if you just want to force the collection to happen. Try explicitly calling the Garbage collector with:
GC.Collect();
I'm having an issue with regards on memory management with our system.
Basically here is my design:
Load the List of the Records.
frmBrowse Memory used
90MB
After clicking record, it will pop another form which is the detailed form of the record selected
frmAP
Memory used 110MB
private void ViewRecord()
{
try
{
if(oAP!=null)
oAP = new frmAP();
oAP.LoadRecordDetails();
oAP.Show();
}
catch (Exception ex)
{
clsClass.oGenMethods.ErrorMessage(ex.Message, "frmBrowse", "EditSearchFields");
}
}
When the user CLOSE, i actually Hide it (frmAP )to prevent re-query datas such as lookup tables and others - frmAP - Memory used 110MB
On the frmAP_Close()
this.Hide();
-Memory at 110MB
After hiding the form (frmSomeForm ) the memory remains at 110MB
When the user close the Form(frmBrowse) where "frmAP" was called/instantiate
public void tsClose_Click(object sender, EventArgs e)
{
if (oAP != null)
{
oAP .Dispose();
oAP .Close();
GC.Collect();
}
}
The Problem is the Memory is Still at ~110MB Whether the List(frmBrowse) Form and the Record form(frmAp) was closed.
Please advice
Thanks in Advance
UPDATE:
i Tried to delete .Hide() to isolate the problem, but still the memory keeps growing
Calling Close does not release the memory associated with an object. That's the job of the garbage collector. Close or Dispose simply tells the object to release any unmanaged memory or resources it is using. The object, and everything it's referencing, still stays in memory.
To ensure oAP is collected, set it to null:
oAP.Close();
oAP = null;
However, this is likely the wrong thing to do. The whole point of a garbage collector is to collect memory from unreferenced objects when it's needed. So let it do its job, don't force it to run, you don't need to set oAP to null, and oAP and everything it references will be collected when the GC needs to.
I have a first form that calls another one and then disappears (frm.visible = false).
This form should not come back after being called once, but it's the main form (the first one that appears when you launch the program). I am trying to make it so when you close the second form, both forms close, I have tried multiple things but all of them leave the process active.
This is the code that I use :
private void frmCreation_FormClosing(object sender, FormClosingEventArgs e)
{
frmProperties frm = new frmProperties();
frm.Dispose();
}
//I have also tried frm.Close() which also does not work
This code close the two forms, but the process remains active. How do I counter this?
Application.Exit() tells your App to close itself.
Environment.Exit(0) tells Windows to kill it.
I prefer the latter since it really closes your app no matter what.
Maybe you are looking for Application.Exit()?
I can believe the solution here is : Do not handle it.
If the process is still pending that means you are not disposing your resources properly.
Using Application.Exit() or asking the system to do it Environment.Exit(0) may be logged in the system as an error occurred and you are better to know how to properly close a process than relied on Application.Exit(), if you want to close a thread of your app you have to know how to collect those garbage.
You can re-implement the Dispose method to Dispose services, sockets, streams, almost everything that has a .Dispose available.
public class MyClass: IMyClass, IDisposable
{
private bool _disposed = false;
// ...
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// dispose your stuff you created in this class
// do the same for other classes
// some examples
/*
_webClient.Dispose();
_connector.DataAvailable -= ConnectorHasDataComing
_socket.Dispose();
_timer.Dispose();
_taskLogs.ForEach(x => {
x.Token.Cancel();
x.Task.Wait();
x.Task.Dispose();
});
*/
}
// dispose native events
_disposed = true;
}
If you use System.Threading.Thread or System.Threading.Tasks.Task or System.IO.MemoryStream (or other kind of Stream - Writer/Reader), and others that requires a CancellationTokenSource. If you created the ressource in the class when you are disposing the class, use the Token.Cancel() method to let it know its parent is being disposed and .Wait() for it before calling .Dispose()
public async Task Run(CancellationTokenSource cancellationTokenSource)
{
// ...
while (Running) {
if (cancellationTokenSource.IsCancellationRequested) return;
// ....
}
// ....
using (var reader = new WaveFileReader(tempFile))
{
reader.Position = 0;
await reader.CopyToAsync(fileWriter,81920, cancellationTokenSource.Token);
}
}
I found my issue using the Diagnostic Tools when my Debug was still pending on something after closing the app.
If you use CPU Usage you can click on Break All and it set a breakpoint.
You can then see profiler and find what are your top functions, you might find out that your form is disposed but you have a Thread or Task that invokes fields on your form.
For my case, I was using a filewriter and I implemented IDisposable in that class but it sometimes was about or actual doing a transfer of data between a filereader and itself using .copyTo so it was pending without throwing an exception.
After clicking on one the events, click on Go to Source code and place a breakpoint, you may see events that your code is stocked on.
Otherwise, you can use in the same tool the tab Memory Usage to take a snapshot and look at the Heap and Objects diff or the tab CPU Usage and look at a recorded Profile. If find my copyTo issue that way.
You can also run your app with Throw on all exceptions
while disposing make sure no one recalls the form or its instance.
Also, if you are using the form event _FormClosing
Make sure if you have a modal to cancel the form closing, return and set e.Cancel = true; but do not set e.Cancel = true if the form is closing. And do not call this.Close() in a _FormClosing() event that you are handling yourself.
After, you may .Dispose() your stuff, but make sure no Dispose methods call the form back like invoking components, since they are being disposed or already disposed.
For people that use the hack that sets the form in an var instance to have access to it anywhere, do not dispose it, otherwise you are disposing a form already disposed.