A memory leak issue with WPF Command Binding
Background
In our application, we have a screen which hosts several tabs. In each tab, it contains a third-party GridControl (like the WPF standard GridView control). And we need to display some column cells as hyper link, so user can click on it, view more details. So we have customized the Cell template as the following:
It is straightforward. Only one place need to note is we have binding the Button::Command to a static command.
At the beginning, the GridClickCommand is defining as a RoutedUICommand.
This is fine, until we have migrated the third party, and find the application is slow down after open the screens several times. The more times it opened the worse of the performance.
After struggled several weeks, we located the root problem is we misused the RoutedUICommand. We still have no idea why the problem didn’t happen before migrate the third party. But we can explain why the whole application performance slows down after open the screen.
Reason for the Performance Issue:
As I mentioned above, there are several hyper link cells in each tab, which binding to the RoutedUICommand. Let’s look down how RoutedCommand (the base class of RoutedUICommand) implements the CanExecuteChanged method
The purpose of above implementation is obviously, the command binding source (aka Button in this case) need update its state, which depends on the Command’s CanExecute method. When the Command’s CanExecute method returns True, the button displays in Enable state, otherwise in Disable state. The problem is who will notice the binding source that the CanExecute value has been changed. One possible solution is by ourselves, when we know the CanExecute value has been changed, we explicitly raise the CanExecuteChanged event, so the button updates its state accordingly.
However the disadvantage of above approach is obviously, you need to raise the CanExecuteChanged event in every place that will affect the CanExecute value. So the common approach in WPF is, we delegate it to CommandManager, that the reason why we register the event handler to CommandManager.RequerySuggested.
With the CommandManager, WPF can automatically ask all of the commands being used in your UI if they can execute. This happens at various times, such as when input focus shifts to another control, an item is selected in a list, etc. Refer more detail about Command and CommandManager from here.
Let’s come back to the reason why RoutedUICommand slows down the performance. It’s simple, because it is a RoutedCommand, which need to route through the element tree to determine whether it should return True or False for its CanExecute method. So it’s very expensive. That’s the reason why the more the screen opened, the worse performance it is. Because, as the number of bindinged RoutedUICommand increase, the CommandManager takes more time to execute its update status logic. And even with your simple click, the logic could be execute, so it affects the whole application’s performance.
Solution for the Performance Issue:
In our scenario, there is totally no need to update status for button; it should be always kept in enable state. Not sure why we use the RoutedUICommand before. The fix is simple; we replace it with customized DelegateCommand, with the following implement.
Memory Leak Issue
Several days later, we notice there is a memory leak issue during doing the release check out. By leverage WinDbg, we could see the leak is related to above change. The GridClickCommand command is static, and its field CanExecuteChanged property keeps reference to event handlers, and indirectly reference to the binding source (the button), which caused the whole virtual tree of the screen could not be disposed.
Solution for the Memory Leak Issue
After google, I found this useful ticket, and resolved the problem with the following solution.
The key point is implementing a dumb CanExecuteChanged, to avoid the binding source hock on this event. In our scenario, it is fine, since the command is always executable, no need to hock the CanExecuteChanged event, no need to raise this event.
More Explanation
Firstly, let’s check why the DelegateCommand causes the memory leak. As some guys said, there is no secret behind the source code. We could get the answer from the .Net Framework source code. For the button, in order to update its state correctly, it needs to hock up the command’s CanExecuteChanged event, and waiting for the event raised. So when you assign a command to button in XAML, it will automatically hock it for us.
Secondly, let’s check why no such problem with RoutedUICommand approaches. The key point is it uses weak reference during hock the command’s CanExecuteChanged event.
RoutedCommand:
public event EventHandler CanExecuteChanged
{
add{ CommandManager.RequerySuggested += value;}
remove {CommandManager.RequerySuggested -= value;}
} CommandManager:
public static event EventHandler RequerySuggested
{
add { CommandManager.AddWeakReferenceHandler(ref CommandManager.Current._requerySuggestedHandlers, value);}
remove { CommandManager.RemoveWeakReferenceHandler(CommandManager.Current._requerySuggestedHandlers, value); }
}
A memory leak issue with WPF Command Binding的更多相关文章
- WPF Command Binding
<Window x:Class="WpfTest.MainWindow" xmlns="http://schemas.microsoft.com/winfx/200 ...
- SilverLight - Memory Leak
There is a memory leak issue in current silverlight project. It occurs in the search function: the m ...
- WPF WebBrowser Memory Leak 问题及临时解决方法
首先介绍一下内存泄漏(Memory Leak)的概念,内存泄露是指程序中已动态分配的堆内存由于某种原因未释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果. 最近在使用W ...
- silverlight wpf DataTemplate Command binding
<Grid x:Name="LayoutRoot" Background="White"> <CommonControl:NoapDataGr ...
- WPF MVVM,Prism,Command Binding
1.添加引用Microsoft.Practices.Prism.Mvvm.dll,Microsoft.Practices.Prism.SharedInterfaces.dll: 2.新建文件夹,Vie ...
- Memory Leak Detection in C++
原文链接:http://www.linuxjournal.com/article/6556?page=0,0 An earlier article [“Memory Leak Detection in ...
- 山东省第七届ACM省赛------Memory Leak
Memory Leak Time Limit: 2000MS Memory limit: 131072K 题目描述 Memory Leak is a well-known kind of bug in ...
- 一则JVM memory leak解决的过程
起因是我们的集群应用(3台机器)新版本测试过程中,一般的JVM内存占用 都在1G左右, 但在运行了一段时间后,慢慢升到了4G, 这是一个明显不正常的现象. 定位 过程: 1.先在该机器上按照步骤尝试重 ...
- MVVM Command Binding: InvokeCommandAction v.s. EventToCommand
This gives you the ability to create a trigger on an event and bind it to an ICommand on the view mo ...
随机推荐
- Python 2.7_First_try_爬取阳光电影网_20161206
之前看过用Scrapy 框架建立项目爬取 网页解析时候用的Xpath进行解析的网页元素 这次尝试用select方法匹配元素 1.入口爬取页面 http://www.ygdy8.com/index.ht ...
- 随笔—邀请赛前训— Codeforces Round #330 (Div. 2) Vitaly and Night
题意:给你很多对数,要么是0要么是1.不全0则ans++. 思路即题意. #include<cstdio> #include<cstring> #include<iost ...
- 《深入理解Nginx》阅读与实践(一):Nginx安装配置与HelloWorld
最近在读陶辉的<深入理解Nginx:模块开发与架构解析>,一是想跟着大牛练练阅读和编写开源代码的能力,二是想学学Nginx优秀的架构设计,三是想找一个点深入下Linux下网络编程的细节.侯 ...
- PS 制作复印件及盖章效果
对要处理的部分选定 1.执行 滤镜--杂色--添加杂色 2.执行 滤镜--模糊--高斯模糊 3.ctrl+L 执行 色阶 调整为 满意的效果 4.最后添加想要的颜色 图像--调整--渐变映射 关键 ...
- A 浪哥的烦恼 完全背包dp
https://biancheng.love/contest-ng/index.html#/131/problems 首先,去到n点的最小时间是所有数加起来. 然后,如果我1 --- 2,然后再2-- ...
- Selenium2+python自动化26-js处理内嵌div滚动条
前言 前面有篇专门用js解决了浏览器滚动条的问题,生活总是多姿多彩,有的滚动条就在页面上,这时候又得仰仗js大哥来解决啦. 一.内嵌滚动条 1.下面这张图就是内嵌div带有滚动条的样子,记住它的长相.
- CSS3动画与2D、3D转换
一.过度动画:transition 五个属性: transition-property css 样式属性名称 transition-duration 动画持续时间(需要单位s) transition- ...
- error C2065: 'INVALID_SET_FILE_POINTER' : undeclared identifier
Searching MSDN for that constant brings up one result: it's a failure code for SetFilePointer() and ...
- Ubuntu 安装snmp报Unlinked OID in IPATM-IPMC-MIB: marsMIB ::= { mib-2 57 }错误
首先运行下面的脚本(脚本摘自:http://www.th7.cn/system/lin/201304/38800.shtml) #!/bin/bashfor i in /usr/share/mibs/ ...
- expdp报错ora 39126
11.2.0.2,expdp报错: ORA-39126: Worker unexpected fatal error in KUPW$WORKER.GET_TABLE_DATA_OBJECTS []O ...