Skip to content

Commit

Permalink
Fixing validation adorner for not yet visible controls (#1167)
Browse files Browse the repository at this point in the history
* Fixing validation adorner for not yet visible controls

* Using "is" pattern

* Moving visibility check to ShowValidationAdornerOperation
  • Loading branch information
batzen authored Apr 27, 2023
1 parent 68ba2fa commit e80b23b
Showing 1 changed file with 61 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,6 @@ private static void OnValidationAdornerSiteForChanged(DependencyObject d, Depend
}
}


internal static void ShowValidationAdorner(DependencyObject targetElement, bool show)
{
// If the element has a VisualStateGroup for validation, then dont show the Adorner
Expand All @@ -363,7 +362,6 @@ internal static void ShowValidationAdorner(DependencyObject targetElement, bool
}
}


private static bool HasValidationGroup(FrameworkElement fe)
{
if (fe != null)
Expand Down Expand Up @@ -415,60 +413,86 @@ private static object ShowValidationAdornerOperation(object arg)
DependencyObject adornerSite = (DependencyObject)args[1];
bool show = (bool)args[2];

ShowValidationAdornerHelper(targetElement, adornerSite, show, false);
// Check if the element is visible, if not try to show the adorner again once it gets visible.
// This is needed because controls hosted in Expander or TabControl don't have a parent/AdornerLayer till the Expander is expanded or the TabItem is selected.
if (adornerSite is UIElement { IsVisible: false } siteUIElement)
{
siteUIElement.IsVisibleChanged += ShowValidationAdornerWhenAdornerSiteGetsVisible;
}
else
{
ShowValidationAdornerHelper(targetElement, adornerSite, show, false);
}

return null;
}

private static void ShowValidationAdornerHelper(DependencyObject targetElement, DependencyObject adornerSite, bool show, bool tryAgain)
private static void ShowValidationAdornerWhenAdornerSiteGetsVisible(object sender, DependencyPropertyChangedEventArgs e)
{
UIElement siteUIElement = adornerSite as UIElement;
if (sender is not UIElement adornerSite)
{
return;
}

adornerSite.IsVisibleChanged -= ShowValidationAdornerWhenAdornerSiteGetsVisible;

if (siteUIElement != null)
DependencyObject targetElement = GetValidationAdornerSiteFor(adornerSite);
if (targetElement == null)
{
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(siteUIElement);
targetElement = adornerSite;
}

if (adornerLayer == null)
{
if (tryAgain)
{
// try again later, perhaps giving layout a chance to create the adorner layer
adornerSite.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new DispatcherOperationCallback(ShowValidationAdornerOperation),
new object[]{targetElement, adornerSite, show});
}
return;
}
ShowValidationAdornerHelper(targetElement, adornerSite, (bool)e.NewValue && GetHasError(targetElement), false);
}

private static void ShowValidationAdornerHelper(DependencyObject targetElement, DependencyObject adornerSite, bool show, bool tryAgain)
{
if (adornerSite is not UIElement siteUIElement)
{
return;
}

TemplatedAdorner validationAdorner = siteUIElement.ReadLocalValue(ValidationAdornerProperty) as TemplatedAdorner;
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(siteUIElement);

if (show && validationAdorner == null)
if (adornerLayer is null)
{
if (tryAgain)
{
// get the template from the site, or from the target element
ControlTemplate validationTemplate = GetErrorTemplate(siteUIElement);
if (validationTemplate == null)
{
validationTemplate = GetErrorTemplate(targetElement);
}
// try again later, perhaps giving layout a chance to create the adorner layer
adornerSite.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
new DispatcherOperationCallback(ShowValidationAdornerOperation),
new object[] { targetElement, adornerSite, BooleanBoxes.Box(show) });
}
return;
}

if (validationTemplate != null)
{
validationAdorner = new TemplatedAdorner(siteUIElement, validationTemplate);
adornerLayer.Add(validationAdorner);
TemplatedAdorner validationAdorner = siteUIElement.ReadLocalValue(ValidationAdornerProperty) as TemplatedAdorner;

siteUIElement.SetValue(ValidationAdornerProperty, validationAdorner);
}
if (show && validationAdorner is null)
{
// get the template from the site, or from the target element
ControlTemplate validationTemplate = GetErrorTemplate(siteUIElement);
if (validationTemplate is null)
{
validationTemplate = GetErrorTemplate(targetElement);
}
else if (!show && validationAdorner != null)

if (validationTemplate is not null)
{
validationAdorner.ClearChild();
adornerLayer.Remove(validationAdorner);
siteUIElement.ClearValue(ValidationAdornerProperty);
validationAdorner = new TemplatedAdorner(siteUIElement, validationTemplate);
adornerLayer.Add(validationAdorner);

siteUIElement.SetValue(ValidationAdornerProperty, validationAdorner);
}
}
else if (!show && validationAdorner is not null)
{
validationAdorner.ClearChild();
adornerLayer.Remove(validationAdorner);
siteUIElement.ClearValue(ValidationAdornerProperty);
}
}


/// <summary>
/// Mark this BindingExpression as invalid. If the BindingExpression has been
/// explicitly marked invalid in this way, then it will remain
Expand Down

0 comments on commit e80b23b

Please sign in to comment.