zoukankan      html  css  js  c++  java
  • WPF架构分析

    1.DisptcherObject提供了线程和并发模型,实现了消息系统。
    2.DependencyObject提供了更改通知,实现了绑定,样式。
    3.Visual是托管API和非托管API(milcore)的之间的关键点。
    4.UIElement定义了Layout,Input和Events等核心子系统。Measure让一个组件来决定自己想要的size,而Arrange让父组件放置子组件并决定子组件的最终size。
    5.WPF的外观和行为总共有3个模型:数据模型(Properties),交互模型(Commands,Events),显示模型(Template)。
    6.不同应用程序域间的WPF可以通过INativeHandleContract来实现WPF AddIn。
    7.WPF定义了三个呈现曾:
         
    • 呈现层 0 无图形硬件加速。 所有图形功能都使用软件加速。 DirectX 版本级别低于 9.0。

    • 呈现层 1 某些图形功能使用图形硬件加速。 DirectX 版本级别高于或等于 9.0。

    • 呈现层 2 大多数图形功能都使用图形硬件加速。 DirectX 版本级别高于或等于 9.0。

    8. WPF性能提升: 
         1)布局处理过程。
              再次调用布局处理的几个操作:向集合中添加了一个子对象; 向子对象应用了 LayoutTransform为子对象调用了 UpdateLayout 方法; 用影响测量或排列过程的元数据进行了标记的依赖项属性的值发生更改。

              提高方法:
                   尽量使用最有效的Panel,功能越强大的Panel性能成本也就越高; 
                   更新而不替换RenderTransform; 
                   从上到下生成逻辑树。
         2)二维图形和图像处理
              提高方法:
                    Drawing对象比Shape对象结构简单,性能更为优越,但是不是从FrameworkElement集成,使用时注意; 
                    使用StreamGeometry而不是PathGeometry;
                    如果需要显示缩略图,尽量使用小版本的图像,或者请求WPF将图像解码为缩略图大小,或者请求WPF加载缩略图大小。始终将图像解码为所需的大小而不是默认大小。
                    
         3)行为
              提高方法:
                   注册DependencyObject时,尽量提供默认值和PropertyMetadata,而不是放在以后赋值。
                   冻结Freezable会改善程序性能,不再需要因维护更改通知而消耗资源。
                   使用VirtualizingStackPanel而不是StackPanel,注意:这样子资源苏不可见时即被移除,这时无法访问不可见的子元素。
                   尽可能使用静态资源,只在必须得情况下使用动态资源。
                   避免在FlowDocument使用TextBlock,而应该使用Run。
                   避免在TextBlock里使用Run来设置文本属性。
                   避免执行到Lable.Content属性的数据绑定。 如果数据源频繁更新,应用TextBlock.Text替换。 
         4)数据绑定
              提高方法:
                    性能从高到低:DependencyObject 〉INotifyPropertyChanged  
                    如果绑定到较大的CLR对象,考虑将对象拆分成多个具有少量属性的CLR对象
                    将IList(而非IEnumerable)绑定到ItemsCount
         5)控件
              提高方法:
                   设置 IsVirtualizing 为true, VirtualizationMode 为Recycling,  IsDeferredScrollingEnabled为true。
                        IsVirtualizing代表是否UI虚拟化,当设置为true时,表示只有当数据项在屏幕上可见时才会在内存中创建存储。 
                        VirtualizationMode代表是否容器回收,正常情况下ItemsControl会为滚动到视图中的每个item创建项容器,并销毁滚动到视图之外的每个item的项容器。通过Recycling,可以让控件能够将现有项容器重复利用于不同的数据项。
                        
                        IsDeferredScrollingEnabled代表是否延长滚动,正常情况下当用户拖到滚动条上的滑块时,内容视图会不会不断更新。当设置为true, 表示只有当用户松开滚动条时,内容才会更新。

                        
         6)其他
                   尽量使用画笔的不透明度(Opacity),而不是使用元素的Opacity,修改元素的Opacity会导致WPF创建临时图标。
                   配置“WPF字体缓存服务”从手动为自动(延迟启动),这个服务如果没有启动,会随着第一个WPF程序启动时启动。这样导致第一个WPF的初始化事件很长。


    9. WPF线程模型
        典型的wpf程序有2个线程,一个用于负责渲染,一个用于管理UI。UI线程把工作项排序到Dispatcher对象中,Dispatcher对象根据工作项的优先级选择执行,直到全部执行。每个UI线程必须至少有一个Dispatcher,每个Dispatcher必须在一个线程里工作。 

         可以通过CheckAccess来判断线程是否可以访问DispatcherObject。原理是:大多数类继承于DispatcherObject,DispatcherObject在构造时把当前运行线程的Dispatcher引用存储。当访问DispatcherObject时,检查当前线程关联的Dispatcher于构造中存储的Dispatcher进行比较,如果相同返回True,不同返回False。

         要构建响应速度快、且用户友好的应用程序,诀窍是减小工作项,以最大限度地提高 Dispatcher 吞吐量。 这样,工作项将永远不会因为在 Dispatcher 队列中等待处理而失效。 输入与响应之间的任何可察觉的延迟都会使用户不快。

         嵌套的消息泵:比如MessageBox,我们调用show后需要用户单击“OK"才能返回。MessageBox创建的窗口必须要由一个消息泵才能进行交互。我们在等待用户单击”OK“时原始应用程序窗口不响应用户输入。
         具体实现: WPF使用一种嵌套的消息处理系统, Dispatcher类包含一个PushFrame的特殊方法, 该方法存储应用程序的当前执行点,然后开始一个新的消息泵,当嵌套的消息泵执行结束时,执行将在最初的PushFrame调用之后继续。PushFrame内部实现类似Win32中GetMessage、TranslateMessage、DispatchMessage的消息泵。 (通过这个原理,我们可以实现自己的MessageBox系统)
         Application.Run内部调用Dispatcher.Run(),Dispatcher.Run()内部调用了Dispatcher.PushFrame(..), 实现了一个Win32的消息泵。

         DispatcherOperation对象用于与Dispatcher队列上的Delegate进行交互,例如更改委托的优先级、从事件队列中移除委托、等待委托返回、获取委托执行之后返回的值。

         Application.DoEvents方法:处理当前Dispatcher消息队列里的所有windows消息。
         
         关于BeginInvoke和Invoke,向关联的Dispatcher的队列中插入同步或者异步工作项。



    10.Weak Event Pattern
         传统的侦听事件可能导致内存泄漏,source.SomeEvent += new SomeEventHandler(MyEventHandler) 这是因为为事件源添加事件处理程序时,会创建一个事件源到事件侦听器(所谓事件侦听器,就是Delegate对象)的强引用。这样事件侦听器就会有生命周期,该生命周期和事件源的生命周期有关, 除非显式的移除了事件处理程序, source.SomeEvent -= new SomeEventHandler(MyEventHandler) 。当事件源从可视树中移除时,事件侦听器还是有生命周期,但此时事件处理程序不会被调用,也就是说造成了无用的事件侦听器的还一直存在。这种情况下就需要弱事件模式。
         
         实现弱事件模式,有三种方式:使用系统已有的(CollectionChangedEventManager,PropertyChangedEventManager等等); 使用泛型弱事件管理器(WeakEventManager<TEventSource, TeventArgs>, 注意有性能损失); 继承WeakEventManager类,实现自定义弱事件管理器。

         例如: 

                        class SomeEventWeakEventManager : WeakEventManager
    {

        private SomeEventWeakEventManager()
        {

        }

        /// <summary>
        /// Add a handler for the given source's event.
        /// </summary>
        public static void AddHandler(EventSource source, 
                                      EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");

            CurrentManager.ProtectedAddHandler(source, handler);
        }

        /// <summary>
        /// Remove a handler for the given source's event.
        /// </summary>
        public static void RemoveHandler(EventSource source, 
                                         EventHandler<SomeEventEventArgs> handler)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (handler == null)
                throw new ArgumentNullException("handler");

            CurrentManager.ProtectedRemoveHandler(source, handler);
        }

        /// <summary>
        /// Get the event manager for the current thread.
        /// </summary>
        private static SomeEventWeakEventManager CurrentManager
        {
            get
            {
                Type managerType = typeof(SomeEventWeakEventManager);
                SomeEventWeakEventManager manager = 
                    (SomeEventWeakEventManager)GetCurrentManager(managerType);

                // at first use, create and register a new manager
                if (manager == null)
                {
                    manager = new SomeEventWeakEventManager();
                    SetCurrentManager(managerType, manager);
                }

                return manager;
            }
        }

        /// <summary>
        /// Return a new list to hold listeners to the event.
        /// </summary>
        protected override ListenerList NewListenerList()
        {
            return new ListenerList<SomeEventEventArgs>();
        }


        /// <summary>
        /// Listen to the given source for the event.
        /// </summary>
        protected override void StartListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent += new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }

        /// <summary>
        /// Stop listening to the given source for the event.
        /// </summary>
        protected override void StopListening(object source)
        {
            EventSource typedSource = (EventSource)source;
            typedSource.SomeEvent -= new EventHandler<SomeEventEventArgs>(OnSomeEvent);
        }

        /// <summary>
        /// Event handler for the SomeEvent event.
        /// </summary>
        void OnSomeEvent(object sender, SomeEventEventArgs e)
        {
            DeliverEvent(sender, e);
        }
    }


         使用方法: 
                将
                       source.SomeEvent += new SomeEventEventHandler(OnSomeEvent);
                       source.SomeEvent -= new SomeEventEventHandler(OnSome);

                替换为  

                        SomeEventWeakEventManager.AddHandler(source, OnSomeEvent);
                        SomeEventWeakEventManager.RemoveHandler(source, OnSomeEvent);








              http://msdn.microsoft.com/zh-cn/library/aa970683.aspx   优化WPF应用程序性能
              http://msdn.microsoft.com/en-us/library/ms741870.aspx   WPF线程模型




  • 相关阅读:
    ASP.NET 无提示关闭窗口
    C# Winform程序获取外网IP地址
    使用System.Drawing.Printing 画报表。
    C# Winform调用IP地址、手机归属和身份证查询接口
    C# 获取文中文字符首字母
    C# WinForm给Button或其它控件添加快捷键响应
    C# 获取农历日期
    TSQL之插入的内容是查询出的值
    C# 获取中文星期的两种方法
    C#批量操作控件
  • 原文地址:https://www.cnblogs.com/muzizongheng/p/3170875.html
Copyright © 2011-2022 走看看