我有一个WPF UserControl,里面有很多其他控件。 TextBoxes就是其中之一。 每个TextBox都有自己的验证:
<TextBox>
<TextBox.Text>
<Binding Path="MyPath" StringFormat="{}{0:N}" NotifyOnValidationError="True">
<Binding.ValidationRules>
<r:MyValidationRule ValidationType="decimal" />
</Binding.ValidationRules>
</Binding>
<TextBox.Text>
<TextBox>
一
现在假设用户在其中键入了一些无效字符。它们都会变成红色。
现在我想重置所有验证错误(来自错误输入)和设置最近正确的值来自DataContext
。
我在构造函数中设置了DataContext,我不想更改它(DataContext = null对我没用):
DataContext = _myDataContext = new MyDataContext(..);
我已经找到的是这些课程:
Validation.ClearInvalid(..)
BindingExpression.UpdateTarget();
我认为这些课程可以帮助我,但是他们需要具体Binding
的{{1}},而我想为所有这些课程全球化。
我是否应该遍历 Visual Tree (这实际上是我不喜欢的)或者是否有更好的解决方案呢?
答案 0 :(得分:2)
这就是BindingGroup的用途......你要在所有控件的容器上设置一个BindingGroup,例如:包含它们的面板。这将导致DataContext的更新被保持,直到您在BindingGroup上调用UpdateSources。如果要重置用户的输入,则需要调用CancelEdit,BindingGroup会将容器内的所有控件重置为DataContext的(仍未更改)值。
答案 1 :(得分:1)
为什么不为数据源的所有属性触发NotifyPropertyChanged?这将更新绑定和UI控件应该从datacontext获取值(这是有效的,因此验证错误将被清除)?
答案 2 :(得分:1)
我遇到了同样的问题。页面上有多个经过验证的控件。我发现/使这个解决方案更新(并清除所有验证)DependencyObject的后代:
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
/// <summary>
/// Updates all binding targets where the data item is of the specified type.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <param name="dataItemType">Type of the data item.</param>
/// <param name="clearInvalid">Clear validation errors from binding.</param>
public static void UpdateAllBindingTargets(this DependencyObject root, int depth, Type dataItemType, bool clearInvalid)
{
var bindingExpressions = EnumerateDescendentsBindingExpressions(root, depth);
foreach (BindingExpression be in bindingExpressions.Where(be => be.DataItem != null && be.DataItem.GetType() == dataItemType))
{
if (be != null)
{
be.UpdateTarget();
if (clearInvalid)
System.Windows.Controls.Validation.ClearInvalid(be);
}
}
}
/// <summary>
/// Enumerates all binding expressions on descendents.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
/// <returns></returns>
public static IEnumerable<BindingExpression> EnumerateDescendentsBindingExpressions(this DependencyObject root, int depth)
{
return root.EnumerateDescendents(depth).SelectMany(obj => obj.EnumerateBindingExpressions());
}
/// <summary>
/// Enumerates the descendents of the specified root to the specified depth.
/// </summary>
/// <param name="root">The root.</param>
/// <param name="depth">The depth.</param>
public static IEnumerable<DependencyObject> EnumerateDescendents(this DependencyObject root, int depth)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
if (depth > 0)
{
foreach (var descendent in EnumerateDescendents(child, --depth))
yield return descendent;
}
}
}
/// <summary>
/// Enumerates the binding expressions of a Dependency Object.
/// </summary>
/// <param name="element">The parent element.</param>
public static IEnumerable<BindingExpression> EnumerateBindingExpressions(this DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
LocalValueEnumerator lve = element.GetLocalValueEnumerator();
while (lve.MoveNext())
{
LocalValueEntry entry = lve.Current;
if (BindingOperations.IsDataBound(element, entry.Property))
{
if (entry.Value is PriorityBindingExpression)
{
foreach (BindingExpression expr in ((PriorityBindingExpression)entry.Value).BindingExpressions)
yield return expr;
}
else if (entry.Value is MultiBindingExpression)
{
foreach (BindingExpression expr in ((MultiBindingExpression)entry.Value).BindingExpressions)
yield return expr;
}
else
yield return entry.Value as BindingExpression;
}
}
}
答案 3 :(得分:0)
我不确定你的意思
我在构造函数中设置了DataContext,我不想更改它 (DataContext = null对我不起帮助)
通常要重置表单上的所有绑定,请执行以下操作:(假设有一个用于views / viewmodel连接的控制器,否则只需在视图上使用代码隐藏。)
var dataContext = view.DataContext;
view.DataContext = null;
view.DataContext = dataContext;
它不会将其更改为新的数据上下文,只是删除数据上下文并重新加载它。这将启动所有绑定以重新加载。
答案 4 :(得分:0)
尽管 hbarck 给出了完全正确的答案,但我想补充一点,对于许多标准 WPF 控件,BindingGroups 是自动创建的。因此,在大多数情况下,以下简单代码足以清除某些控件(例如 DataGrid)内的所有验证错误:
foreach (var bg in BindingOperations.GetSourceUpdatingBindingGroups(myDataGrid))
bg.CancelEdit();