Мне нужно создать узел проверки, который будет возвращать ошибку, если введенное значение уже существует. У меня есть GUI с предметами, которым можно задать имя. Я хочу, чтобы имена были уникальными.
Поэтому для каждой проверки мне нужны следующие два параметра:
- Список всех имен всех элементов или некоторый предикат, который сообщит меня имя существует
- Текущее имя элемента, чтобы исключить его из вышеуказанной проверки (изменение имени на то же значение не должно быть ошибкой)
Контексты данных выглядят так (просто интерфейс для иллюстрации):
class AppMainContext
{
public IEnumerable<string> ItemNames {get;}
public Item SelectedItem {get;}
}
class Item
{
public string Name {get;}
}
Поле в WPF выглядит следующим образом, а его родительский элемент привязан к `{SelectedItem}:
<DockPanel DockPanel.Dock="Top">
<Label Content="Name: "/>
<TextBox DockPanel.Dock="Top">
<TextBox.Text>
<Binding Path="Name" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<vmvalidation:UniqueNameRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DockPanel>
Валидатор выглядит следующим образом:
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Controls;
namespace MyApp.Validation
{
public class UniqueNameRule : ValidationRule
{
public IEnumerable<string> ExistingNames { get; set; }
public string MyName { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if(value is string newValue)
{
// name changed
if(!value.Equals(MyName))
{
if(ExistingNames.Contains(newValue))
{
return new ValidationResult(false, "Name already exists!");
}
}
return new ValidationResult(true, null);
}
else
{
return new ValidationResult(false, "Invalid value type. Is this validator valid for the given field?");
}
}
}
}
Я попытался хотя бы связать текущее имя с валидатором. Текстовое поле уже существует в контексте данных текущих элементов, поэтому правильная привязка будет:
<Binding.ValidationRules>
<vmvalidation:UniqueNameRule MyName="{Binding Name}" />
</Binding.ValidationRules>
За исключением того, что это дает ошибку:
Элемент MyName
не является распознан или недоступен.
Список всех элементов находится в контексте данных windows, доступном через ItemNames
. Я полагаю, к нему можно получить доступ так:
{Binding Path=DataContext.ItemNames, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
Я попытался правильно связать, используя ответ ниже, но затем я получаю ошибку:
Невозможно установить «Связывание» в свойстве «MyName» типа MyProject_Validation_UniqueNameRule_9_468654
. «Привязка» может быть установлена только для свойства DependencyProperty объекта DependencyObject.
Похоже, что привязки вообще не поддерживаются.
Так как я могу собрать это вместе, чтобы Правило проверки может получить доступ к обеим этим переменным?