2016-06-23 4 views
1

Я использую MVVM Light в Visual Studio 2015 для создания WPF-приложения. В коде есть метод, который повторяется с небольшим изменением в коде 4 раза; единственное различие заключается в изменении типа ObservableCollection и способа, вызываемого на уровне службы данных.Как сделать метод многоразовым с разными типами

Вот метод, который возвращает ObservableCollection объектов StatusViewModel, которые используются для заполнения ComboBox; StatusVm используется для связывания с SelectedItem в ComboBox, устанавливается в качестве первого элемента в коллекции и «пустым»:

private async Task<ObservableCollection<StatusViewModel>> GetStatuses() 
{ 
    var result = new ObservableCollection<StatusViewModel>(); 
    var blank = new StatusViewModel 
    { 
     StatusId = -1, 
     Status = null, 
     Description = null, 
     IsActive = false, 
     CreatedDate = DateTime.Now 
    }; 
    result.Add(blank); 
    var dataService = new MyDataService(); 
    foreach (var c in await dataService.GetStatuses()) 
     result.Add(c); 
    StatusVm = 
     result.SingleOrDefault(c => c.StatusId.Equals(-1)); 
    return result; 
} 

Вот частное поле и общественное имущество для StatusVm:

private StatusViewModel _statusVm; 
public StatusViewModel StatusVm 
{ 
    get { return _statusVm; } 
    set 
    { 
     if (Equals(value, _statusVm)) return; 
     _statusVm = value; 
     RaisePropertyChanged(); 
    } 
} 

Теперь представьте, что это было повторено еще 3 раза, еще 3 типа VM! Как сделать GetStatuses() в методе, который может принимать разные типы моделей вида и вызвать соответствующий метод в службе данных? Спасибо.

Update: Вот свойство и метод для других типов:

private MroViewModel_mroVm; 
public MroViewModel MroVm 
{ 
    get { return _mroVm; } 
    set 
    { 
     if (Equals(value, _mroVm)) return; 
     _mroVm = value; 
     RaisePropertyChanged(); 
    } 
} 

private async Task<ObservableCollection<MroViewModel>> GetMro() 
{ 
    var result = new ObservableCollection<MroViewModel>(); 
    var blank = new MroViewModel 
    { 
     StatusId = -1, 
     Status = null, 
     Description = null, 
     IsActive = false, 
     CreatedDate = DateTime.Now 
    }; 
    result.Add(blank); 
    var dataService = new MyDataService(); 
    foreach (var c in await dataService.GetMro()) 
     result.Add(c); 
    MroVm = 
     result.SingleOrDefault(c => c.StatusId.Equals(-1)); 
    return result; 
} 
+1

Две проблемы с генерацией 'GetStatuses()' являются инициализацией 'blank' и вызовом веб-метода' GetStatuses() '. Первое, что я хотел бы обработать, либо потребовав, чтобы модели viewmodels были инициализированы соответствующим образом, или если это нецелесообразно, потребовав от них реализовать интерфейс, который возвращает экземпляр 'blank'. Затем может потребоваться, чтобы вызывающие абоненты передавали в лямбда, которая вызывает веб-метод, или требуют, чтобы модели просмотра предоставляли метод «CallWebMethod()». Или добавьте это в интерфейс, о котором я упоминал выше. Назовите его 'IWebMethodRetrievable' или некоторые из них. Идея интерфейса растет на меня. –

+0

Спасибо, @EdPlunkett. Какой пример лямбды для вызова веб-метода? – Alex

+2

Как получить общий метод lile private async Задача > GetStatuses (T t), если все типы имеют одинаковые свойства? – Ian

ответ

3

Вы бы создать интерфейс для общих свойств.

internal interface IStatusViewModel { 
     int StatusId { get; set; } 
     string Status { get; set; } 
     string Description { get; set; } 
     bool IsActive { get; set; } 
     DateTime CreatedDate { get; set; } 
} 

Реализовать интерфейс в классах, которые вы должны получить статус для

internal class MroViewModel : IStatusViewModel { 
     public int StatusId { get; set; } 
     public string Status { get; set; } 
     public string Description { get; set; } 
     public bool IsActive { get; set; } 
     public DateTime CreatedDate { get; set; } 
} 

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

public static async Task<ObservableCollection<T>> GetStatuses<T>(
    Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction) 
    where T : IStatusViewModel, new() 
{ 
    var result = new ObservableCollection<T>(); 
    var blank = new T 
    { 
     StatusId = -1, 
     Status = null, 
     Description = null, 
     IsActive = false, 
     CreatedDate = DateTime.Now 
    }; 
    result.Add(blank); 

    var dataService = new MyDataService(); 
    foreach (var c in await retrieveStatusesAction(dataService)) 
     result.Add(c); 

    // TODO Implement Expression<Func<TSource, TResult>> projection for assigning to VM 
    StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)); 

    return result; 
} 

Вы бы затем вызвать этот метод следующим образом:

GetStatuses((service) => service.GetMro()); 

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

