Мой настоящий сценарий таков: у меня есть ListView
и пользовательские настройки UserControl
в master-detail.С помощью пункта меню можно добавить несколько недействительных элементов.
Что я хотел бы сделать, так это, в конце концов, предотвратить передачу, если какие-либо элементы в списке недействительны.В краткосрочной перспективе я пытаюсь дать визуальный ключ к недействительному предмету.Моя мысль состоит в том, чтобы ввести стиль для триггера ListView
таргетинга ListViewItem
на вложенном свойстве Validation.HasError
ListViewItem
, чтобы фон всей строки стал красным.
Для этого яЯ, конечно, добавил стиль и ввел простое правило проверки, которое я использую в DisplayMemberBinding
из GridViewColumn
.С помощью отладчика я проверил, что правило вызывается и что правило функционирует, как и ожидалось, но я не вижу изменений в стиле.
Я включил все соответствующие части ниже в репродукцию.Буду признателен за любую помощь здесь.Я должен отметить, что кнопка всегда генерирует окно сообщения с «valid!»в качестве текста, несмотря на то, что отладчик показывает сбойное правило, которое ударило.
Я также использую .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, чтобы проверить, не содержат ли какие-либо дочерние элементы связанные свойства с ошибочными правилами проверки:
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 за помощь в этом.