Отражение через сериализованный объект для установки события PropertyChanged - PullRequest
4 голосов
/ 29 сентября 2011

У меня есть объект, который создается путем десериализации некоторого XML.Я использовал инструмент Visual Studio для генерации XSD из XML-макета производителя.Затем, используя инструмент XSD, я получил от них классы.Я установил инструмент XSD таким образом, чтобы сгенерированные классы были INotifyPropertyChanged.

Теперь я пытаюсь отобразить этот объект на вкладке в моем приложении WPF.Мне нужен «грязный» индикатор всякий раз, когда кто-то вносит изменения.Проблема в том, что этот объект, будучи сгенерированным классом из сгенерированного XSD, не является самой красивой структурой.Отображение для этого объекта не имитирует его структуру данных.Я думал о создании экранных объектов (без использования банкомата MMVM) и об их использовании, чтобы связать мои изменения с последующим сохранением этих изменений в объекте в качестве объекта данных.По сути, я бы просто выбросил свой текущий дисплей, чтобы сделать это, так как сейчас я просто добавляю проверку на наличие изменений.

Я думаю, что нужно отразить объект и установить событие PropertyChanged для каждого свойства, которое япопадаются (прогуливаясь по графику объектов и свойств).Мое размышление фу меня подводит, и я также, вероятно, допускаю некоторые недальновидные ошибки.

Вот код, который я написал до сих пор:

void SetupPropertyChanged(INotifyPropertyChanged component)
    {
        component.PropertyChanged += CAMConfig_PropertyChanged;

        Type componentType = component.GetType();

        foreach (PropertyInfo info in componentType.GetProperties())
        {
            Type[] types =
                info.PropertyType.FindInterfaces((a, b) => { return a.ToString() == b.ToString(); }, typeof(INotifyPropertyChanged));

            bool isINotify = types.Contains(typeof(INotifyPropertyChanged));


            if (isINotify)
                this.SetupPropertyChanged((INotifyPropertyChanged)info.GetValue(component, new object[] { }));
        }
    }

Я думаю, что ясталкивается с проблемами типа свойства Observable коллекции, поскольку вызывает исключение, когда я пересекаю свой объект.Меня также поразило, что я не знаю, будет ли эта структура объекта иметь циклические ссылки.

Может ли кто-нибудь помочь мне разработать этот код, чтобы я мог пройти по графу объектов.Прямо сейчас я не слишком обеспокоен возможностью циклических ссылок, но если решение, которое предотвращает эту ситуацию, будет очень, очень полезно!

Основываясь на ответе Карела, я создал этого «помощника»."класс:

    public static class NotifyPropertyChangedHelper
{
    public delegate void ChangeOccuredHandler(object sender);

    public static void SetupPropertyChanged(INotifyPropertyChanged component, ChangeOccuredHandler changedHandler)
    {
        SetupPropertyChanged(new List<object>(), component, changedHandler);
    }

    static void SetupPropertyChanged(IList<object> closed, INotifyPropertyChanged component, ChangeOccuredHandler changedHandler)
    {
        if (closed.Contains(component)) return; // event was already registered

        closed.Add(component); //adds the property that is to be processed

        //sets the property changed event if the property isn't a collection
        if (!(component is INotifyCollectionChanged))
            component.PropertyChanged += (sender, e) =>
                {
                    changedHandler(sender);
                };

        /*
         * If the component is an enumerable there are two steps. First check to see if it supports the INotifyCollectionChanged event.
         * If it supports it add and handler on to this object to support notification.  Next iterate through the collection of objects
         * to add hook up their PropertyChangedEvent.
         * 
         * If the component isn't a collection then iterate through its properties and attach the changed handler to the properties.
         */
        if (component is IEnumerable<object>)
        {
            if (component is INotifyCollectionChanged)
            {
                //((INotifyCollectionChanged)component).CollectionChanged += collectionHandler;
                ((INotifyCollectionChanged)component).CollectionChanged += (sender, e) =>
                    {
                        changedHandler(sender);
                    };
            }

            foreach (object obj in component as IEnumerable<object>)
            {
                if (obj is INotifyPropertyChanged)
                    SetupPropertyChanged(closed, (INotifyPropertyChanged)obj, changedHandler);
            }
        }
        else
        {
            foreach (PropertyInfo info in component.GetType().GetProperties())
            {
                var propertyValue = info.GetValue(component, new object[] { });
                var inpc = propertyValue as INotifyPropertyChanged;
                if (inpc == null) continue;
                SetupPropertyChanged(closed, inpc, changedHandler);
            }
        }
    }
}

1 Ответ

3 голосов
/ 29 сентября 2011

Сохраните список свойств (компонентов), для которых вы уже зарегистрировали событие, и сделайте вашу функцию рекурсивной.И вы можете привести компонент к INotifyPropertyChanged напрямую.

void SetupPropertyChanged(IList<object> closed, INotifyPropertyChanged component)  
{
  if(closed.Contains(component)) return; // event was already registered

  closed.Add(component);

  component.PropertyChanged += CAMConfig_PropertyChanged;            
  foreach (PropertyInfo info in componentType.GetProperties())          
  {
    var propertyValue = info.GetValue(component, new object[] { });
    var inpc = propertyValue as INotifyPropertyChanged;
    if(inpc == null) continue;
    this.SetupPropertyChanged(closed, inpc);
  }  
}
...