c# - ListView 绑定(bind)项上的 ValidationRules

标签 c# wpf validation data-binding listview

我的实际情况是这样的:我在主从中设置了一个 ListView 和一个自定义 UserControl。可以通过菜单项添加多个最初无效的项目。

我最终想做的是,如果列表中的任何项目无效,则阻止提交。短期内,我试图为无效的项目提供视觉线索。我的想法是,在 ListViewItem 的附加 Validation.HasError 上针对 ListViewItem 触发器引入一种样式属性触发整行的背景变为红色。

为了执行此操作,我当然添加了样式,并引入了一个简单的验证规则,我在 GridViewColumnDisplayMemberBinding 中使用了该规则。我已使用调试器验证规则正在调用,并且规则按预期运行,但我没有看到样式更改。

我在复制品中包含了以下所有相关部分。我很感激这里的任何帮助。我应该注意,该按钮总是生成一个带有“有效!”的消息框。与文本一样,尽管调试器显示失败的规则被命中。

我也在使用 .Net 3.5 SP1。

Person.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ListViewItemValidation
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        static Person[] _Data;
        public static Person[] Data
        {
            get
            {
                if (_Data == null)
                {
                     _Data =new[]{
                        new Person() { Name="John", Age=30},
                        new Person() { Name="Mary", Age=40},
                        new Person() { Name="", Age=20},
                        new Person() { Name="Tim", Age=-1},
                    };
                }
                return _Data;
            }
        }
    }
}

RequiredStringValidator.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Controls;

namespace ListViewItemValidation
{
    public class RequiredStringValidator : ValidationRule
    {
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            if (string.IsNullOrEmpty(value as string))
                return new ValidationResult(false, "String cannot be empty.");

            return ValidationResult.ValidResult;
        }
    }
}

Window1.xaml:

<Window
    x:Class="ListViewItemValidation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:l="clr-namespace:ListViewItemValidation"  
    Title="Window1" Height="300" Width="300">
    <DockPanel>
        <Button Content="Validate"
                DockPanel.Dock="Bottom"
                Click="ValidateClicked"
                />
        <ListView 
            HorizontalAlignment="Stretch"        
            VerticalAlignment="Stretch"
            ItemsSource="{x:Static l:Person.Data}"
            >
            <ListView.Resources>
                <Style TargetType="ListViewItem">
                    <Style.Triggers>
                        <Trigger Property="Validation.HasError" Value="True">
                            <Setter Property="Background" Value="Red" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridView.Columns>
                        <GridViewColumn Header="Name">
                            <GridViewColumn.DisplayMemberBinding>
                                <Binding Path="Name">
                                    <Binding.ValidationRules>
                                        <l:RequiredStringValidator
                                            ValidatesOnTargetUpdated="True"
                                            ValidationStep="RawProposedValue"
                                            />
                                    </Binding.ValidationRules>
                                </Binding>
                            </GridViewColumn.DisplayMemberBinding>
                        </GridViewColumn>
                        <GridViewColumn 
                            Header="Age"
                            DisplayMemberBinding="{Binding Path=Age}"
                            />
                    </GridView.Columns>
                </GridView>
            </ListView.View>
        </ListView>
    </DockPanel>
</Window>

Window1.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ListViewItemValidation
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void ValidateClicked(object sender, RoutedEventArgs e)
        {
            if (Validation.GetHasError(this))
                MessageBox.Show("invalid!");
            else
                MessageBox.Show("valid!");
        }
    }
}

