Handle tab controls when focusing on error

This commit is contained in:
2023-04-14 10:37:15 -04:00
parent 79e9f06678
commit 0dd21c9b88
3 changed files with 139 additions and 130 deletions

View File

@@ -1,25 +0,0 @@
using System.Globalization;
using System.Windows.Controls;
namespace ChrisKaczor.Wpf.Validation
{
public class RequiredValidationRule : ValidationRule
{
public static string GetErrorMessage(object? fieldValue)
{
var errorMessage = string.Empty;
if (fieldValue == null || string.IsNullOrWhiteSpace(fieldValue.ToString()))
errorMessage = "Required";
return errorMessage;
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var error = GetErrorMessage(value);
return !string.IsNullOrEmpty(error) ? new ValidationResult(false, error) : ValidationResult.ValidResult;
}
}
}

View File

@@ -1,13 +1,15 @@
using System.Collections.Generic; using DebounceThrottle;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading;
namespace ChrisKaczor.Wpf.Validation;
namespace ChrisKaczor.Wpf.Validation
{
public class BindingExpressionInfo public class BindingExpressionInfo
{ {
public FrameworkElement FrameworkElement { get; } public FrameworkElement FrameworkElement { get; }
@@ -62,11 +64,12 @@ namespace ChrisKaczor.Wpf.Validation
if (dependencyObject is FrameworkElement frameworkElement) if (dependencyObject is FrameworkElement frameworkElement)
{ {
// Get the list of properties // Get the list of properties
IEnumerable<DependencyProperty> dependencyProperties = (from PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(frameworkElement) IEnumerable<DependencyProperty> dependencyProperties = TypeDescriptor.GetProperties(frameworkElement)
select DependencyPropertyDescriptor.FromProperty(propertyDescriptor) .Cast<PropertyDescriptor>()
into dependencyPropertyDescriptor .Select(DependencyPropertyDescriptor.FromProperty)
where dependencyPropertyDescriptor != null .Where(dependencyPropertyDescriptor => dependencyPropertyDescriptor != null)
select dependencyPropertyDescriptor.DependencyProperty).ToList(); .Select(dependencyPropertyDescriptor => dependencyPropertyDescriptor.DependencyProperty)
.ToList();
// Loop over each dependency property in the list // Loop over each dependency property in the list
foreach (var dependencyProperty in dependencyProperties) foreach (var dependencyProperty in dependencyProperties)
@@ -87,19 +90,26 @@ namespace ChrisKaczor.Wpf.Validation
} }
} }
public static void UpdateAllSources(this DependencyObject window, IEnumerable<BindingExpressionInfo> bindingExpressions) public static void UpdateAllSources(this DependencyObject _, IEnumerable<BindingExpressionInfo> bindingExpressions)
{ {
foreach (var expression in bindingExpressions) foreach (var expression in bindingExpressions)
expression.BindingExpression.UpdateSource(); expression.BindingExpression.UpdateSource();
} }
public static void ClearAllValidationErrors(this DependencyObject window, IEnumerable<BindingExpressionInfo> bindingExpressions) public static void ClearAllValidationErrors(this DependencyObject _, IEnumerable<BindingExpressionInfo> bindingExpressions)
{ {
foreach (var expression in bindingExpressions) foreach (var expression in bindingExpressions)
System.Windows.Controls.Validation.ClearInvalid(expression.BindingExpression); System.Windows.Controls.Validation.ClearInvalid(expression.BindingExpression);
} }
public static bool IsValid(this DependencyObject window) public static bool IsValid(this DependencyObject window)
{
return IsValid(window, null);
}
private static readonly DebounceDispatcher FocusDispatcher = new(50);
public static bool IsValid(this DependencyObject window, TabControl? tabControl)
{ {
// Get a list of all framework elements and binding expressions // Get a list of all framework elements and binding expressions
var bindingExpressions = window.GetBindingExpressions(); var bindingExpressions = window.GetBindingExpressions();
@@ -117,10 +127,31 @@ namespace ChrisKaczor.Wpf.Validation
// Get the first framework element with an error // Get the first framework element with an error
var firstErrorElement = bindingExpressions.First(b => b.BindingExpression.HasError).FrameworkElement; var firstErrorElement = bindingExpressions.First(b => b.BindingExpression.HasError).FrameworkElement;
if (tabControl == null)
{
// Set focus // Set focus
firstErrorElement.Focus(); firstErrorElement.Focus();
return false; return false;
} }
// Loop over each tab item
foreach (TabItem tabItem in tabControl.Items)
{
// Cast the content as visual
var content = (Visual) tabItem.Content;
// See if the control with the error is a descendant
if (!firstErrorElement.IsDescendantOf(content))
continue;
// Select the tab
tabItem.IsSelected = true;
}
var dispatcher = Dispatcher.CurrentDispatcher;
FocusDispatcher.Debounce(() => dispatcher.Invoke(() => firstErrorElement.Focus()));
return false;
} }
} }

View File

@@ -14,6 +14,9 @@
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<AssemblyName>ChrisKaczor.Wpf.Validation</AssemblyName> <AssemblyName>ChrisKaczor.Wpf.Validation</AssemblyName>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="DebounceThrottle" Version="2.0.0" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="README.md"> <None Update="README.md">
<Pack>True</Pack> <Pack>True</Pack>