Я хочу проверить мои DataGrid
строки на наличие ошибок, и если есть какие-либо ошибки, то кнопка «Сохранить» должна быть отключена или, по крайней мере, должно быть какое-то сообщение о том, что есть ошибки.
После некоторых исследований я наткнулся на следующий пост: Обнаружение ошибок валидации WPF
В посте есть решение использовать этот фрагмент кода для проверки DataGrid на ошибки:
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = IsValid(sender as DependencyObject);
}
private bool IsValid(DependencyObject obj)
{
// The dependency object is valid if it has no errors and all
// of its children (that are dependency objects) are error-free.
return !Validation.GetHasError(obj) &&
LogicalTreeHelper.GetChildren(obj)
.OfType<DependencyObject>()
.All(IsValid);
}
Однако я использую реализацию ICommand
для своей кнопки, и я не могу понять, как реализовать с ней функцию IsValid
.Я попробовал несколько вещей с регистрацией DependencyObject
, используя Binding
и некоторые другие вещи.
Может ли кто-нибудь указать мне правильное направление, это должно быть довольно просто, я думаю, но я просто не могу обойти это.
Ниже реализации моей кнопки:
public class MyViewModel
{
public ICommand MyCommandButton { get; set; }
public MyViewModel()
{
MyCommandButton = new BaseCommand(MyCommandFunction);
this.Initialize();
}
private void MyCommandFunction(object obj)
{
//... some (not yet implemented) logic
}
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
: this(method, null)
{
}
public BaseCommand(Action<object> method, Predicate<object> canExecute)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
}
Кнопка XAML:
<Button
Name="MyButton"
Command="{Binding MyCommandButton}"
/>
ОБНОВЛЕНИЕ
После еще одного исследования (Object parameter
из MyCommandCanExecuteFunction
в примере @RajN возвращало null
каждый раз) я наткнулся на следующий пост: отправитель объекта всегда равен нулю в RelayCommand , в котором говорится, что яследует использовать CommandParameter
, чтобы оно не было null
.Теперь функция IsValid
работает, но продолжает возвращать TRUE (поэтому она действительна), даже если в datagrid
.
есть подозрения, что я подозреваю, что с самой сеткой данных / данными что-то не так, поэтому я опубликуюа также ниже:
DataGrid
<DataGrid x:Name="MainGrid"
ItemsSource="{Binding ItemList}"
SelectedItem="{Binding SelectedItem,Converter={StaticResource ignoreNewItemPlaceHolderConverter}}"
AutoGenerateColumns="False"
DataContextChanged="OnMainGridDataContextChanged"
CanUserAddRows="False"
>
<DataGrid.Columns>
<DataGridTextColumn>
<DataGridTextColumn.Header>
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
</Grid.ColumnDefinitions>
<Button
Height="25"
Width="25"
Style="{StaticResource MaterialDesignFloatingActionMiniDarkButton}"
Command="{Binding Path=DataContext.AddRowCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
ToolTip="Voeg regel toe"
>
<materialDesign:PackIcon
Kind="Add"
Height="24"
Width="24" />
</Button>
</Grid>
</DataGridTextColumn.Header>
</DataGridTextColumn>
<DataGridTextColumn Binding="{Binding SequenceNumber}"
Header="Line"
EditingElementStyle="{StaticResource MaterialDesignDataGridTextColumnEditingStyle}"
Width="63"
IsReadOnly="true" />
<DataGridTextColumn Header="Width" Width="100" IsReadOnly="false" EditingElementStyle="{StaticResource errTemplate}" >
<DataGridTextColumn.Binding>
<Binding Path="Width" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<validationbinding1:RequiredRule ValidatesOnTargetUpdated="True"/>
<validationbinding1:NumericRule ValidatesOnTargetUpdated="True" />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
<DataGridTextColumn Header="Height" Width="100" IsReadOnly="false" EditingElementStyle="{StaticResource errTemplate}" >
<DataGridTextColumn.Binding>
<Binding Path="Height" ValidatesOnDataErrors="True" NotifyOnValidationError = "True">
<Binding.ValidationRules>
<validationbinding1:RequiredRule />
<validationbinding1:NumericRule />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
~~Some other columns which are left out
</DataGrid.Columns>
</DataGrid>
Свойства и привязки DataGrid:
Вкл.само представление
private void OnMainGridDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
m_MyViewModel = (m_MyViewModel)this.DataContext;
}
В ViewModel
public class MyViewModel : MyModel
{
// Property variables
private ObservableCollection<ItemListDetails> p_ItemList;
public ICommand MyCommandButton { get; set; }
public MyViewModel()
{
MyCommandButton = new BaseCommand(MyCommandFunction, canExecute);
this.Initialize();
}
private bool canExecute (object obj)
{
return IsValid(obj as DependencyObject);
}
private bool IsValid(DependencyObject obj)
{
// The dependency object is valid if it has no errors and all
// of its children (that are dependency objects) are error-free.
if (obj == null)
return true;
return !Validation.GetHasError(obj) &&
LogicalTreeHelper.GetChildren(obj)
.OfType<DependencyObject>()
.All(IsValid);
}
private void MyCommandFunction(object obj)
{
//... some (not yet implemented) logic
}
private void AddRow(object obj)
{
ItemListDetails Item = new ItemListDetails
{
Width = 0,
Height = 0,
};
p_ItemList.Add(Item);
}
public ObservableCollection<ItemListDetails> ItemList
{
get { return p_ItemList; }
set
{
p_ItemList = value;
this.MutateVerbose(ref p_ItemList, value, RaisePropertyChanged());
}
}
/// <summary>
/// The currently-selected item.
/// </summary>
public ItemListDetails SelectedItem { get; set; }
/// <summary>
/// Updates the ItemCount Property when the list collection changes.
/// </summary>
void OnListChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
// Update item count
this.ItemCount = this.p_ItemList.Count;
// Resequence list
SequencingService.SetCollectionSequence(this.p_ItemList);
}
/// <summary>
/// Initializes this application.
/// </summary>
private void Initialize()
{
// Create item list
p_ItemList = new ObservableCollection<ItemListDetails>();
// Subscribe to CollectionChanged event
p_ItemList.CollectionChanged += OnListChanged;
// Initialize list index
this.p_ItemList = SequencingService.SetCollectionSequence(this.p_ItemList);
// Update bindings
//base.RaisePropertyChangedEvent("GroceryList");
//this.MutateVerbose(ref _materiaal, value, RaisePropertyChanged());
}
}
Класс ItemListDetails элементов DataGrid
public class ItemListDetails : ObservableObject, ISequencedObject
{
// Property variables
private int p_SequenceNumber;
private int p_Width;
private int p_Height;
/// <summary>
/// Default constructor
/// </summary>
public ItemListDetails ()
{
}
/// <summary>
/// Paramterized constructor.
/// </summary>
public ItemListDetails (int width, int height, int itemIndex)
{
p_Width = width;
p_Height = height;
p_SequenceNumber = itemIndex;
}
/// <summary>
/// The sequential position of this item in a list of items.
/// </summary>
public int SequenceNumber
{
get { return p_SequenceNumber; }
set
{
p_SequenceNumber = value;
base.RaisePropertyChangedEvent("SequenceNumber");
}
}
/// <summary>
/// The width
/// </summary>
public int Width
{
get { return p_Width; }
set
{
p_Width = value;
base.RaisePropertyChangedEvent("Int");
}
}
/// <summary>
/// The height
/// </summary>
public int Height
{
get { return p_Height; }
set
{
p_Height = value;
base.RaisePropertyChangedEvent("Int");
}
}
}
Класс MyModel
содержит некоторые другие поля, которые не содержатся в DataGrid
.