Я создал свой UserControl
, названный PersonNameControl
, который предполагается использовать повторно. Элемент управления имеет три поля TextBox
и три свойства зависимостей в файле класса.
Каждое значение свойства зависимости связано с полем, поэтому свойство зависимости Имя связано с Имя TextBox
и т. Д.
I явно не устанавливал явно DataContext из UserControl. Контроль должен быть максимально свободным. Он должен получать свои значения (для полей) только через свои свойства зависимостей. Это даже не должно выглядеть как DataContext.
<UserControl x:Class="WpfApplication1.PersonNameControl">
<StackPanel>
<Label>Firstname:</Label>
<TextBox Text="{Binding Firstname, Mode=TwoWay,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
</TextBox>
<Label>Insertion:</Label>
<TextBox Text="{Binding Insertion, Mode=TwoWay,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
</TextBox>
<Label>Lastname:</Label>
<TextBox Text="{Binding Lastname, Mode=TwoWay,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
</TextBox>
</StackPanel>
</UserControl>
И класс элемента управления:
public partial class PersonNameControl : UserControl
{
public PersonNameControl()
{
InitializeComponent();
}
public string Firstname
{
get { return (string)GetValue(FirstnameProperty); }
set { SetValue(FirstnameProperty, value); }
}
public static readonly DependencyProperty FirstnameProperty =
DependencyProperty.Register("Firstname", typeof(string), typeof(PersonNameControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Insertion
{
get { return (string)GetValue(InsertionProperty); }
set { SetValue(InsertionProperty, value); }
}
public static readonly DependencyProperty InsertionProperty =
DependencyProperty.Register("Insertion", typeof(string), typeof(PersonNameControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Lastname
{
get { return (string)GetValue(LastnameProperty); }
set { SetValue(LastnameProperty, value); }
}
public static readonly DependencyProperty LastnameProperty =
DependencyProperty.Register("Lastname", typeof(string), typeof(PersonNameControl),
new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
Элемент управления предполагается использовать внутри другого представления следующим образом:
<!--
Here we are inside a view or some other control.
The bindings here provide the dependency properties of the UserControl with a value.
The DataContext of the view where my UserControl is used, is a ViewModel that implements INotifyDataErrorInfo
-->
<myControls:PersonNameControl
Firstname="{Binding SomeFirstnameFromVM, Mode=TwoWay}"
Insertion="{Binding SomeInsertionFromVM, Mode=TwoWay}"
Lastname="{Binding SomeLastnameFromVM, Mode=TwoWay}">
</myControls:PersonNameControl>
Когда ViewModel (реализует INotifyDataErrorInfo) создал ошибку проверки, с моим PersonNameControl
UserControl ничего не происходит. Мне удалось создать независимый элемент управления, поскольку он не полагается на специфицированный c DataContext, не устанавливает собственный DataContext в своем файле codebehind и просто получает его значения через свойства зависимостей. Значения обмениваются через привязки и отображаются, но ошибки проверки не отображаются. Я хочу передать ошибки проверки через UserControl.
В некоторых решениях inte rnet используется ValidationAdornerSite
, и я попробовал это. Но это сработало бы только для одного TextBox
.
. Я не вижу никакого решения, если бы мой контроль не зависел от внешнего мира или не вводил уродливые дополнительные свойства, чтобы сделать его громоздким. Я думал, что ошибки «туннелируются» как часть информации через все привязки к последнему уровню, на который поступает значение. Но, похоже, это неправильное соображение.
Редактировать:
Я добавил свой класс ViewModel.
public class CustomerFormViewModel : ViewModelBase, INotifyDataErrorInfo
{
protected string _clientNumber;
protected DateTime _date;
protected string _firstname;
protected string _insertion;
protected string _lastname;
protected Address _address;
protected ObservableCollection<Email> _emails;
protected ObservableCollection<PhoneNumber> _phoneNumbers;
protected string _note;
protected bool _hasErrors;
protected IList<ValidationFailure> _validationErrors;
public IList<ValidationFailure> ValidationErrors
{
get { return _validationErrors; }
set { _validationErrors = value; OnPropertyChanged("ValidationErrors"); }
}
public string ClientNumber
{
get { return _clientNumber; }
set { _clientNumber = value; OnPropertyChanged("ClientNumber"); }
}
public DateTime Date
{
get { return _date; }
set { _date = value; OnPropertyChanged("Date"); }
}
public string Firstname
{
get { return _firstname; }
set { _firstname = value; OnPropertyChanged("Firstname"); }
}
public string Insertion
{
get { return _insertion; }
set { _insertion = value; OnPropertyChanged("Insertion"); }
}
public string Lastname
{
get { return _lastname; }
set { _lastname = value; OnPropertyChanged("Lastname"); }
}
public Address Address
{
get { return _address; }
set { _address = value; OnPropertyChanged("Address"); }
}
public ObservableCollection<Email> Emails
{
get { return _emails; }
set { _emails = value; OnPropertyChanged("Emails"); }
}
public ObservableCollection<PhoneNumber> PhoneNumbers
{
get { return _phoneNumbers; }
set { _phoneNumbers = value; OnPropertyChanged("PhoneNumbers"); }
}
public string Note
{
get { return _note; }
set { _note = value; OnPropertyChanged("Note"); }
}
private DelegateCommand _saveCustomerCommand;
public DelegateCommand SaveCustomerCommand
{
get { return _saveCustomerCommand; }
private set { _saveCustomerCommand = value; OnPropertyChanged("SaveCustomerCommand"); }
}
public CustomerFormViewModel()
{
ValidationErrors = new List<ValidationFailure>();
SaveCustomerCommand = new DelegateCommand(SaveCustomer, CanSaveCustomer);
}
protected void ValidateInput()
{
ValidationErrors.Clear();
CustomerFormValidator validator = new CustomerFormValidator();
FluentValidation.Results.ValidationResult result = validator.Validate(this);
ValidationErrors = result.Errors;
foreach (ValidationFailure f in ValidationErrors)
{
Console.WriteLine(f.ErrorMessage);
}
_hasErrors = result.Errors.Count != 0;
List<string> vmProperties = new List<string>() { "Firstname", "Lastname", "Address", "ClientNumber", "Date" };
foreach (string propertyName in vmProperties)
{
OnErrorsChanged(propertyName);
}
}
public bool HasErrors
{
get { return _hasErrors; }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
protected void OnErrorsChanged(string name)
{
ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(name));
}
public IEnumerable GetErrors(string propertyName)
{
return ValidationErrors.Where<ValidationFailure>(x => x.PropertyName == propertyName);
}
public void SaveCustomer(object parameter)
{
this.ValidateInput();
if( ! HasErrors)
{
Customer customer = new Customer(-1, ClientNumber, Date, Firstname, Insertion, Lastname, Address);
ICustomerRepository repo = new CustomerRepository();
bool res = repo.SaveCustomer(customer);
if(res) {
// ...
}
// ...
} else
{
MessageBox.Show("One or more fields are not filled in correctly.", "Invalid input", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public bool CanSaveCustomer(object parameter)
{
return true;
}
}