使用自定义ICommand实现在按钮单击时验证网格行-WPF

时间:2018-12-24 15:05:44

标签: wpf datagrid icommand

我想验证我的DataGrid行是否有错误,如果存在任何错误,则应禁用“保存”按钮,或者至少应该有一些消息指出存在错误。

经过一番研究,我偶然发现了以下帖子:Detecting WPF Validation Errors

该帖子提供了解决方案,可以使用这段代码来验证错误的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}"
/>

更新

经过更多研究(@RajN示例中的Object parameter中的MyCommandCanExecuteFunction每次都返回null),我偶然发现了以下帖子:object sender is always null in RelayCommand,其中指出我应该使用CommandParameter,因为它不是null。 现在,IsValid函数可以使用,但是即使datagrid上有错误,也仍会返回TRUE(因此它是有效的)。

我怀疑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());

    }

}

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中未包含的其他一些字段。

1 个答案:

答案 0 :(得分:0)

你在这里

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);
    }
}
相关问题