WPF not releasing memory - wpf

I have a issue in our application where the memory is not released when the user controls
are unloaded. This will increase the memory starting from 40MB and ends up with 200MB and more.
To simulate this,
I created a wpf project which has the main window and a user control
loaded 1000 objects into a wpf datagrid which is placed in a user control
A scroll viewer is put in the main window
The user control is loaded inside this scroll viewer Once the show button is clicked
The user control is removed from the Content of the Scroll viwer once the Close button is clicked
Once i checked with the task manager, before the 1000 objects are loaded to the grid, the memory consumption is 14MB. Once its loaded by clicking on the show button it increases to 70MB. But when i click on Close button to remove the user control from the window, the memory reduces to 67MB only. Shouldn't it reduce to 14BMB or something close to that??
When i checked this with the ANTS memory profiler, it shows that the 1000 objects remain in memory even after the User control is removed from the Window. Shouldn't the garbage collector release these objects when the user control is removed from the window (once the Scroll viewer Content Property is set to null)?
Following is the Code i used for this. I didn't use any styles, data templates or any third party controls, only used the WPF DataGrid control to load the data.
The User Control Code Behind
public partial class UserControl1 : UserControl,IDisposable
{
List<TestClass> list = null;
public UserControl1()
{
InitializeComponent();
}
public void Dispose()
{
BindingOperations.ClearBinding(dgList, DataGrid.ItemsSourceProperty);
list.Clear();
GC.Collect();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
list = new List<TestClass>();
for (int i = 0; i < 1000; i++)
{
TestClass obj = new TestClass();
obj.Name = "test name";
obj.Age = 34;
list.Add(obj);
}
dgList.ItemsSource = list;
}
}
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
Main Window Code behind
public partial class MainWindow : Window
{
UserControl1 control = null;
public MainWindow()
{
InitializeComponent();
}
private void btnClose_Click(object sender, RoutedEventArgs e)
{
control.Dispose();
scwContent.Content = null;
control = null;
}
private void btnShow_Click(object sender, RoutedEventArgs e)
{
control = new UserControl1();
scwContent.Content = control;
}
}
Thanks.

The Garbage collector only collects when memory is needed, not when references are set to null.
(Only exception: calling GC.Collect())

