2015-07-29 2 views
1

У меня есть головоломка с моим самым новым приложением.WPF MVVM с mvvmlight и Fluent.Validation

Это приложение MVFM WPF MVPM, которое использует MVVM Light и Fluent.Validation.

The View в DataContext является MainViewModel : ViewModelBase с ObservableCollection<ProviderDto> для ListView на левой и свойства ProviderDto SelectedProvider для детальных свойств справа.

Есть также несколько RelayCommands добавление, редактирование и удаление одиночных ProviderDto «ы

ViewModel использует ProviderService выполнить эти действия, которые впрыскивают в это конструктор с mvvmlight-х SimpleIoC в отдельном ViewModelLocator.

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

теперь я пытался добавить Fluent.Validation к Mix и реализован, как это описано в this post (My ProviderDto Теперь наследуется от ValidationBase вместо ObservableObject. База Теперь наследуется от ObservableObject. Также я зарегистрировал ProviderDtoValidator в ViewModelLocator .)

Это позволяет мне автоматически проверять мои ObservableObjects и называть .IsValid.

Пока все хорошо, я уверен, что смогу заставить его работать до вида и сделать эти окна ошибок красными :).


Теперь мой вопрос:

Я хочу иметь кнопки на View, чтобы сохранить изменения на SelectedProvider. Это, естественно, должны быть связаны с этим:

Relaycommand SaveProviderCommand = new RelayCommand(SaveProvider, CanSaveProvider) 

private bool CanSaveProvider() 
{ 
    return SelectedProvider.IsValid; 
} 

private void SaveProvider() 
{ 
    if (SelectedProvider.IsValid) 
     _providerController.SaveProvider(SelectedProvider); 
} 

Где поставить SaveProviderCommand SaveCommand?

Если я положил его в ViewModel, то я могу назвать его из SelectedProvider-недвижимости:

public ProviderDto SelectedProvider 
{ 
    get { return _selectedProvider; } 
    set 
    { 
     Set(() => SelectedProvider, ref prV_selectedProvider, value); 
     SaveProviderCommand.RaiseCanExecuteChanged(); // Here! 
    } 
} 

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

Другая возможность заключается в том, чтобы поставить команду на сам DTO и вызвать ее каждый раз, когда свойство получает Изменено. Например, когда E-mail-Недвижимость меняется:

//A Property from Provider 
public string Email 
{ 
    get { return _email; } 
    set 
    { 
     Set(() => Email, ref _email, value.TrimSafe()); 
     SaveProviderCommand.RaiseCanExecuteChanged(); // Here! 
    } 
} 

Преимущество здесь в том, что проверка работает вне коробки до Вид-Level при изменении каждого свойства. Недостатком является то, что я должен был бы ввести ProviderController в конструктор DTO, чтобы его можно было вызвать в частном Save-Method. Я не думаю, что DTO должен знать, как спасти себя. Он должен только определить, есть ли он .IsValid, и логика сохранения должна принадлежать ViewModel.

Я надеюсь, что вы можете увидеть мою дилемму:

  • Если я поставил SaveCommand в ViewModel, то я должен буду делать I-Dont-знает-что, чтобы проверить мой SelectedProvider в представлении. Как будет работать валидация? Я посмотрел в DataTemplating Controls, но, похоже, я не могу заставить его работать вместе с Fluent.Validation ..

  • Если я поместил SaveCommand в сам DTO, то проверка работает хорошо, но я не думаю правильно вводить так много возможностей во что-то, что должно оставаться немым.

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

ответ

1

Я нашел подходящее решение для этой проблемы, возможно, это поможет кому-то другому.

Поскольку как SelectedProvider в Viewmodel, так и в одиночных свойствах реализуют INotifyPropertyChanged (trhough ViewModelBase или ObservableObject), я могу просто подписаться на SelectedProvider.PropertyChanged в ViewModel.

public MainViewModel() 
{ 
    // Constructor 
    if (SelectedProvider != null) 
     SelectedProvider.PropertyChanged += SelectedProvider_PropertyChanged; 
} 

private void SelectedProvider_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    SaveProviderCommand.RaiseCanExecuteChanged(); 
} 

В окне можно осуществлять управление в соответствии с this post

<Window.Resources> 
    <Style TargetType="{x:Type TextBox}"> 
     <Setter Property="Validation.ErrorTemplate"> 
      <Setter.Value> 
       <ControlTemplate> 
        <DockPanel> 
         <Grid DockPanel.Dock="Right" Width="16" Height="16" 
          VerticalAlignment="Center" Margin="3 0 0 0"> 
          <Ellipse Width="16" Height="16" Fill="Red"/> 
          <Ellipse Width="3" Height="8" 
           VerticalAlignment="Top" HorizontalAlignment="Center" 
           Margin="0 2 0 0" Fill="White"/> 
          <Ellipse Width="2" Height="2" VerticalAlignment="Bottom" 
           HorizontalAlignment="Center" Margin="0 0 0 2" 
           Fill="White"/> 
         </Grid> 
         <Border BorderBrush="Red" BorderThickness="2" CornerRadius="2"> 
          <AdornedElementPlaceholder/> 
         </Border> 
        </DockPanel> 
       </ControlTemplate> 
      </Setter.Value> 
     </Setter> 
     <Style.Triggers> 
      <Trigger Property="Validation.HasError" Value="true"> 
       <Setter Property="ToolTip" Value="{Binding RelativeSource= 
        {x:Static RelativeSource.Self}, 
        Path=(Validation.Errors)[0].ErrorContent}"/> 
      </Trigger> 
     </Style.Triggers> 
    </Style> 
</Window.Resources> 

<TextBox Name="TxtEmail" Margin="5" Text="{Binding Path=SelectedProvider.Email, 
    UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, Mode=TwoWay}" /> 

Этот подход дает мне разделения проблем, которые я хотел и Ниццу вне коробки Validation. Единственный небольшой недостаток: мне не очень приятно иметь Event-Subscription в VM ...

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

  • Нет связанных вопросов^_^