Для выражения и свойств присвоения: Property selector Expression<Func<T>>. How to get/set value to selected property

- EDIT -

Нечто подобное для назначения VM:

public static async Task<ObservableCollection<T>> GetStatuses<T, TContainer>(
     TContainer instance, 
     Expression<Func<TContainer, T>> viewModelProjection, 
     Func<MyDataService, Task<IEnumerable<T>>> retrieveStatusesAction) 
     where T : IStatusViewModel, new() 
    { 
     var result = new ObservableCollection<T>(); 
     var blank = new T 
     { 
      StatusId = -1, 
      Status = null, 
      Description = null, 
      IsActive = false, 
      CreatedDate = DateTime.Now 
     }; 
     result.Add(blank); 

     var dataService = new MyDataService(); 
     foreach (var c in await retrieveStatusesAction(dataService)) 
      result.Add(c); 

     var vmStatus = result.SingleOrDefault(c => c.StatusId.Equals(-1)); 

     // Warning: Check casted values, this is unsafe 
     var vm = (PropertyInfo)((MemberExpression)viewModelProjection.Body).Member; 
     vm.SetValue(instance, vmStatus, null); 

     return result; 
    } 

Вы бы вызвать метод как это:

await GetStatuses(this, inst => inst.MroVm, (service) => service.GetMro()); 

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

Это скомпилирует, но не уверен, что он будет вести себя так, как ожидалось. Должно. Если у вас возникли проблемы, напишите их в комментариях.

1

Можно определить интерфейсы и попытаться комбо Strategy и Factory, как следующее (я пропустил асинхронный/ждать для простоты):

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine("Starting"); 
     var mainVm = new MainViewModel(); 
     mainVm.GetStatuses(); 
     mainVm.GetMro(); 

     Console.WriteLine("Status: {0} {1}", mainVm.StatusVm.Name, mainVm.StatusVm.CreateDate); 
     Console.WriteLine("MroVm: {0} {1}", mainVm.MroVm.Name, mainVm.MroVm.CreateDate); 
    } 
} 

public class MainViewModel 
{ 
    public StatusViewModel StatusVm { get; set; } 
    public MroViewModel MroVm { get; set; } 

    public void GetStatuses() 
    { 
     var result = Get(VmKind.Status); 
     StatusVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as StatusViewModel; 
    } 
    public void GetMro() 
    { 
     var result = Get(VmKind.Mro); 
     MroVm = result.SingleOrDefault(c => c.StatusId.Equals(-1)) as MroViewModel; 
    } 

    public IEnumerable<IVm> Get(VmKind vmKind) 
    { 
     var dataService = new MyDataService(); 
     return dataService.Get(vmKind); 
    } 


} 

public interface IVm 
{ 
    int StatusId { get; set; } 
    DateTime CreateDate { get; set; } 
    string Name { get; } 
} 

public class StatusViewModel : IVm 
{ 
    public DateTime CreateDate { get; set; } 
    public int StatusId { get; set; } 
    public string Name { get { return "StatusViewModel"; } } 
} 

public class MroViewModel : IVm 
{ 
    public DateTime CreateDate { get; set; } 
    public int StatusId { get; set; } 
    public string Name { get { return "MroViewModel"; } } 
} 

public enum VmKind {Status, Mro } 

#region Strategy 

public interface IDataGetter 
{ 
    IEnumerable<IVm> Get(VmKind vmKind); 
} 

public class MyDataService : IDataGetter { 
    public IEnumerable<IVm> Get(VmKind vmKind) 
    { 
     switch (vmKind) 
     { 
      case VmKind.Status: 
       return GetStatuses(); 
       //break; 
      case VmKind.Mro: 
       return GetMro(); 
       //break; 
      default: 
       throw new ArgumentException("Unknown VM type"); 
     } 
    } 

    private IEnumerable<IVm> GetMro() 
    { 
     return new List<MroViewModel> { 
      new MroViewModel { StatusId = -1, CreateDate = DateTime.Now }, 
      new MroViewModel { StatusId = 2, CreateDate = DateTime.Now } 
     }; 
    } 

    private IEnumerable<StatusViewModel> GetStatuses() 
    { 
     return new List<StatusViewModel> { 
      new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now }, 
      new StatusViewModel { StatusId = 2, CreateDate = DateTime.Now } 
     }; 
    } 
} 
#endregion 

#region Factory 
public class VmFactory { 
    static IVm Create(VmKind vmKind) 
    { 
     IVm result = null; 
     switch (vmKind) 
     { 
      case VmKind.Status: 
       result = new StatusViewModel { StatusId = -1, CreateDate = DateTime.Now }; 
       break; 
      case VmKind.Mro: 
       result = new MroViewModel { StatusId = -1, CreateDate = DateTime.Now }; 
       break; 
      default: 
       throw new ArgumentException("Unknown VM type"); 
       //break; 
     } 
     return result; 
    } 
} 
#endregion 

я фактически не использовать завод, но вы могли бы сделайте это для легкого создания виртуальной машины.

Смежные вопросы