【问题标题】:identify what property changed, and edit all selected Items确定更改了哪些属性,并编辑所有选定的项目
【发布时间】:2021-03-17 04:32:10
【问题描述】:

我最近发现了如何在我的 ListView 中更改 Item 的属性时添加事件:

foreach (Profil item in gp.ListProfils)
{
    if (item is INotifyPropertyChanged)
    {
        INotifyPropertyChanged observable = (INotifyPropertyChanged)item;
        observable.PropertyChanged +=
            new PropertyChangedEventHandler(ItemPropertyChanged);
    }
}

现在,我想将修改应用到所有选定的项目。

我找到了如何识别更改的属性名称:

private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if(sender is Profil)
    {
        string myPropertyName = e.PropertyName;
        Profil editedProfile = (Profil)sender;
        foreach(Profil prf in this.SelectedProfiles)
        {
            //prf.NAME_PROPERTY = editedProfile.NAME_PROPERTY
            if (!this.ListProfilToEdit.Contains(editedProfile))
            {
                this.ListProfilToEdit.Add(editedProfile);
            }
        }
    }
}

但是如何替换注释中的行。 具体来说,如果我编辑了“宽度”字段,我想更改所有选定元素的宽度。 有没有办法做到这一点,而不创建一个单独的函数EditProperty(string nameProperty)

【问题讨论】:

    标签: c# wpf listview inotifypropertychanged


    【解决方案1】:

    在这里,您需要能够从一个对象动态读取一个属性,然后将其动态写入另一个对象。

    您可以使用表达式树动态生成 Getter 和 Setter,并将它们放入缓存中。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    
    namespace CoreAppMain
    {
        public class TypeAccessors<T> where T : class
        {
            //Getters
            public static Dictionary<string, Func<T, object>> Getters = GetTypeGetters();
    
            private static Dictionary<string, Func<T, object>> GetTypeGetters()
            {
                var _getters = new Dictionary<string, Func<T, object>>();
                var props = typeof(T).GetProperties();
                foreach (var p in props)
                {
                    var entityParam = Expression.Parameter(typeof(T), "e");
                    Expression columnExpr = Expression.Property(entityParam, p);
                    Expression conversion = Expression.Convert(columnExpr, typeof(object));
                    var fct = Expression.Lambda<Func<T, object>>(conversion, entityParam).Compile();
                    _getters.Add(p.Name, fct);
                }
    
                return _getters;
            }
    
            //setters
            public static Dictionary<string, Action<T, object>> Setters = GetTypeSetters();
    
            public static Dictionary<string, Action<T, object>> GetTypeSetters()
            {
                var setters = new Dictionary<string, Action<T, object>>();
                const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
                var propsToSet = typeof(T).GetProperties(flags)
                    .Where(x => x.CanWrite)
                    .ToList();
    
                foreach (var field in propsToSet)
                {
                    var propertyInfo = typeof(T).GetProperty(field.Name);
                    setters.Add(field.Name, GetPropertySetter<T>(propertyInfo));
                }
                return setters;
            }
    
            public static Action<TModel, object> GetPropertySetter<TModel>(PropertyInfo propertyInfo)
            {
                // Note that we are testing whether this is a value type
                bool isValueType = propertyInfo.DeclaringType.IsValueType;
                var method = propertyInfo.GetSetMethod(true);
                var obj = Expression.Parameter(typeof(object), "o");
                var value = Expression.Parameter(typeof(object));
    
                // Note that we are using Expression.Unbox for value types
                // and Expression.Convert for reference types
                Expression<Action<TModel, object>> expr =
                    Expression.Lambda<Action<TModel, object>>(
                        Expression.Call(
                            isValueType ?
                                Expression.Unbox(obj, method.DeclaringType) :
                                Expression.Convert(obj, method.DeclaringType),
                            method,
                            Expression.Convert(value, method.GetParameters()[0].ParameterType)),
                            obj, value);
                Action<TModel, object> action = expr.Compile();
                return action;
            }
    
        }
    }
    

    用法示例:

    var cust1 = new Customer()
    {
        FirstName = "TOTOT",
        LastName = "TITI"
    };
    
    var cust2 = new Customer()
    {
        FirstName = "XXXXXX",
        LastName = "YYYYYY"
    };
    
    var value = TypeAccessors<Customer>.Getters[nameof(Customer.FirstName)](cust2);
    TypeAccessors<Customer>.Setters[nameof(Customer.FirstName)](cust1, value);
    
    Console.WriteLine(cust1.FirstName);
    Console.ReadKey();
    

    【讨论】:

      【解决方案2】:

      有没有办法做到这一点,而不创建一个单独的函数 EditProperty(string nameProperty)?

      是否创建一个单独的方法是一个好的设计和干净的代码的问题,它不会以任何方式影响问题的核心。您始终可以创建单独的方法或在现有方法中编写代码。

      但是如何替换注释中的行。

      不清楚你在做什么,所以我怀疑你所问的问题是否可以用不同的方式解决,但事实上,你要求只知道实例的名称来读取和写入实例的属性.为此,您需要使用 Reflection

      为了执行后期绑定,访问在运行时创建的类型的方法。请参阅主题Dynamically Loading and Using Types

      假设您的属性是公开的,这应该可以工作:

      var propertyInfo = typeof(Profil).GetProperty(myPropertyName);
      var value = propertyInfo.GetValue(editedProfile, null);
      
      foreach(Profil prf in this.SelectedProfiles)
      {
         propertyInfo.SetValue(prf, value);
         
         // ...other code.
      }
      

      也就是说,要小心使用反射,以便进一步阅读:

      【讨论】:

      • 谢谢,事实上这正是我最终决定要做的(你写的那段代码)。因为我也注意到,我只需要为某些字段而不是所有字段启用“多重编辑”,所以我做了一个函数,只有当字段名称对应于这两个字段时才能完成这项工作。
      猜你喜欢
      • 2011-04-21
      • 1970-01-01
      • 1970-01-01
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2016-10-07
      相关资源
      最近更新 更多