WPF OutOfMemory Exception after displaying images a couple of times - wpf

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.

Related

wpf textbox won't throw textInput event

I have a window with a textbox that is not throwing textInput events when typing.
I have been looking at it with Snooper. Only the KeyDown and KeyUp events get thrown.
It IS responding to a few keys: Space, backspace, Insert, home, delete, end
It responds to copy & paste commands, as well as Select All
It is NOT responding to any character, symbol or number
And here's the kicker: This window is opened via a shared method that is called from two different places in the code. When called from one location the textbox works perfectly, when called from other location it doesn't.
I have ruled out binding, data converters, styles, control location.
I stripped the window down to just a single plain textbox with no bindings, and the problem persists.
I've tried all I can think of to track down this mysterious bug. I can't see what could be handling my event before the previewTextInput even gets throw, or why it could possibly be only occurring half the time.
Any ideas about the cause of this bug, or other tools I could try to trace the events would be greatly appreciated!
Edit: Adding some code to demonstrate. This has been stripped down to the barest code required, and the issue is still occurring.
<Window x:Class="EventViewEmail"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="880" Height="600">
<TextBox VerticalAlignment="Top"/>
</Window>
notice the lack of bindings, styles, or anything else that may be interfering the control
Public Class EventViewEmail
'all code removed from the view-behind'
End Class
this is the static class that builds the window. The two separate calls to this class build the arguments differently. I've removed the code that uses the arguments to show that they aren't affecting the issue at hand.
Public Class EventManager
Public Shared Sub Show(e As EventEdit, p As WorkplanPageViewModel)
Dim w = New EventViewEmail
If w Is Nothing Then Return
'removed datacontext for testing'
'w.DataContext = e '
'w.Tag = p'
w.Show()
End Sub
End Class
The only other thing I can add is that the code calling the Show() sub are coming from two separate solutions. Not sure what impact that could possibly have after I've removed all the arguments
Edit 2:
I have been trying to trace the events sequence to narrow down where the event is getting handled. So far I can see that between the keyDown and keyUp events, there is a sequence of events that should be happening that isn't:
PreviewInputReport / InputReport (no source)
PreviewTextInputStart / TextInputStart (textbox)
PreviewTextInput / TextInput (textbox)
PreviewInputReport / InputReport (textboxView)
the keydown event isn't being handled, so I'm not sure why the PreviewInputReport is not getting fired
Disclaimer
I'll post C# code because I'm not fluent enough in VB to write code (only to read it in a limited scope). I'll try to translate it if I find some time, or anyone of you can feel free to do it.
I have managed to reproduce the behavior you described by handling the PreviewTextInput event up the visual tree on the window (remember it's a tunneling routed event):
<Window (...)>
<StackPanel>
<TextBox x:Name="myTextBox"></TextBox>
</StackPanel>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
}
}
So in my opinion it's a strong suspect for the cause of your problems. It also explains the comment to #AQuirky's answer that your PreviewTextInput handler is not called. You can confirm that that's the case by subscribing differently (the handler is called even if the event was marked as handled):
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
myTextBox.AddHandler(
routedEvent: PreviewTextInputEvent,
handler: new TextCompositionEventHandler((s, e) =>
{
if (e.Handled)
throw new Exception("Gotcha!");
}),
handledEventsToo: true);
}
}
If this turns out to be correct diagnosis, then there's one thing that's somewhat mysterious - who and where does handle the PreviewTextInput event? I guess that's for you to investigate...
This comment from #l33t might come in handy:
Look for global event handlers like EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...) and/or behaviors. Are you using any third-party UI library? Look in their code as well.
Update
Since it seems like PreviewTextInput is not the culprit after all, here's what I'd do next.
If I'm not mistaken, there's a whole chain of events being fired prior to TextInputEvent, and I believe handling any of those can break the chain. It also looks like InputManager is responsible for managing this event cycle (you can inspect its C# source code here).
That being said I suggest doing the following:
Using InputManager.Current subscribe to its PreProcessInput and/or PostProcessInput events (optionally also PreNotifyInput and PostNotifyInput)
Record the events chain in the working scenario (especially inspect the args' StagingItem.Input.RoutedEvent, which holds the routed event currently being processed)
Repeat the procedure for the not working scenario
Pinpoint the first difference - the first routed event being processed in the working scenario, but not being processed in the not working scenario
Investigate both last common routed event and the first one that's different - perhaps one of those is handled in your code?
In this case it turned out that the form that launched the window in the not working case was a winform. The winform was blocking the keystrokes. This was fixed by opening the window as a modal.
I had the same problem here. The difference between the two calls was: one time the WPF-Windows was called modal (works) and one time nonmodal (doesnt work), everytime from a WindowsForms Window.
As Shaboboo wrote in his anwer, this is the difference, but what, if you need to call nonmodal.
The answer is given this thread. (stupid me, I had the same problem 2 years ago, asked and got an answer). We have to use
ElementHost.EnableModelessKeyboardInterop(wpfWindow);
Thanks again to V.Leon
The problem is that while the TextInput is being fired (not thrown...exceptions are thrown) you are not seeing it because the text box itself is using it. To catch this event you need to use the PreviewTextInput. So for example
<TextBox TextInput="UIElement_OnTextInput" PreviewTextInput="UIElement_OnPreviewTextInput"></TextBox>
With event handlers...
private void UIElement_OnTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In text input event, {e.Text}");
}
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In preview text input event, {e.Text}");
}
The first handler is never called, the second one always.
Your handler is not called because other handlers have marked the event as handled. You can add an handler with AddHandler() and pass true for the third argument:
public MainWindow()
{
var handler = new RoutedEventHandler(OnTextInput);
myTextBox.AddHandler(TextInputEvent, handler, handledEventsToo: true);
}
private static void OnTextInput(object sender, RoutedEventArgs e)
{
}