Why are you trying to call GC? That is not required
Change
for (int i = 0; i < 1000; i++)
{
TestClass obj = new TestClass();
...
To
TestClass obj;
for (int i = 0; i < 1000; i++)
{
obj = new TestClass();
...
You can set the list object to null ..
Read this for understanding setting objects to null
Setting an object to null vs Dispose()

Related

WPF Winforms Interop eating keystroke

I create a winform project with a single form with 4 textboxes and a button.
On button click, I perform the following:
Window1 w = new Window1();
ElementHost.EnableModelessKeyboardInterop(w);
w.Show();
Where window 1 is a Wpf window. Window1 has a single button on it and when that button is clicked the following occurs:
System.Windows.MessageBox.Show("HelloWOrld");
When you run the application the WinForm Form pops ups. If you hit tab it cycles through the 4 textboxes no problem. Then Click the button to open the WPF window. Click that button and popup the messagebox. Leave them open and then go back to the WinForm form you can no longer tab through the fields but you can type other characters. It appears as though the textboxes get the keystrokes but the form doesn't get them. I also get a system beep as though the model was getting the keystroke.
EDIT 9/9/2014 3:44PM
Hans responded in the comments and was correct. I tried describing a simpler case that would be easier for other people to reproduce that gave use the same symptoms. Our actual problem is that we have created a window base class that supports modal to parent capabilities. Here is the relevant code for our BaseWindow
public class BaseWindow: Window
{
[DllImport("user32.dll")]
static extern bool EnableWindow(IntPtr hWnd, bool bEnable);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
public void ShowModalToParent(Window frmParent, Action<bool?> callback = null)
{
IntPtr myHandle = (new System.Windows.Interop.WindowInteropHelper(this)).Handle;
EnableWindow(myHandle,
SetForegroundWindow(myHandle);
this.Closing += Window_Closing;
ShowInTaskbar = false;
Owner = frmParent; // Keep on top of parent
ClosedCallBack += callback ?? (p => { _modalDialogResult = p; });
var parentHandle = (new System.Windows.Interop.WindowInteropHelper(frmParent)).Handle;
EnableWindow(parentHandle, false); // Prevent events for parent
new ShowAndWaitHelper(this).ShowAndWait();
}
internal class ShowAndWaitHelper
{
private readonly Window _window;
private DispatcherFrame _dispatcherFrame;
internal ShowAndWaitHelper(Window window)
{
if (window == null)
{
throw new ArgumentNullException("panel");
}
this._window = window;
}
internal void ShowAndWait()
{
if (this._dispatcherFrame != null)
{
throw new InvalidOperationException("Cannot call ShowAndWait while waiting for a previous call to ShowAndWait to return.");
}
this._window.Closed += new EventHandler(this.OnPanelClosed);
_window.Show();
this._dispatcherFrame = new DispatcherFrame();
Dispatcher.PushFrame(this._dispatcherFrame);
}
private void OnPanelClosed(object source, EventArgs eventArgs)
{
if (this._dispatcherFrame == null)
{
return;
}
this._window.Closed -= new EventHandler(this.OnPanelClosed);
this._dispatcherFrame.Continue = false;
this._dispatcherFrame = null;
}
}
}
I'm sure this code was taken from a Blog/Forum post of some sort but am unable to find any reference to it in code. We want to keep the modal to parent but some how address the odd key press issue. To reproduce the issue replace the button_click in Window1 to call ShowModalToParent on a window that uses this as a base class.

Custom Message Box Advice

Well I'm using a Window as my custom message box with a couple of controls which are displayed/populated with text depending on which constructor is called.
I have a defined event, which is subscribed to via the original class, this fires once the button has been clicked.
However I can't see how to use this effectively, preferably I'd like to return a bool whether Yes or No was clicked, however obviously my code will carry on executing, hence the method which is subscibed to the button click. Below is some example code to make the issue clearer.
Message Box Window
public partial class CustomMessageBox : Window
{
public delegate void MessageBoxHandler(object sender, EventArgs e);
public event MessageBoxHandler MessageBoxEvent;
public CustomMessageBox()
{
InitializeComponent();
}
public CustomMessageBox(string message)
{
InitializeComponent();
this.txtdescription.Text = message;
}
public CustomMessageBox(string message, string title, string firstBtnText)
{
InitializeComponent();
this.lbltitle.Content = title;
this.txtdescription.Text = message;
this.btnstart.Content = firstBtnText;
}
}
public static class MessageBoxButtonClick
{
public static bool Yes { get; set; }
public static bool No { get; set; }
public static bool Cancel { get; set; }
}
Window Which Instantiates the MessageBox Window
private void StartProcess_Click(object sender, System.Windows.RoutedEventArgs e)
{
foreach (var result in results)
{
if(result.ToBeProcessed)
_validResults.Add(new ToBeProcessed(result.Uri, result.Links));
}
_msgbox = new CustomMessageBox("Each Uri's backlinks will now be collected from Yahoo and filtered, finally each link will be visited and parsed. The operation is undertaken in this manner to avoid temporary IP Blocks from Yahoo's servers.", "Just a FYI", "OK");
_msgbox.MessageBoxEvent += (MessageBoxHandler);
if (_msgBoxProceed)
{
_msgbox.Close();
Yahoo yahoo = new Yahoo();
yahoo.Status.Sending += (StatusChange);
//What I'd like to happen here is the code simply stop, like it does when calling a messagebox is winforms
//e.g.
// if(ProceedClicked == true)
// do stuff
// yahoo.ScrapeYahoo(_validResults[Cycle].Uri, _validResults[Cycle].LinkNumber);
//Cycle++;
}
else
{
_msgbox.Close();
}
}
private void MessageBoxHandler(object sender, EventArgs e)
{
if (MessageBoxButtonClick.Yes)
{
ProceedClicked = true;
}
else
{
ProceedClicked = false;
}
}
Hopefully that makes it clear enough, I can't put any execution code ie call a certain method due to using it multiple times throughout my application.
Very hard to understand what the problem exactly is. Also the code you wrote here, doesn't seemt to have any calls, that would actually show the CustomMessageBoxWindow.
But I'll take a stab at this...
First of all, am I right in guessing that in your main Window you want your code to wait at if(_msgBoxProceed) until the user actually presses a button in your CustomMessageBoxWindow (currently it just shows the message box and continues executing the next statements)?
If so then I'm guessing you are showing your message box window with the Show() method. Use ShowDialog() instead. That will cause code execution to stop, until the message box gets closed.
If you don't want to use a modal dialog then there are two options. Either use thread syncrhonization objects (eg AutoResetEvent) or set up a new event for when the message box closes and continue your code execution in the closed event handler (in StartProcess_Click the last line would be a call to _msgBox.Show() and everything from if(_msgBoxProceed) would be in the closed event handler).

How to draw connecting lines between two controls on a grid WPF

I am creating controls (say button) on a grid. I want to create a connecting line between controls.
Say you you do mousedown on one button and release mouse over another button. This should draw a line between these two buttons.
Can some one help me or give me some ideas on how to do this?
Thanks in advance!
I'm doing something similar; here's a quick summary of what I did:
Drag & Drop
For handling the drag-and-drop between controls there's quite a bit of literature on the web (just search WPF drag-and-drop). The default drag-and-drop implementation is overly complex, IMO, and we ended up using some attached DPs to make it easier (similar to these). Basically, you want a drag method that looks something like this:
private void onMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
UIElement element = sender as UIElement;
if (element == null)
return;
DragDrop.DoDragDrop(element, new DataObject(this), DragDropEffects.Move);
}
On the target, set AllowDrop to true, then add an event to Drop:
private void onDrop(object sender, DragEventArgs args)
{
FrameworkElement elem = sender as FrameworkElement;
if (null == elem)
return;
IDataObject data = args.Data;
if (!data.GetDataPresent(typeof(GraphNode))
return;
GraphNode node = data.GetData(typeof(GraphNode)) as GraphNode;
if(null == node)
return;
// ----- Actually do your stuff here -----
}
Drawing the Line
Now for the tricky part! Each control exposes an AnchorPoint DependencyProperty. When the LayoutUpdated event is raised (i.e. when the control moves/resizes/etc), the control recalculates its AnchorPoint. When a connecting line is added, it binds to the DependencyProperties of both the source and destination's AnchorPoints. [EDIT: As Ray Burns pointed out in the comments the Canvas and grid just need to be in the same place; they don't need to be int the same hierarchy (though they may be)]
For updating the position DP:
private void onLayoutUpdated(object sender, EventArgs e)
{
Size size = RenderSize;
Point ofs = new Point(size.Width / 2, isInput ? 0 : size.Height);
AnchorPoint = TransformToVisual(node.canvas).Transform(ofs);
}
For creating the line class (can be done in XAML, too):
public sealed class GraphEdge : UserControl
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
public Point Source { get { return (Point) this.GetValue(SourceProperty); } set { this.SetValue(SourceProperty, value); } }
public static readonly DependencyProperty DestinationProperty = DependencyProperty.Register("Destination", typeof(Point), typeof(GraphEdge), new FrameworkPropertyMetadata(default(Point)));
public Point Destination { get { return (Point) this.GetValue(DestinationProperty); } set { this.SetValue(DestinationProperty, value); } }
public GraphEdge()
{
LineSegment segment = new LineSegment(default(Point), true);
PathFigure figure = new PathFigure(default(Point), new[] { segment }, false);
PathGeometry geometry = new PathGeometry(new[] { figure });
BindingBase sourceBinding = new Binding {Source = this, Path = new PropertyPath(SourceProperty)};
BindingBase destinationBinding = new Binding { Source = this, Path = new PropertyPath(DestinationProperty) };
BindingOperations.SetBinding(figure, PathFigure.StartPointProperty, sourceBinding);
BindingOperations.SetBinding(segment, LineSegment.PointProperty, destinationBinding);
Content = new Path
{
Data = geometry,
StrokeThickness = 5,
Stroke = Brushes.White,
MinWidth = 1,
MinHeight = 1
};
}
}
If you want to get a lot fancier, you can use a MultiValueBinding on source and destination and add a converter which creates the PathGeometry. Here's an example from GraphSharp. Using this method, you could add arrows to the end of the line, use Bezier curves to make it look more natural, route the line around other controls (though this could be harder than it sounds), etc., etc.
See also
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dd246675-bc4e-4d1f-8c04-0571ea51267b
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part1.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part2.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part3.aspx
http://www.codeproject.com/KB/WPF/WPFDiagramDesigner_Part4.aspx
http://www.syncfusion.com/products/user-interface-edition/wpf/diagram
http://www.mindscape.co.nz/products/wpfflowdiagrams/

How to create input gesture for page/window from an user control

I have a reusable usercontrol that uses a few commands and corresponding keyboard gestures,
(specifically Escape and Ctrl+1...Ctrl+9)
Now as I use this usercontrol in multiple locations I'd like to define the input gestures in the usercontrol, which works fine as long as the focus is within the usercontrol. However, I'd need it to work as long as focus is within the current page/window.
How can I do it, or do I really have to do command/input bindings on every page?
You could handle the Loaded event of the UserControl and walk up the logical tree to find the owning page/window, then you can add the bindings there.
e.g.
public partial class Bogus : UserControl
{
public Bogus()
{
Loaded += (s, e) => { HookIntoWindow(); };
InitializeComponent();
}
private void HookIntoWindow()
{
var current = this.Parent;
while (!(current is Window) && current is FrameworkElement)
{
current = ((FrameworkElement)current).Parent;
}
if (current != null)
{
var window = current as Window;
// Add input bindings
var command = new AlertCommand();
window.InputBindings.Add(new InputBinding(command, new KeyGesture(Key.D1, ModifierKeys.Control)));
}
}
}

Drift when restoring window location and size in WPF

I am using the code below to save and restore the window position and size upon restart.
I am observing an upward drift of 28 pixels everytime I execute this code!
Am I reading the wrong values, or am I restoring them incorrectly? Where is the number 28 (size of the chrome?) coming from (and how would I account for it programmatically, rather than a fixed number in code)?
Here is my code:
public partial class MainStudioWindowControl : RibbonWindow
{
public MainStudioWindowControl()
{
App.MainWindowOwner = this;
this.Loaded += new System.Windows.RoutedEventHandler(MainStudioWindowControl_Loaded);
}
void MainStudioWindowControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
System.Windows.Window mainWindow = System.Windows.Application.Current.MainWindow;
mainWindow.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
if (Studio.Properties.Settings.Default.Width > 0)
{
mainWindow.Left = Studio.Properties.Settings.Default.Left;
mainWindow.Top = Studio.Properties.Settings.Default.Top;
mainWindow.Width = Studio.Properties.Settings.Default.Width;
mainWindow.Height = Studio.Properties.Settings.Default.Height;
}
Debug.WriteLine(string.Format("Loading: Top = {0}", this.Top));
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
System.Windows.Window mainWindow = System.Windows.Application.Current.MainWindow;
Studio.Properties.Settings.Default.Left = mainWindow.Left;
Studio.Properties.Settings.Default.Top = mainWindow.Top;
Studio.Properties.Settings.Default.Width = mainWindow.Width;
Studio.Properties.Settings.Default.Height = mainWindow.Height;
Studio.Properties.Settings.Default.Save();
Debug.WriteLine(string.Format("Saving: Settings.Top = {0}", Studio.Properties.Settings.Default.Top));
}
}
Try this:
1) Derive your class from the normal Window, not the RibbonWindow - if that fixes it, it's a RibbonWindow issue.
2) Use hard-coded values to set the measurments in the Loaded handler - if that fixes it, the problem's got something to do with the settings.
With these two changes, it worked fine for me. The window appeared where it should every time.

Resources