更新:最终解决方案 我添加了以下类来为 ListViewItem 提供附加属性,以检查是否有任何子项包含具有失败的验证规则的绑定(bind)属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace ListViewItemValidation
{
    public class ListViewItemExtensions
    {
        #region ChildrenHaveError Property

        public bool ChildrenHaveError
        {
            get { return (bool)this.ListViewItem.GetValue(ChildrenHaveErrorProperty); }
            set { this.ListViewItem.SetValue(ChildrenHaveErrorProperty, value); }
        }

        public static bool GetChildrenHaveError(ListViewItem obj)
        {
            return EnsureInstance(obj).ChildrenHaveError;
        }

        public static void SetChildrenHaveError(ListViewItem obj, bool value)
        {
            EnsureInstance(obj).ChildrenHaveError = value;
        }

        public static readonly DependencyProperty ChildrenHaveErrorProperty =
            DependencyProperty.RegisterAttached(
                "ChildrenHaveError",
                typeof(bool),
                typeof(ListViewItemExtensions),
                new PropertyMetadata(
                    new PropertyChangedCallback((o, a) => { EnsureInstance((ListViewItem)o); })
                )
        );
        #endregion

        #region ValidatesChildren Property
        public bool ValidatesChildren
        {
            get { return (bool)this.ListViewItem.GetValue(ValidatesChildrenProperty); }
            set { this.ListViewItem.SetValue(ValidatesChildrenProperty, value); }
        }

        public static bool GetValidatesChildren(ListViewItem obj)
        {
            return EnsureInstance(obj).ValidatesChildren;
        }

        public static void SetValidatesChildren(ListViewItem obj, bool value)
        {
            EnsureInstance(obj).ValidatesChildren = value;
        }

        public static readonly DependencyProperty ValidatesChildrenProperty =
            DependencyProperty.RegisterAttached(
                "ValidatesChildren",
                typeof(bool),
                typeof(ListViewItemExtensions),
                new PropertyMetadata(
                    new PropertyChangedCallback((o, a) => { EnsureInstance((ListViewItem)o); })
                )
           );
        #endregion

        #region Instance Property
        public static ListViewItemExtensions GetInstance(ListViewItem obj)
        {
            return (ListViewItemExtensions)obj.GetValue(InstanceProperty);
        }

        public static void SetInstance(ListViewItem obj, ListViewItemExtensions value)
        {
            obj.SetValue(InstanceProperty, value);
        }

        public static readonly DependencyProperty InstanceProperty =
            DependencyProperty.RegisterAttached("Instance", typeof(ListViewItemExtensions), typeof(ListViewItemExtensions));
        #endregion

        #region ListViewItem Property
        public ListViewItem ListViewItem { get; private set; }
        #endregion

        static ListViewItemExtensions EnsureInstance(ListViewItem item)
        {
            var i = GetInstance(item);
            if (i == null)
            {
                i = new ListViewItemExtensions(item);
                SetInstance(item, i);
            }
            return i;
        }

        ListViewItemExtensions(ListViewItem item)
        {
            if (item == null)
                throw new ArgumentNullException("item");

            this.ListViewItem = item;
            item.Loaded += (o, a) =>
            {
                this.FindBindingExpressions(item);
                this.ChildrenHaveError = ComputeHasError(item);
            };
        }

        static bool ComputeHasError(DependencyObject obj)
        {
            var e = obj.GetLocalValueEnumerator();

            while (e.MoveNext())
            {
                var entry = e.Current;

                if (!BindingOperations.IsDataBound(obj, entry.Property))
                    continue;

                var binding = BindingOperations.GetBinding(obj, entry.Property);
                foreach (var rule in binding.ValidationRules)
                {
                    ValidationResult result = rule.Validate(obj.GetValue(entry.Property), null);
                    if (!result.IsValid)
                    {
                        BindingExpression expression = BindingOperations.GetBindingExpression(obj, entry.Property);
                        Validation.MarkInvalid(expression, new ValidationError(rule, expression, result.ErrorContent, null));
                        return true;
                    }
                }
            }

            for (int i = 0, count = VisualTreeHelper.GetChildrenCount(obj); i < count; ++i)
                if (ComputeHasError(VisualTreeHelper.GetChild(obj, i)))
                    return true;

            return false;
        }

        void OnDataTransfer(object sender, DataTransferEventArgs args)
        {
            this.ChildrenHaveError = ComputeHasError(this.ListViewItem);
        }

        void FindBindingExpressions(DependencyObject obj)
        {
            var e = obj.GetLocalValueEnumerator();

            while (e.MoveNext())
            {
                var entry = e.Current;
                if (!BindingOperations.IsDataBound(obj, entry.Property))
                    continue;

                Binding binding = BindingOperations.GetBinding(obj, entry.Property);
                if (binding.ValidationRules.Count > 0)
                {
                    Binding.AddSourceUpdatedHandler(obj, new EventHandler<DataTransferEventArgs>(this.OnDataTransfer));
                    Binding.AddTargetUpdatedHandler(obj, new EventHandler<DataTransferEventArgs>(this.OnDataTransfer));
                }
            }

            for (int i = 0, count = VisualTreeHelper.GetChildrenCount(obj); i < count; ++i)
            {
                var child = VisualTreeHelper.GetChild(obj, i);
                this.FindBindingExpressions(child);
            }
        }

    }
}

然后,我将 ListViewItem 样式修改为:

        <Style TargetType="ListViewItem">
            <Style.Setters>
                <Setter Property="l:ListViewItemExtensions.ValidatesChildren" Value="True" />
            </Style.Setters>
            <Style.Triggers>
                <Trigger Property="l:ListViewItemExtensions.ChildrenHaveError" Value="True">
                    <Setter Property="Background" Value="Red" />
                </Trigger>
            </Style.Triggers>
        </Style>

非常感谢@Quartermeister 帮助我解决了这个问题。

最佳答案

Validation.HasError 仅在单个单元格的 TextBlock 上设置,因为这是应用绑定(bind)的地方。这是 ListViewItem 的子项之一,但不是 ListViewItem 本身。它也没有在窗口上设置,这就是为什么您的消息框总是显示“有效!”。

当一个单元格验证失败时,您可以用来突出显示整行的一种方法是设置 ValidationAdornerSite让单元格成为它的行。这将导致 ErrorTemplate用于要应用的 ListViewItem,默认情况下会给它一个红色边框。尝试添加这样的样式:

<Style TargetType="TextBlock">
    <Setter
        Property="Validation.ValidationAdornerSite"
        Value="{Binding RelativeSource={RelativeSource AncestorType=ListViewItem}}"/>
</Style>

关于c# - ListView 绑定(bind)项上的 ValidationRules,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3314299/

相关文章:

c# - 在 WCF 服务中添加自定义构造函数并在客户端(asp.net 或 Windows 窗体)上访问它

c# - 什么是 MvcHtmlString 以及何时应该使用它?

c# - 修复 xaml 文件中的大小

java - 使用 Java Nashorn 引擎仅验证 javascript

javascript - 正则表达式未按预期运行

php - 如何创建不允许使用符号的正则表达式?

c# - 单击按钮后应如何隐藏 AppBars?

c# - 如何下载网址中没有文件名和文件扩展名的文件

c# - 将网格添加到 ItemsControl 的 ItemsPanelTemplate

wpf - (WPF) 将 OneWayToSource 与转换器绑定(bind)会导致立即异常