zoukankan      html  css  js  c++  java
  • 定制WPF中的DataGrid控件支持对不同的实体类实现中文标题显示

    定制WPF中的DataGrid控件支持对不同的实体类实现中文标题显示

    问题提出:

    这是今天被问到的一个问题。情况是这样的:

    我们都知道WPF中有一个用来显示列表数据的DataGrid控件,而且该控件具有一个AutoGenerateColumns 属性(默认为true),它可以根据给定的数据,自动地设置列的标题,也就是说,我们可以根据需要读取不同的实体数据,然后绑定到控件上去,它自己知道该如何创建列,以及显示数据。

    这里的问题在于,我们的实体类定义通常都是英文的,例如下面是一个最简单的例子

        public class Employee
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
        }

    DataGrid会自动为每个属性建立一个列,并且列标题设置为属性名称。例如下面这样:

    image

    但是,美中不足的是,我们的用户更喜欢中文的标题。那么,我们是否能够以最小的代价,让这些标题能显示中文呢?

    解决方案:

    首先,我联想到了MVC中的一个做法,就是需要给实体类添加属性描述,因为无论如何,我们需要有一个地方可以定义这些中文标题。幸运的是,我们可以直接使用内置的DataAnnotation的功能来实现,例如:

        public class Employee
        {
            [Display(Name="姓氏")]
            public string FirstName { get; set; }
            [Display(Name="名字")]
            public string LastName { get; set; }
        }

    备注:这里要先引用System.ComponentModel.DataAnnotations 这个程序集。

    接下来,我们的问题就是,如何将这里定义好的Display的属性,读取到DataGrid的列标题处。我首先想到的是,能否通过定制ColumnHeaderStyle来实现,但经过一些努力,没有成功。如果有朋友对这个方案有补充,请不吝赐教。

    我最后采用的方法是这样的,DataGrid有一个事件叫:AutoGeneratingColumn  ,顾名思义,这个事件就是在列被创建出来之前触发的。我通过下面的代码实现了我们想要的功能。

            private void DataGrid_AutoGeneratingColumn_1(object sender, DataGridAutoGeneratingColumnEventArgs e)
            {
                var result = e.PropertyName;
                var p = (e.PropertyDescriptor as PropertyDescriptor).ComponentType.GetProperties().FirstOrDefault(x => x.Name == e.PropertyName);
    
                if (p != null)
                {
                    var found = p.GetCustomAttribute<DisplayAttribute>();
                    if (found != null) result = found.Name;
                }
    
                e.Column.Header = result;
            }

    这样一来,我们看到的界面就是下面这样的啦

    image

    而且重要的,这个功能是完全通用的,不管日后想要换成什么样的实体类型,都可以通过定义Display这个Attributel来改变标题。

    最后,我还这个功能封装了一下,以便更加好的使用.我做了一个扩展控件,如下所示

    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Linq;
    using System.Reflection;
    using System.Windows.Controls;
    
    namespace WpfApplication1
    {
        class xGrid:DataGrid
        {
            public xGrid()
            {
                AutoGeneratingColumn += (o, e) =>
                {
                    var result = e.PropertyName;
                    var p = (e.PropertyDescriptor as PropertyDescriptor).ComponentType.GetProperties().FirstOrDefault(x => x.Name == e.PropertyName);
    
                    if (p != null)
                    {
                        var found = p.GetCustomAttribute<DisplayAttribute>();
                        if (found != null) result = found.Name;
                    }
    
                    e.Column.Header = result;
                };
            }
        }
    }
    

    这样的话,在项目中任何页面上我都可以直接像下面这样使用这个控件了。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow"
            xmlns:controls="clr-namespace:WpfApplication1"
            Height="350"
            Width="525">
        <Grid>
            <controls:xGrid ItemsSource="{Binding}"></controls:xGrid>
        </Grid>
    </Window>
    
     
    分类: WPF或者Silverligh

    .NET中使用P/Invoke 导致内存已损坏异常的一则解决方法

     

    一 问题重现

        前面在减少.NET内存占用的一则实践中,和大家分享了在.NET中使用P/Invoke技术来调用C++编写的非托管代码的例子。虽然性能和内存占用还不错,但是在随后而来的几周里,在某些同事的机器上总是偶尔会出现异常导致应用程序突然崩溃,尤其是在一些配置比较好的机器上。于是完善了一下日志记录,捕捉到最多的异常是:

        “Attempted to read or write protected memory. This is often an indication that other memory is corrupt.”

        然后调试的时候无法跟进去,直接抛出如下的异常:

    内存已损坏

        根据这个异常实在查找不出任何有意义的信息,不过结合这两者很明显的知道,问题出在调用的非托管的代码里面。

    二 解决方法

        根据之前提示的问题,在Google里面查了一下,发现了一篇文章P/Invoke and memory related issues 该文章指出:由于.NET对内存崩溃异常敏感,所以P/Invoke最容易出现内存异常的问题。出现“Attempted to read or write protected memory. This is often an indication that other memory is corrupt”问题的很大一部分原因是P/Invoke非托管代码导致的,有两种情况下很容易出现:

    • 传进去了错误的指针。
    • 在非托管内部代码中有异常,或者在方法内部对内存存在错误访问。

        在程序中往非托管方法传进去参数的时候,都是以String作为参数的,返回值是以StringBuilder作为类型在参数里面带出来的,函数的返回值指示方法的执行成功与否。传进去的值是没有问题,传出的值的StringBuilder在开始调用的时候,也已经分配了足够大的空间。

        所以就开始看是否是在非托管代码里面是否出了异常,于是开始在C++对每个方法对进行了try catch看能否捕捉到异常,并尝试恢复,不让应用程序挂掉,但是发现C++中的异常处理并不是像.NET中的那样,出现了内存已损坏的问题,从中恢复很困难,导致程序直接崩溃。由于我对C++不太熟悉,很多方法都是我临时拿了本书看了下写的,所以为了彻底解决这一问题,去请教我们部门对C++比较懂的同事看了下,也没有发现什么问题。

    挣扎了一会儿,最后想到是不是在并发的时候出了问题,因为这个异常很容易在连续请求的时候产生,也很容易在配置比较好的机器上产生。于是想着对方法的访问加锁。在一开始的时候,我想着在C++里面加了锁,后来想想,还不如直接在调用P/Invoke方法的地方加锁。于是,解决方法很简单,定义一个全局的锁

    public static volatile object SecuLock = new object();

        然后在所有调用同一个非托管dll里面方法的地方都加锁,然后问题就解决了。

    lock (SeverCallBack.SecuLock)
    {
        EMGFindSecu("300",result);
    }

    三 问题的原因

        由于我在非托管的dll中,定义了一个全局的集合变量,然后多个方法都会对这个全局的变量进行查询或者修改等操作,在某些特定情况下,会发生同一个dll中的两个非托管方法会被同时调用的问题,这样这两个方法会同时操作一个集合对象,这时就会在C++中抛出如上异常,该异常捕捉到之后,似乎不太好恢复,所以会直接到导致应用程序出现崩溃。所以大家在应用P/Invoke的时候,如果出现如上的异常信息,不防对非托管dll方法中有可能对同一集合对象进行操作的方法加锁,在某一时刻,只允许这些方法中的一个方法对其进行操作。

        P/Invoke是.NET的一个很强大的特性,他使得我们能够高效的和非托管代码进行互操作,并且很容易使用,但是在使用的过程中也很容易出现异常,这些异常不仅难以处理和恢复,在大多数情况下会直接使得我们的应用程序崩溃。希望本文对您在.NET P/Invoke中遇到类似问题能够提供一些帮助。

    作者: yangecnuyangecnu's Blog on 博客园) 
    出处:http://www.cnblogs.com/yangecnu/ 
    作品yangecnu 创作,采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请 给我留言
     
  • 相关阅读:
    Vue2.5开发去哪儿网App 城市列表开发之 Vuex实现数据共享及高级使用
    Vue2.5开发去哪儿网App 搜索功能完成
    Vue2.5开发去哪儿网App 城市列表开发之 兄弟组件间联动及列表性能优化
    Vue2.5开发去哪儿网App 城市列表开发
    Vue2.5开发去哪儿网App 首页开发
    Vue2.5开发去哪儿网App 第五章笔记 下
    Vue2.5开发去哪儿网App 第五章笔记 上
    页面基本布局的一二事
    css属性—position的使用与页面的分层介绍
    Html的本质及在web中的作用
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3011256.html
Copyright © 2011-2022 走看看