Отключить кнопку сохранения в WPF, если проверка не завершена
Я принял то, что кажется стандартным способом проверки текстовых полей в WPF с использованием интерфейса и стилей IDataErrorInfo, как показано ниже. Однако как отключить кнопку "Сохранить", когда страница становится недействительной? Это как-то происходит через триггеры?
Default Public ReadOnly Property Item(ByVal propertyName As String) As String Implements IDataErrorInfo.Item
Get
Dim valid As Boolean = True
If propertyName = "IncidentCategory" Then
valid = True
If Len(IncidentCategory) = 0 Then
valid = False
End If
If Not valid Then
Return "Incident category is required"
End If
End If
Return Nothing
End Get
End Property
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="3" />
<Setter Property="Height" Value="23" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="MyAdorner" />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
Ответы
Ответ 1
Несколько вещей:
Во-первых, я бы рекомендовал использовать RoutedCommand ApplicationCommands.Save
для реализации обработки кнопки сохранения.
Если вы не проверили модель командной строки WPF, вы можете получить здесь .
<Button Content="Save" Command="Save">
Теперь, чтобы реализовать функциональность, вы можете добавить привязку команды к Window/UserControl или к самой кнопке:
<Button.CommandBindings>
<CommandBinding Command="Save"
Executed="Save_Executed" CanExecute="Save_CanExecute"/>
</Button.CommandBindings>
</Button>
Внесите их в код позади:
private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
}
private void Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
В Save_CanExecute
установите e.CanExecute
в зависимости от действительности привязки в текстовом поле.
Если вы хотите реализовать с использованием шаблона проектирования MVVM (Model-View-ViewModel), посмотрите сообщение Джоша Смита на CommandSinkBinding.
Последнее замечание: если вы хотите, чтобы активировать/отключить обновление, как только изменилось значение в TextBox
, установите UpdateSourceTrigger="PropertyChanged"
на привязку для TextBox
.
EDIT: Если вы хотите проверить/недействить на основе всех привязок в элементе управления, вот несколько советов.
1) Вы уже используете IDataErrorInfo
. Попробуйте реализовать свойство IDataErrorInfo.Error
, чтобы он возвращал строку, которая является недопустимой для всех свойств, к которым вы привязываетесь. Это будет работать только в том случае, если весь ваш элемент управления привязан к одному объекту данных. Установите e.CanExecute = string.IsNullOrEmpty(data.Error);
2) Используйте отражение, чтобы получить все статические свойства DependencyProperties для соответствующих элементов управления. Затем вызовите BindingOperations.GetBindingExpression(relevantControl, DependencyProperty)
в цикле для каждого свойства, чтобы вы могли проверить проверку.
3) В конструкторе вручную создайте коллекцию всех связанных свойств на вложенных элементах управления. В CanExecute выполните итерацию по этой коллекции и подтвердите каждую комбинацию DependencyObject
/DepencyProperty
, используя BindingOperation.GetBindingExpression()
, чтобы получить выражения, а затем изучить BindingExpression.HasError
.
Ответ 2
Я создал прикрепленное свойство только для этого:
public static class DataErrorInfoHelper
{
public static object GetDataErrorInfo(ButtonBase obj)
{
return (object)obj.GetValue(DataErrorInfoProperty);
}
public static void SetDataErrorInfo(ButtonBase obj, object value)
{
obj.SetValue(DataErrorInfoProperty, value);
}
// Using a DependencyProperty as the backing store for DataErrorInfo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataErrorInfoProperty =
DependencyProperty.RegisterAttached("DataErrorInfo", typeof(object), typeof(DataErrorInfoHelper), new PropertyMetadata(null, OnDataErrorInfoChanged));
private static void OnDataErrorInfoChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var button = d as ButtonBase;
if (button.Tag == null)
button.Tag = new DataErrorInfoContext { Button = button };
var context = button.Tag as DataErrorInfoContext;
if(e.OldValue != null)
{
PropertyChangedEventManager.RemoveHandler(((INotifyPropertyChanged)e.OldValue), context.Handler, string.Empty);
}
var inotify = e.NewValue as INotifyPropertyChanged;
if (inotify != null)
{
PropertyChangedEventManager.AddHandler(inotify, context.Handler, string.Empty);
context.Handler(inotify, new PropertyChangedEventArgs(string.Empty));
}
}
private class DataErrorInfoContext
{
public ButtonBase Button { get; set; }
public void Handler(object sender, PropertyChangedEventArgs e)
{
var dei = sender as IDataErrorInfo;
foreach (var property in dei.GetType().GetProperties())
{
if (!string.IsNullOrEmpty(dei[property.Name]))
{
Button.IsEnabled = false;
return;
}
}
Button.IsEnabled = string.IsNullOrEmpty(dei.Error);
}
}
}
Я использую его как это в моих формах:
<TextBlock Margin="2">e-mail:</TextBlock>
<TextBox Margin="2" Text="{Binding Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<!-- other databindings--->
<Button Margin="2" local:DataErrorInfoHelper.DataErrorInfo="{Binding}" Commands="{Binding SaveCommand}">Create account</Button>