首页 > 解决方案 > 使用自定义 ICommand 实现验证按钮单击时的网格行 - WPF

问题描述

我想验证我的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,使用 aBinding和其他一些东西。

谁能指出我正确的方向,我想这应该很简单,但我无法理解它。

下面是我的按钮的实现:

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}"
/>

更新

经过更多的研究(@RajN 示例中的每次都返回Object parameter),我偶然发现了以下帖子:object sender always is null in RelayCommand声明我应该使用for it not to be 。现在该函数可以工作,但即使在.MyCommandCanExecuteFunctionnullCommandParameternullIsValiddatagrid

我怀疑数据网格/数据本身有问题,所以我也会在下面发布:

数据网格

<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;
}

在视图模型中

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());

    }

}

DataGrid 项的 ItemListDetails 类

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.

标签: wpfdatagridicommand

解决方案


干得好

public class MyViewModel
{
    public ICommand MyCommandButton { get; set; }


    public MyViewModel()
    {
        MyCommandButton = new BaseCommand(MyCommandFunction, MyCommandCanExecuteFunction);
        this.Initialize();
    }

    private void MyCommandFunction(object obj)
    {
        //... some (not yet implemented) logic
    }
    private bool MyCommandCanExecuteFunction(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.
        return !Validation.GetHasError(obj) &&
        LogicalTreeHelper.GetChildren(obj)
        .OfType<DependencyObject>()
        .All(IsValid);
    }
}

推荐阅读