Кнопка включения WPF MVVM в дочерней DataGrid с помощью Command и CommandParameter - PullRequest
0 голосов
/ 26 декабря 2018

У меня есть приложение WPF, в котором Window имеет отношение master / child.Когда пользователь выбирает Покупателя из ListView, DataGrid связывается со связанными предметами, которые они приобрели.В GridView слева есть кнопка, где можно произвести оплату за этот отдельный элемент.Однако я хочу отключить / включить кнопку в зависимости от состояния элемента, независимо от того, связан ли с ним существующий платеж.Использование Galasoft MVVM Light, RelayCommand во всем приложении.У меня кнопка Pay работает, но ТОЛЬКО при нажатии (выбранная строка).Это прекрасно работает, когда привязан к команде следующим образом:

<DataGrid ..
  ItemsSource="{Binding Items}" 
  SelectedItem="{Binding SelectedItem}"
  IsSynchronizedWithCurrentItem="True"
  .. >
    <DataGrid.Columns>
      <DataGridTemplateColumn>
        <DataGridTemplateColumn.CellTemplate>
          <DataTemplate>
          <Button ...
           Command="{Binding Path=DataContext.PayItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" 
           CommandParameter="{Binding SelectedItem, Mode=OneWay, ElementName=itemsPurchasedGrid}"
          >Pay</Button>
          ...

// in ctor
PayItemCommand = new RelayCommand<Item>(PayItem, IsItemUnPaid);

// in body
private void PayItem(Item item)
{
    PaymentMode = PaymentMode.Item;
    Messenger.Default.Send<BuyerViewModel>(this, "LaunchPayWindow");
}

// CanExecute impl
private bool IsItemUnPaid(Item item)
{
   // code to determine if item is already paid
}

Итак, когда я нажимаю кнопку, он правильно передает SelectedItem делегату RelayCommand.Однако реализация CanExecute: IsItemUnPaid (Item item) никогда не получает элемент, потому что он (еще) не выбран.Я попытался: IsSynchronizedWithCurrentItem = "True", который всегда пропускает первый элемент, но не любые другие.Есть ли способ получить дескриптор элемента, когда он привязан к DataGrid, чтобы отключить / включить его?Я уверен, что во время связывания есть неприятные пути в коде, но есть ли способ MVVM проверить текущий элемент и переключить эту кнопку?Большое спасибо!

PS Я написал простой конвертер, который работает, но действительно ли это лучший способ?

public class PayItemConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (!(value is Item item))
            return true;

        // if you find any payments for this item...
        var payments = (item.Payments != null && item.Payments.Any(p => p.ItemId.HasValue && p.ItemId.Value == item.Id))
            ? item.Payments.Where(p => p.ItemId.HasValue && p.ItemId.Value == item.Id).ToArray()
            : null;

        // if you find payments for this item, are the sum of the payments less than the total of the item TotalAmount, it is said to be unpaid
        return (payments != null && payments.Length > 0)
            ? payments.Sum(p => p.Amount) < item.TotalAmount
            : true;                      
    }

1 Ответ

0 голосов
/ 27 декабря 2018

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

Проблема в том, чтоВаше свойство Items похоже на данные модели, предположительно из базы данных или чего-то еще.Он должен представлять собой набор моделей представлений, по одной на элемент, который раскрывает свойства элемента (или даже самого элемента), а также содержит дополнительные поля для логики, необходимой вашему представлению, а именно:

public bool PayButtonEnabled {get; private set;}
...