-2

I recently found how to add an event when changing a property of an Item in my ListView :

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

Now, I would like to apply the modify to all selected items.

I found how to identify the name of property changed :

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);
            }
        }
    }
}

But how can I replace the line in comment. Concretely if I edited the field "Width", I want to change the width for all selected elements. Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?

Siegfried.V
  • 1,508
  • 1
  • 16
  • 34

2 Answers2

1

Is there a way to do it, without creating a separated function EditProperty(string nameProperty)?

Creating a separate method or not is a matter of good design and clean code, it does not affect the core of the question in any way. You can always create a separate method or write the code in your existing method.

But how can I replace the line in comment.

It is not exactly clear what you are doing, so I am in doubt whether what you are asking could be solved in different way, but as it is, you are asking to read and write properties of instances by only knowing their names. You will need to use Reflection for this.

For performing late binding, accessing methods on types created at run time. See the topic Dynamically Loading and Using Types.

Assuming your properties are public, this should work:

var propertyInfo = typeof(Profil).GetProperty(myPropertyName);
var value = propertyInfo.GetValue(editedProfile, null);

foreach(Profil prf in this.SelectedProfiles)
{
   propertyInfo.SetValue(prf, value);
   
   // ...other code.
}

That said, be careful using reflection, for further reading:

thatguy
  • 21,059
  • 6
  • 30
  • 40
  • Thanks, in fact this is exactly what I decided to do on the end( the piece of code you wrote). Because I also noticed, that I need to enable "multi-editing" only for some fields, and not all of them, so I did a function that did the job only if the field name correspond to these 2 fields. – Siegfried.V Dec 05 '20 at 15:24
1

Here you need to be able to read a property dynamically from an object and then write it dynamically on another object.

You can use Expression Trees to generate Getters & Setters dynamically and them put in the cache.

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;
        }

    }
}

Usage Example:

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();