ShowDialog() behind the parent window

I am using ShowDialog() with WindowStyle = WindowStyle.SingleBorderWindow; to open a modal window in my WPF (MVVM) application, but it lets me navigate to parent window using the Windows taskbar (Windows 7).
I've found an answer here: WPF and ShowDialog() but it isn't suitable for me because I don't need an "always on top" tool window.
Thanks in advance
Try setting the Owner property of the dialog. That should work.
Window dialog = new Window();
dialog.Owner = mainWindow;
dialog.ShowDialog();
Edit:
I had a similar problem using this with MVVM. You can solve this by using delegates.
public class MainWindowViewModel
{
public delegate void ShowDialogDelegate(string message);
public ShowDialogDelegate ShowDialogCallback;
public void Action()
{
// here you want to show the dialog
ShowDialogDelegate callback = ShowDialogCallback;
if(callback != null)
{
callback("Message");
}
}
}
public class MainWindow
{
public MainWindow()
{
// initialize the ViewModel
MainWindowViewModel viewModel = new MainWindowViewModel();
viewModel.ShowDialogCallback += ShowDialog;
DataContext = viewModel;
}
private void ShowDialog(string message)
{
// show the dialog
}
}
I had this problem but as the Window was being opened from a view model I didn't have a reference to the current window. To get round it I used this code:
var myWindow = new MyWindowType();
myWindow.Owner = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
You can use: myWindow.Owner = Application.Current.MainWindow;
However, this method causes problems if you have three windows open like this:
MainWindow
|
-----> ChildWindow1
|
-----> ChildWindow2
Then setting ChildWindow2.Owner = Application.Current.MainWindow will set the owner of the window to be its grandparent window, not parent window.
When the parent window makes (and shows) the child window, that is where you need to set the owner.
public partial class MainWindow : Window
{
private void openChild()
{
ChildWindow child = new ChildWindow ();
child.Owner = this; // "this" is the parent
child.ShowDialog();
}
}
Aditionally, if you don't want an extra taskbar for all the children... then
<Window x:Class="ChildWindow"
ShowInTaskbar="False" >
</Window>
Much of the reason for the MVVM pattern is so that your interaction logic can be unit tested. For this reason, you should never directly open a window from the ViewModel, or you'll have dialogs popping up in the middle of your unit tests.
Instead, you should raise an event that the View will handle and open a dialog for you. For example, see this article on Interaction Requests: https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx#sec12
The problem seems to be related to Window.Owner, and indeed if you judge by previous knowledge that you might have of the Win32 API and WinForms, a missing owner would be the typical cause of such a problem, but as many have pointed out, in the case of WPF that's not it. Microsoft keeps changing things to keep things interesting.
In WPF you can have a dialog with a specific owner and you can still have the dialog appear in the taskbar. Because why not. And that's the default behavior. Because why not. Their rationale is that modal dialogs are not kosher anymore, so you should not be using them; you should be using modeless dialogs, which make sense to show as separate taskbar icons, and in any case the user can then decide whether they want to see different app windows as separate icons, or whether they want to see them grouped.
So, they are trying to enforce this policy with complete disregard to anyone who might want to go against their guidelines and create a modal dialog. So, they force you to explicitly state that you do not want a taskbar icon to appear for your dialog.
To fix this problem, do the following in the constructor of your view class:
ShowInTaskbar = false;
(This may happen right after InitializeComponent();
This is equivalent to Xcalibur37's answer, though the way I figure things, since WPF forces you to have both a .cs file and a .xaml file, you might as well put things that are unlikely to change in the .cs file.
Add "ShowInTaskbar" and set it to false.
Even if this post is a bit old, I hope it is OK that I post my solution.
All the above results are known to me and did not exactly yield the desired result.
I am doing it for the other googlers :)
Lets say f2 is your window that you want to display on top of f1 :
f2.Owner = Window.GetWindow(this);
f2.ShowDialog();
That's it , I promise it will not disappear !
HTH
Guy

