提高大型项目 c# WPF 的性能 XAML
Improve performance in big project c# WPF XAML
我们公司有一个用C#+WPF+XAML写的产品(程序)。这对我们来说是一个相当重要的程序,我们的许多客户都安装了它。但是在切换到 Framework 4.7.2 之后,我们注意到性能明显下降。后来我们换到4.8版本了,但是程序运行还是很慢,尤其是视觉部分。
在我们的程序中,我们显示来自大量传感器(运动传感器、温度、光量等)的数据。我们不断接收我们处理的新数据,保存到 SQL Server 2014/2017 数据库,然后客户端程序可视化这些数据。
服务器部分和通信虽然复杂,但即使在功能不是很强大的计算机上也能正常工作。但是我们在客户监视器上显示数据时遇到了很大的问题。
程序结构如下:客户端在他想看数据的地方画图。例如,他有一个黑色的背景,并用线条画出了他的工厂。这个工厂里不同的地方都有传感器,他在这些地方画。然后他开始扫描并查看他绘制标签的数据结果。数据少的时候不是很明显,数据多的时候,鼠标在数据之间移动变得受限,客户端看到程序不断变慢,需要等几秒,make用鼠标移动一些,然后再次等待程序响应。如果您同时做几件事,那么程序就好像冻结了一样。不是这样,但是感觉程序现在要停止工作了。
我尝试使用 CPU 和 RAM 的调试和测量,但它实际上对我没有任何帮助。数据通常通过 Web 服务从服务器下载到客户端程序,并根据需要占用尽可能多的内存。很难以某种方式优化它。但是当我们开始向用户显示这些数据时,一切都开始变得非常糟糕。如何优化数据可视化,让用户继续从容地使用程序工作?很高兴收到任何建议。
我所做的有助于改进 DataGrid 表中的虚拟化。对用户来说好一点,但这还不够,你还需要别的东西,尤其是绘制的部分,并在不同的地方显示带有数据的标签。
根据我的经验,wpf 不适合可视化大量数据。创建一个奇特的 UI 很好,但是随着对象数量的增加,性能会急剧下降。我尝试了从缓存到冻结对象的所有方法,最后我得出结论,我只是选择了错误的技术。它没有正确使用您的 GPU。
您可以尝试转换为 UWP,它可能会有所帮助。
话虽如此,这里有一些您也可以尝试的提示:
Simplify your Visual Tree A common source of performance issues is a deep and complex layout. Keep your XAML markup as simple and shallow
as possible. When UI elements are drawn onscreen, a “layout pass” is
called twice for each element (a measure pass and an arrange pass).
The layout pass is a mathematically-intensive process—the larger the
number of children in the element, the greater the number of
calculations required.
Virtualize your ItemsControls As mentioned earlier, a complex and deep visual tree results in a larger memory footprint and slower
performance. ItemsControls usually increase performance problems with
deep visual trees because they are not virtualized. This means they
are constantly being created and destroyed for each item in the
control. Instead, use the VirtualizingStackPanel as the items host and
make use of the VirtualizingStackPanel.IsVirtualizing and set the
VirtualizationMode to Recycling in order to reuse item containers
instead of creating new ones each time.
Favor StaticResources Over DynamicResources StaticResources provide values for any XAML property attribute by looking up a reference to an
already defined resource. Lookup behavior for that resource is the
same as a compile-time lookup. DynamicResources will create a
temporary expression and defer lookup for resources until the
requested resource value is required. Lookup behavior for that
resource is the same as a run-time lookup, which imposes a performance
impact. Always use a StaticResource whenever possible.
Opacity on Brushes Instead of Elements If you use a Brush to set the Fill or Stroke of an element, it is better to set the Opacity on
the Brush rather than setting the element’s Opacity property. When you
modify an element’s Opacity property, it can cause WPF to create
temporary surfaces which results in a performance hit.
Avoid Using Run to Set Text Properties Avoid using Runs within a TextBlock as this results in a much higher performance intensive
operation. If you are using a Run to set text properties, set those
directly on the TextBlock instead.
Favor StreamGeometries over PathGeometries The StreamGeometry object is a very lightweight alternative to a PathGeometry.
StreamGeometry is optimized for handling many PathGeometry objects. It
consumes less memory and performs much better when compared to using
many PathGeometry objects.
Use Reduced Image Sizes If your app requires the display of smaller thumbnails, consider creating reduced-sized versions of your images.
By default, WPF will load and decode your image to its full size. This
can be the source of many performance problems if you are loading full
images and scaling them down to thumbnail sizes in controls such as an
ItemsControl. If possible, combine all images into a single image,
such as a film strip composed of multiple images.
Lower the BitMapScalingMode By default, WPF uses a high-quality image re-sampling algorithm that can sometimes consume system
resources which results in frame rate degradation and causes
animations to stutter. Instead, set the BitMapScalingMode to
LowQuality to switch from a “quality-optimized” algorithm to a
“speed-optimized” algorithm.
Use and Freeze Freezables A Freezable is a special type of object that has two states: unfrozen and frozen. When you freeze an object
such as a Brush or Geometry, it can no longer be modified. Freezing
objects whenever possible improves the performance of your application
and reduces its memory consumption.
Fix your Binding Errors Binding errors are the most common type of performance problem in WPF apps. Every time a binding error occurs,
your app takes a perf hit and as it tries to resolve the binding and
writes the error out to the trace log. As you can imagine, the more
binding errors you have the bigger the performance hit your app will
take. Take the time to find and fix all your binding errors. Using a
RelativeSource binding in DataTemplates is a major culprit in binding
error as the binding is usually not resolved properly until the
DataTempate has completed its initialization. Avoid using
RelativeSource.FindAncestor at all costs. Instead, define an attached
property and use property inheritance to push values down the visual
tree instead of looking up the visual tree.
Avoid Databinding to the Label.Content Property If you are using a Label to data bind to a String property, this will result in poor
performance. This is because each time the String source is updated,
the old string object is discarded, and a new String is created. If
the Content of the Label is simple text, replace it with a TextBlock
and bind to the Text property instead.
Bind ItemsControls to IList instead of IEnumerable When data binding an ItemsControl to an IEnumerable, WPF will create a wrapper
of type IList which negatively impacts performance with the
creation of a second object. Instead, bind the ItemsControl directly
to an IList to avoid the overhead of the wrapper object.
Use the NeutralResourcesLanguage Attribute Use the NeutralResourcesLanguageAttribute to tell the ResourceManager what the
neutral culture is and avoid unsuccessful satellite assembly lookups.
Load Data on Separate Threads A very common source of performance problems, UI freezes, and apps that stop responding is how you load
your data. Make sure you are asynchronously loading your data on a
separate thread as to not overload the UI thread. Loading data on the
UI thread will result in very poor performance and an overall bad
end-user experience. Multi-threading should be something every WPF
developer is using in their applications.
Beware of Memory Leaks Memory leaks are the number one cause of performance problems in most WPF applications. They are easy to have
but can be difficult to find. For example, using the
DependencyPropertyDescriptor.AddValueChanged can cause the WPF
framework to take a strong reference to the source of the event that
isn’t removed until you manually call
DependencyPropertyDescriptor.RemoveValueChanged. If your views or
behaviors rely on events being raised from an object or ViewModel
(such as INotifyPropertyChanged), subscribe to them weakly or make
sure you are manually unsubscribing. Also, if you are binding to
properties in a ViewModel which does not implement
INotifyPropertyChanged, chances are you have a memory leak.
Finally, a bonus tip. Sometimes when you have a performance problem it
can be very difficult to identify what exactly is causing the issue. I
suggest using an application performance profiler to help identify
where these performance bottlenecks are occurring in your code base.
There are a lot of profiler options available to you. Some are paid,
and some are free. The one I personally use the most is the Diagnosis
Tools built directly into Visual Studio 2019.
Blockquote
来源:https://dzone.com/articles/15-wpf-performance-tips-for-2019
我们公司有一个用C#+WPF+XAML写的产品(程序)。这对我们来说是一个相当重要的程序,我们的许多客户都安装了它。但是在切换到 Framework 4.7.2 之后,我们注意到性能明显下降。后来我们换到4.8版本了,但是程序运行还是很慢,尤其是视觉部分。
在我们的程序中,我们显示来自大量传感器(运动传感器、温度、光量等)的数据。我们不断接收我们处理的新数据,保存到 SQL Server 2014/2017 数据库,然后客户端程序可视化这些数据。
服务器部分和通信虽然复杂,但即使在功能不是很强大的计算机上也能正常工作。但是我们在客户监视器上显示数据时遇到了很大的问题。
程序结构如下:客户端在他想看数据的地方画图。例如,他有一个黑色的背景,并用线条画出了他的工厂。这个工厂里不同的地方都有传感器,他在这些地方画。然后他开始扫描并查看他绘制标签的数据结果。数据少的时候不是很明显,数据多的时候,鼠标在数据之间移动变得受限,客户端看到程序不断变慢,需要等几秒,make用鼠标移动一些,然后再次等待程序响应。如果您同时做几件事,那么程序就好像冻结了一样。不是这样,但是感觉程序现在要停止工作了。
我尝试使用 CPU 和 RAM 的调试和测量,但它实际上对我没有任何帮助。数据通常通过 Web 服务从服务器下载到客户端程序,并根据需要占用尽可能多的内存。很难以某种方式优化它。但是当我们开始向用户显示这些数据时,一切都开始变得非常糟糕。如何优化数据可视化,让用户继续从容地使用程序工作?很高兴收到任何建议。
我所做的有助于改进 DataGrid 表中的虚拟化。对用户来说好一点,但这还不够,你还需要别的东西,尤其是绘制的部分,并在不同的地方显示带有数据的标签。
根据我的经验,wpf 不适合可视化大量数据。创建一个奇特的 UI 很好,但是随着对象数量的增加,性能会急剧下降。我尝试了从缓存到冻结对象的所有方法,最后我得出结论,我只是选择了错误的技术。它没有正确使用您的 GPU。
您可以尝试转换为 UWP,它可能会有所帮助。
话虽如此,这里有一些您也可以尝试的提示:
Simplify your Visual Tree A common source of performance issues is a deep and complex layout. Keep your XAML markup as simple and shallow as possible. When UI elements are drawn onscreen, a “layout pass” is called twice for each element (a measure pass and an arrange pass). The layout pass is a mathematically-intensive process—the larger the number of children in the element, the greater the number of calculations required.
Virtualize your ItemsControls As mentioned earlier, a complex and deep visual tree results in a larger memory footprint and slower performance. ItemsControls usually increase performance problems with deep visual trees because they are not virtualized. This means they are constantly being created and destroyed for each item in the control. Instead, use the VirtualizingStackPanel as the items host and make use of the VirtualizingStackPanel.IsVirtualizing and set the VirtualizationMode to Recycling in order to reuse item containers instead of creating new ones each time.
Favor StaticResources Over DynamicResources StaticResources provide values for any XAML property attribute by looking up a reference to an already defined resource. Lookup behavior for that resource is the same as a compile-time lookup. DynamicResources will create a temporary expression and defer lookup for resources until the requested resource value is required. Lookup behavior for that resource is the same as a run-time lookup, which imposes a performance impact. Always use a StaticResource whenever possible.
Opacity on Brushes Instead of Elements If you use a Brush to set the Fill or Stroke of an element, it is better to set the Opacity on the Brush rather than setting the element’s Opacity property. When you modify an element’s Opacity property, it can cause WPF to create temporary surfaces which results in a performance hit.
Avoid Using Run to Set Text Properties Avoid using Runs within a TextBlock as this results in a much higher performance intensive operation. If you are using a Run to set text properties, set those directly on the TextBlock instead.
Favor StreamGeometries over PathGeometries The StreamGeometry object is a very lightweight alternative to a PathGeometry. StreamGeometry is optimized for handling many PathGeometry objects. It consumes less memory and performs much better when compared to using many PathGeometry objects.
Use Reduced Image Sizes If your app requires the display of smaller thumbnails, consider creating reduced-sized versions of your images. By default, WPF will load and decode your image to its full size. This can be the source of many performance problems if you are loading full images and scaling them down to thumbnail sizes in controls such as an ItemsControl. If possible, combine all images into a single image, such as a film strip composed of multiple images.
Lower the BitMapScalingMode By default, WPF uses a high-quality image re-sampling algorithm that can sometimes consume system resources which results in frame rate degradation and causes animations to stutter. Instead, set the BitMapScalingMode to LowQuality to switch from a “quality-optimized” algorithm to a “speed-optimized” algorithm.
Use and Freeze Freezables A Freezable is a special type of object that has two states: unfrozen and frozen. When you freeze an object such as a Brush or Geometry, it can no longer be modified. Freezing objects whenever possible improves the performance of your application and reduces its memory consumption.
Fix your Binding Errors Binding errors are the most common type of performance problem in WPF apps. Every time a binding error occurs, your app takes a perf hit and as it tries to resolve the binding and writes the error out to the trace log. As you can imagine, the more binding errors you have the bigger the performance hit your app will take. Take the time to find and fix all your binding errors. Using a RelativeSource binding in DataTemplates is a major culprit in binding error as the binding is usually not resolved properly until the DataTempate has completed its initialization. Avoid using RelativeSource.FindAncestor at all costs. Instead, define an attached property and use property inheritance to push values down the visual tree instead of looking up the visual tree.
Avoid Databinding to the Label.Content Property If you are using a Label to data bind to a String property, this will result in poor performance. This is because each time the String source is updated, the old string object is discarded, and a new String is created. If the Content of the Label is simple text, replace it with a TextBlock and bind to the Text property instead.
Bind ItemsControls to IList instead of IEnumerable When data binding an ItemsControl to an IEnumerable, WPF will create a wrapper of type IList which negatively impacts performance with the creation of a second object. Instead, bind the ItemsControl directly to an IList to avoid the overhead of the wrapper object.
Use the NeutralResourcesLanguage Attribute Use the NeutralResourcesLanguageAttribute to tell the ResourceManager what the neutral culture is and avoid unsuccessful satellite assembly lookups.
Load Data on Separate Threads A very common source of performance problems, UI freezes, and apps that stop responding is how you load your data. Make sure you are asynchronously loading your data on a separate thread as to not overload the UI thread. Loading data on the UI thread will result in very poor performance and an overall bad end-user experience. Multi-threading should be something every WPF developer is using in their applications.
Beware of Memory Leaks Memory leaks are the number one cause of performance problems in most WPF applications. They are easy to have but can be difficult to find. For example, using the DependencyPropertyDescriptor.AddValueChanged can cause the WPF framework to take a strong reference to the source of the event that isn’t removed until you manually call DependencyPropertyDescriptor.RemoveValueChanged. If your views or behaviors rely on events being raised from an object or ViewModel (such as INotifyPropertyChanged), subscribe to them weakly or make sure you are manually unsubscribing. Also, if you are binding to properties in a ViewModel which does not implement INotifyPropertyChanged, chances are you have a memory leak.
Finally, a bonus tip. Sometimes when you have a performance problem it can be very difficult to identify what exactly is causing the issue. I suggest using an application performance profiler to help identify where these performance bottlenecks are occurring in your code base. There are a lot of profiler options available to you. Some are paid, and some are free. The one I personally use the most is the Diagnosis Tools built directly into Visual Studio 2019. Blockquote
来源:https://dzone.com/articles/15-wpf-performance-tips-for-2019