Мнения о отображении полей вложенных / иерархических объектов в плоский список? - PullRequest
0 голосов
/ 08 мая 2020

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

Моя ViewModel должна иметь возможность запускать / обновлять свои экземпляры из данных этих свойств документа, а также записывать свои значения и обновлять поля документа.

Примерно так:

class PersonVM : INotifyPropertyChanged 
{
  // properties
  string Name { get; set; }
  string PhoneNumber { get; set; }

  // methods to get data or save data of this properties to or from the word document
  void saveMyPropertyValuesToWord()
  {
    // …
  } 

  void updateMyPropertiesFromWord()
  {
     // …
  }
}

class ProjectVM : INotifyPropertyChanged
{
  int ProjectNumber { get; set; }
  PersonVM Manager { get; set; }
  PersonVM Mechanic1 { get; set; }
  PersonVM Mechanic2 { get; set; }

  void saveMyPropertyValuesToWord()
  {
     Manager.saveMyPropertyValuesToWord();
     Mechanic1.saveMyPropertyValuesToWord();
     Mechanic2.saveMyPropertyValuesToWord();

     // handle ProjectNumber  etc.
  } 

  void updateMyPropertiesFromWord()
  {
     Manager.updateMyPropertiesFromWord();
     Mechanic1.updateMyPropertiesFromWord();
     Mechanic2.updateMyPropertiesFromWord();

     // handle ProjectNumber  etc.
  }

  class CompanyVM : INotifyPropertyChanged
  {
    string Name { get; set; }
    PersonVM Owner { get; set; } 
    ProjectVM Project1 { get; set; }
    ProjectVM Project2 { get; set; }

    // …
  }

  // …
}

Прямо сейчас у меня есть класс со stati c строковыми свойствами для каждого свойства документа, которое может присутствовать в текстовом документе, из которого я хотел бы загрузить данные соответственно:

class WordUtils 
{
  // Company
  static string CompanyName = "dp_CompanyName";
  // Company.Owner
  static string CompanyOwnerName = "dp_CompanyOwnerName";
  static string CompanyOwnerPhone = "dp_CompanyOwnerPhone";
  // Company.Project1
  static string CompanyProject1Number = "dp_CompanyProject1Number";
  // Company.Project1.Manager
  static string CompanyProject1ManagerName = "dp_CompanyProject1ManagerName";
  static string CompanyProject1ManagerPhone = "dp_CompanyProject1ManagerPhone";
  // Company.Project1.Mechanic1

  // … etc
}

Теперь вернемся к реализации этих PersonVM.saveMyPropertyValuesToWord() - я подумал о чем-то вроде этого:

void saveMyPropertyValuesToWord()
{
   Name = MyApp.MyWordDocument.GetCustomProperty(WordUtils.OwnerName);
}

, но здесь мне нужно точно знать на уровне класса, из какого экземпляра он вызывается (т.е. какой PersonVM я, Company.Owner или Project1.Manager или?), чтобы решить, какое WordUtils.Name мне нужно предоставить.

Я не уверен, как это должно быть сделано, может быть, сделать PersonVM абстрактным и создать новый класс для каждой роли (который снова будет иметь только один экземпляр, что не очень красиво в моих глазах)? Я также кратко рассмотрел атрибуты и ожидаю, что они могут быть полезны в этом сценарии. Может быть, мне не хватает чего-то очевидного, но обширные поиски надежного способа решения этой проблемы пока что безрезультатны.

Ответы [ 2 ]

1 голос
/ 09 мая 2020

Конечно, вы можете реализовать конкретный класс IPerson для каждого типа и жестко запрограммировать отдельные реализации.

Поскольку вы знаете тип человека в момент создания экземпляра PersonVMM, вы можно добавить атрибут PersonTypeId и установить его из конструктора

void SomeMethod()
{
  var personVm = new PersonVM(WordUtils.OwnerName);
}

class PersonVM : INotifyPropertyChanged 
{
  // properties
  string PersonTypeId { get; set; }
  string Name { get; set; }
  string PhoneNumber { get; set; }

  public PersonVM() 
  {}

  public PersonVM(string personTypeId) 
  {
    PersonTypeId = personTypeId;
  }

  // methods to get data or save data of this properties to or from the word document
  void saveMyPropertyValuesToWord()
  {
    Name = MyApp.MyWordDocument.GetCustomProperty(PersonTypeId);
  }
}
1 голос
/ 08 мая 2020

Как насчет чего-то вроде этого:

class Property
{
    public string Key { get; }
    public string Value { get; set; }
    public Property(string key) => Key = key;
}

interface IPropertyTree
{
    IEnumerable<IPropertyTree> ChildNodes { get; }
    IEnumerable<Property> Properties { get; }
}
class PersonVM : IPropertyTree
{
    private readonly string prefix;

    public PersonVM(string prefix)
    {
        Name = new Property(prefix + "Name" );
        PhoneNumber = new Property(prefix + "PhoneNumber");
    }

    public Property Name { get;  }
    public Property PhoneNumber { get;  }
    public IEnumerable<IPropertyTree> ChildNodes => Enumerable.Empty<IPropertyTree>();
    public IEnumerable<Property> Properties => new[] {Name, PhoneNumber};
}
static class PropertyTreeExtensions
{
    public static void Update(this IPropertyTree self)
    {
        foreach (var property in self.Flatten().SelectMany(tree => tree.Properties))
        {
            property.Value = MyApp.MyWordDocument.GetCustomProperty(property.Key);
        }
    }

    public static IEnumerable<IPropertyTree> Flatten(this IPropertyTree self)
    {
        var stack = new Stack<IPropertyTree>();
        stack.Push(self);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            foreach (var child in current.ChildNodes)
            {
                stack.Push(child);
            }
        }
    }
}

Это должно позволить каждому свойству иметь уникальный ключ и сохранять тесную связь между ключом и значением свойства. Это также должно позволить вам переместить лог c сохранения / обновления в централизованное место.

...