wpf BackgroundWorker - Regarding updating UI

I use a browse for files dialog to allow a user to select multiple images. If a lot of images are selected, as expected it takes a bit. Below is an example of what I do with the selected images. I loop through the filepaths to images and create an instance of a user control, the user control has an Image control and a few other controls. I create the instance of this control then add it to a existing stackPanel created in the associating window xaml file. The example just below works fine, but I'm trying to understand BackGroundWorker better, I get the basics of how to set it up, with it's events, and pass back a value that could update a progress bar, but because my loop that takes up time below adds the usercontrol instance to an existing stackPanel, It won't work, being in a different thread. Is BackGroundWorker something that would work for an example like this? If so, what's the best way to update the ui (my stackpanel) that is outside the thread. I'm fairly new to wpf and have never used the BackGroundWorker besides testing having it just update progress with a int value, so I hope this question makes sense, if I'm way off target just let me know. Thanks for any thoughts.
Example of how I'm doing it now, which does work fine.
protected void myMethod(string[] fileNames) {
MyUserControl uc;
foreach (String imagePath in fileNames) {
uc = new MyUserControl();
uc.setImage(imagePath);
stackPanel.Children.Add(uc);
progressBar.Value = ++counter;
progressBar.Refresh();
}
}
below this class i have this so I can have the progressBar refresh:
public static class extensionRefresh {
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement) {
uiElement.Dispatcher.Invoke(DispatcherPriority.Background, EmptyDelegate);
}
}
Check out this article on
Building more responsive apps with the Dispatcher
Now that you have a sense of how the Dispatcher works, you might be surprised to know that you will not find use for it in most cases. In Windows Forms 2.0, Microsoft introduced a class for non-UI thread handling to simplify the development model for user interface developers. This class is called the BackgroundWorker
In WPF, this model is extended with a DispatcherSynchronizationContext class. By using BackgroundWorker, the Dispatcher is being employed automatically to invoke cross-thread method calls. The good news is that since you are probably already familiar with this common pattern, you can continue using BackgroundWorker in your new WPF projects
Basically the approach is
BackgroundWorker _backgroundWorker = new BackgroundWorker();
// Set up the Background Worker Events
_backgroundWorker.DoWork += _backgroundWorker_DoWork;
_backgroundWorker.RunWorkerCompleted += _backgroundWorker_RunWorkerCompleted;
// Run the Background Worker
_backgroundWorker.RunWorkerAsync(5000);
// Worker Method
void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Do something
}
// Completed Method
void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Doing UI stuff
if (e.Cancelled)
{
statusText.Text = "Cancelled";
}
else if (e.Error != null)
{
statusText.Text = "Exception Thrown";
}
else
{
statusText.Text = "Completed";
}
}
Using a BackgroundWorker alone won't solve your issue since elements created during the DoWork portion will still have originated from a non-UI thread. You must call Freeze on any objects you intend to use on another thread. However only certain UI objects will be freezable. You may have to load in the images as BitmapImages on the background thread, then create the rest of your user control on the UI thread. This may still accomplish your goals, since loading in the image is probably the most heavyweight operation.
Just remember to set BitmapImage.CacheOption to OnLoad, so it actually loads up the image when you create the object rather than waiting until it needs to be displayed.

