Поскольку я заметил, что эту проблему все еще трудно решить в очень общей форме, я попробовал различные решения.
В конце концов, тот, который сработал для меня:
Всякий раз, когда есть необходимость, чтобы изменения пользовательского интерфейса были проверены и обновлены до его источников (проверка изменений при закрытии окна, выполнении операций сохранения, ...), я вызываю функцию проверки, которая выполняет различные действия:
- убедитесь, что сфокусированный элемент (например, текстовое поле, выпадающий список, ...) теряет фокус, что вызывает поведение источника обновлений по умолчанию
- проверить все элементы управления в дереве объекта DependencyObject, который передается в функцию проверки
- вернуть фокус на исходный фокусированный элемент
Сама функция возвращает true, если все в порядке (проверка прошла успешно) -> ваше первоначальное действие (закрытие с необязательным запросом подтверждения, сохранение, ...) может продолжаться. В противном случае функция вернет значение false, и ваше действие не может быть продолжено, поскольку имеются ошибки валидации для одного или нескольких элементов (с помощью универсального шаблона ErrorTemplate для элементов).
Код (функциональность проверки основана на статье Обнаружение ошибок проверки WPF ):
public static class Validator
{
private static Dictionary<String, List<DependencyProperty>> gdicCachedDependencyProperties = new Dictionary<String, List<DependencyProperty>>();
public static Boolean IsValid(DependencyObject Parent)
{
// Move focus and reset it to update bindings which or otherwise not processed until losefocus
IInputElement lfocusedElement = Keyboard.FocusedElement;
if (lfocusedElement != null && lfocusedElement is UIElement)
{
// Move to previous AND to next InputElement (if your next InputElement is a menu, focus will not be lost -> therefor move in both directions)
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Previous));
(lfocusedElement as UIElement).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
Keyboard.ClearFocus();
}
if (Parent as UIElement == null || (Parent as UIElement).Visibility != Visibility.Visible)
return true;
// Validate all the bindings on the parent
Boolean lblnIsValid = true;
foreach (DependencyProperty aDependencyProperty in GetAllDependencyProperties(Parent))
{
if (BindingOperations.IsDataBound(Parent, aDependencyProperty))
{
// Get the binding expression base. This way all kinds of bindings (MultiBinding, PropertyBinding, ...) can be updated
BindingExpressionBase lbindingExpressionBase = BindingOperations.GetBindingExpressionBase(Parent, aDependencyProperty);
if (lbindingExpressionBase != null)
{
lbindingExpressionBase.ValidateWithoutUpdate();
if (lbindingExpressionBase.HasError)
lblnIsValid = false;
}
}
}
if (Parent is Visual || Parent is Visual3D)
{
// Fetch the visual children (in case of templated content, the LogicalTreeHelper will return no childs)
Int32 lintVisualChildCount = VisualTreeHelper.GetChildrenCount(Parent);
for (Int32 lintVisualChildIndex = 0; lintVisualChildIndex < lintVisualChildCount; lintVisualChildIndex++)
if (!IsValid(VisualTreeHelper.GetChild(Parent, lintVisualChildIndex)))
lblnIsValid = false;
}
if (lfocusedElement != null)
lfocusedElement.Focus();
return lblnIsValid;
}
public static List<DependencyProperty> GetAllDependencyProperties(DependencyObject DependencyObject)
{
Type ltype = DependencyObject.GetType();
if (gdicCachedDependencyProperties.ContainsKey(ltype.FullName))
return gdicCachedDependencyProperties[ltype.FullName];
List<DependencyProperty> llstDependencyProperties = new List<DependencyProperty>();
List<FieldInfo> llstFieldInfos = ltype.GetFields(BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Static).Where(Field => Field.FieldType == typeof(DependencyProperty)).ToList();
foreach (FieldInfo aFieldInfo in llstFieldInfos)
llstDependencyProperties.Add(aFieldInfo.GetValue(null) as DependencyProperty);
gdicCachedDependencyProperties.Add(ltype.FullName, llstDependencyProperties);
return llstDependencyProperties;
}
}