WPF event that fires after element is fully parented, but before it's arranged

I'm writing a WPF control that dynamically changes its contents depending on what types of Window/UserControl descendants are in its parentage list (part of an experiment with convention vs. configuration).
As such, I need some code to run after my control is fully parented (i.e. all the way up to the Window that's being shown). Ideally, I'd also like my code to run before the first Measure/Arrange pass, since my code is going to change the control's contents and force another Measure/Arrange pass.
I've looked at EndInit, but it fires after the control is loaded from XAML, at which time it might not be fully parented. (For example, if my control was on a UserControl, then EndInit will fire once the UserControl is loaded -- but before it's parented to anything else. I want to wait until the UserControl is parented to something, and that's parented to something else, all the way up.)
Currently I'm just hooking the Loaded event from my control's constructor, and running my code there (oddly enough, WPF doesn't have an OnLoaded method to override):
public class MyControl
{
public MyControl()
{
Loaded += (sender, e) => { ... };
}
}
This works -- it fires when the parents are fully populated -- but it's slightly less than optimal, because there's a Measure/Arrange pass that happens before Loaded.
Is there a good place I can put my code so that it runs after the Parents are set all the way up, but before the first Measure/Arrange pass?
Extra coolness points for solutions that would also work in Silverlight, ElementHost, the Blend/VS designer, and VisualBrush (i.e., not assuming that the top-level parent is a Window, or in the case of VisualBrush, not assuming that there even is a parent -- just that it's as parented as it's gonna be before showing up on the screen, or being sent to the printer, or whatever).
I believe the parents are all set in a single dispatcher operation, so you should be able to get that behavior by putting your logic in a delegate and queuing it up as the next dispatcher operation after the parent is set:
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
this.Dispatcher.BeginInvoke(new Action(OnReady));
}
private void OnReady()
{
// Element should be fully parented here
}
You could also do that from EndInit rather than OnVisualParentChanged if you want to handle the case of no parent, although EndInit appears to be called more than once so you will need to check for duplicates:
private bool readyQueued;
public override void EndInit()
{
base.EndInit();
if (!readyQueued)
{
this.Dispatcher.BeginInvoke(new Action(OnReady));
readyQueued = true;
}
}
private void OnReady()
{
readyQueued = false;
// Element should be fully parented here
}

HowTo write a recursive Custom Control in WinForms .NET

I am attempting to write a 'User Control' in WinForms .NET (not ASP.NET). The control is relatively simple. It will contain a label, a button, and a DataGridView.
However, the control needs to be able to instantiate itself, i.e. when the user clicks the button (of the parent control) at least 1 nested (children) control of the same type will be displayed underneath (kind of like a Tree)
I am having no success writing such a recursive control using a 'User Control'. A StackOverflow Exception occurs when instantiating MyControl within it's own constructor.
Therefore, I am leaning towards using a 'Custom Control', hoping it can handle the instantiation of itself (maybe in the Paint event??). Alot more work has to go into a Custom Control however, so I don't want to go down this path if it's going to take forever. I am on a tight deadline.
Anybody done this using a Custom Control or have any solid ideas on how to create a recursive control?
By the way, this control would be used in a fairly finite number of recursive combinations, so maybe it would be better to create a separate control for each parent/children scenario? I am thinking that would result in at least 10 separate user controls.
thanks for your help
UPDATE (here is my initial attempt at a stop condition per your feedback, but this is still causing children to be created indefinitely) :
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX()
{
InitializeComponent();
Recurse(0);
}
private void Recurse(int childCount)
{
if (childCount
The problem is probably that the child control also instantiates a child control. There has to be a stop condition or controls will be generated until the stack overflows.
This should work:
public partial class CustomX : UserControl
{
private IList _children = new List();
public CustomX(int depth)
{
InitializeComponent();
if(depth > 0)
{
CustomX child = new CustomX(depth-1);
this.Controls.Add(child)
}
}
}
You should have no problem doing this with a user control. It is more likely an issue with not terminating the recursion properly. It (might) be more readable to perform the control creation in just the topmost parent control rather than delegating that task into each constructor.
Can you post the code you have in your constructor?

Resources