dialog validation support

This commit is contained in:
Pilzinsel64
2025-04-23 12:02:59 +02:00
parent b65af65a26
commit e2d6d98002
11 changed files with 1331 additions and 10 deletions

View File

@@ -0,0 +1,65 @@
//using System.Linq;
using Telerik.WinControls;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
/// <summary>
/// Represent a base for RadValidation
/// </summary>
public interface IRadValidationRuleEx
{
/// <summary>
/// Represents the List of Controls that belongs to this Rule.
/// </summary>
List<Control> Controls { get; set; }
/// <summary>
/// Gets or sets the ToolTip Text.
/// </summary>
string ToolTipText { get; set; }
/// <summary>
/// Gets or sets ToolTip Title.
/// </summary>
string ToolTipTitle { get; set; }
/// <summary>
/// Enable ot disable the ToolTip showing when validation fails.
/// </summary>
bool AutoToolTip { get; set; }
/// <summary>
/// Get the Rule Expression e.g. 'Value > 5'
/// </summary>
string Expression { get; }
/// <summary>
/// Sets or sets the PropertyName which will be evaluated. For example 'Value' Property.
/// </summary>
string PropertyName { get; set; }
/// <summary>
/// Add a RadControl descendant to the Rule's Controls collection.
/// </summary>
/// <param name="control">RadControl descendant instance</param>
void AddControl(RadControl control);
/// <summary>
/// Remove a RadControl descendant from the Rule's Controls collection.
/// </summary>
/// <param name="control">RadControl descendant instance</param>
void RemoveControl(RadControl control);
string ToString();
/// <summary>
/// The Value of the rule. This Value will be evaluated against the Property.
/// </summary>
object Value { get; }
/// <summary>
/// Turn On or Off the CaseSensitive evaluation.
/// </summary>
bool CaseSensitive { get; set; }
}

View File

@@ -0,0 +1,179 @@
using System.ComponentModel;
using System.Drawing.Design;
using Telerik.WinControls;
//using System.Linq;
using Telerik.WinControls.Data;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
/// <summary>
/// RadCompositeValidationRule evaluates two or more RadValidationRules or RadValidationRuleWithTargetControl
/// and combines their with AND or OR operator.
/// </summary>
public class RadCompositeValidationRuleEx : CompositeFilterDescriptor, IRadValidationRuleEx
{
private bool caseSensitive = false;
private string toolTipText = "";
private string toolTipTitle = "Validation Failed";
private bool autoToolTip = true;
/// <summary>
/// Gets or Sets a collection with RadControl descendants that belongs this Rule.
/// </summary>
[Editor(DesignerConsts.RadValidationRuleAssociatedControlsEditorString, typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public List<Control> Controls
{
get
{
List<Control> controls = [];
foreach (IRadValidationRuleEx validationRule in ValidationRules)
{
foreach (Control control in validationRule.Controls)
{
if (!controls.Contains(control))
{
controls.Add(control);
}
}
}
return controls;
}
set
{
foreach (RadControl control in value)
{
AddControl(control);
}
}
}
/// <summary>
/// <para>Associates this rule and all controls in ValidationRules collection with the specified RadControl descendant.</para>
/// </summary>
/// <param name="control">A RadControl descendant that represents the editor.</param>
public virtual void AddControl(RadControl control)
{
if (control == null)
{
return;
}
foreach (IRadValidationRuleEx validationRule in ValidationRules)
{
if (!validationRule.Controls.Contains(control))
{
validationRule.AddControl(control);
}
}
}
/// <summary>
/// <para>Removes the specified RadControl descendant from this rule and from all controls in ValidationRules collection .</para>
/// </summary>
/// <param name="control">A RadControl descendant that represents the editor.</param>
public void RemoveControl(RadControl control)
{
if (control == null)
{
return;
}
foreach (IRadValidationRuleEx validationRule in ValidationRules)
{
while (validationRule.Controls.Contains(control))
{
validationRule.RemoveControl(control);
}
}
}
/// <summary>
/// Inherit property. Not used in RadCompositeValidationRule.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[Browsable(false)]
public override FilterDescriptorCollection FilterDescriptors { get { return base.FilterDescriptors; } }
/// <summary>
/// Gets or Sets the collection of ValidationRules that belongs to this RadValidationProvider.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(DesignerConsts.RadValidationProviderCompositeConditionsCollectionEditorString, typeof(UITypeEditor)),
Category(RadDesignCategory.DataCategory),
Description("Gets a collection representing the Rules in this CompositeValidationRule.")]
public FilterDescriptorCollection ValidationRules
{
get
{
return base.FilterDescriptors;
}
}
/// <summary>
/// Gets the Rule expression.
/// </summary>
/// <value>The Rule expression.</value>
public override string Expression { get { return base.Expression; } }
/// <summary>
/// Gets or Sets the ToolTip Text.
/// </summary>
[DefaultValue("")]
public string ToolTipText { get { return toolTipText; } set { toolTipText = value; } }
/// <summary>
/// Gets or Sets the ToolTip Title Text. This text will be shown as ToolTip Title text when rule validation fails.
/// </summary>
[DefaultValue("Validation Failed")]
public string ToolTipTitle { get { return toolTipTitle; } set { toolTipTitle = value; } }
/// <summary>
/// Inherited property. Not used in the Rule.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]
public override bool IsFilterEditor
{
get { return base.IsFilterEditor; }
set { base.IsFilterEditor = value; }
}
/// <summary>
/// Enable or Disable the ToolTip when validation fails.
/// </summary>
[DefaultValue(true)]
public bool AutoToolTip
{
get { return autoToolTip; }
set { autoToolTip = value; }
}
/// <summary>
/// Inherited property. Not used in the Rule.
/// </summary>
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override object Value { get { return base.Value; } set { base.Value = value; } }
/// <summary>
/// Enable or Disable the case sensitive Rule's Like operator.
/// </summary>
[DefaultValue(false)]
public bool CaseSensitive
{
get
{
return caseSensitive;
}
set
{
caseSensitive = value;
}
}
}

View File

@@ -0,0 +1,109 @@
using Telerik.WinControls;
using Telerik.WinControls.UI;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
public delegate void RadValidationEventHandlerEx(object sender, RadValidationEventArgsEx e);
public class RadValidationEventArgsEx : EventArgs
{
const string DefaultToolTipTitle = "Validation Failed";
private readonly Control control;
private readonly IRadValidationRuleEx validationRule;
private bool displayIconAndToolTip = true;
private string errorTitle = DefaultToolTipTitle;
private bool enableToolTipShadow = true;
/// <summary>
/// Gets or Sets the ValidationHelperElement descendant that contains the Error image. This element is set next to the validated control.
/// </summary>
public ValidationHelperElement ValidationHelperElement { get; set; }
/// <summary>
/// Gets or Sets the result from the rule evaluation.
/// </summary>
public bool IsValid { get; set; }
/// <summary>
/// Get the Control which is evaluated in the ValidationRule.
/// </summary>
public Control Control { get { return control; } }
/// <summary>
/// Error image that will displayed next to the Validated control.
/// </summary>
public Image ErrorImage { get; set; }
/// <summary>
/// Error SVG image that will displayed next to the Validated control.
/// </summary>
public RadSvgImage ErrorSvgImage { get; set; }
/// <summary>
/// Gets or sets the custom ToolTip for the Rule.
/// </summary>
public ToolTip ToolTip { get; set; }
/// <summary>
/// Sets the X position of the ToolTip.
/// </summary>
public int? ToolTipX { get; set; }
/// <summary>
/// Sets the Y position of the ToolTip.
/// </summary>
public int? ToolTipY { get; set; }
/// <summary>
/// Sets the ToolTip's duration in Milliseconds
/// </summary>
public int? ToolTipDuration { get; set; }
/// <summary>
/// The Rule that fires this event.
/// </summary>
public IRadValidationRuleEx ValidationRule { get { return validationRule; } }
/// <summary>
/// Sets or Gets the ToolTip's Text.
/// </summary>
public string ErrorText { get; set; }
/// <summary>
/// Sets or Gets the ToolTip's Title.
/// </summary>
public string ErrorTitle { get { return errorTitle; } set { errorTitle = value; } }
/// <summary>
/// Enable or Disable the Error border and error image.
/// </summary>
public bool DisplayIconAndToolTip { get { return displayIconAndToolTip; } set { displayIconAndToolTip = value; } }
/// <summary>
/// Enable or Disable the ToolTip shadow.
/// </summary>
public bool EnableToolTipShadow { get { return enableToolTipShadow; } set { enableToolTipShadow = value; } }
/// <summary>
/// Construct the RadValidationEventArgs object.
/// </summary>
/// <param name="control">The validated control. Must be RadEditorControl descendant</param>
/// <param name="errorImage">Erorr image</param>
/// <param name="errorText">Error text</param>
/// <param name="errorTitle">Error title</param>
/// <param name="validationRule">Validation rule</param>
/// <param name="isFailed">Validatation failed</param>
public RadValidationEventArgsEx(
Control control,
Image errorImage,
string errorText,
string errorTitle,
IRadValidationRuleEx validationRule,
bool isFailed)
{
this.control = control;
ErrorImage = errorImage;
ErrorText = errorText;
this.validationRule = validationRule;
IsValid = !isFailed;
this.errorTitle = errorTitle;
}
}

View File

@@ -0,0 +1,706 @@
using System.ComponentModel;
using System.Drawing.Design;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using Telerik.Data.Expressions;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.Design;
using Telerik.WinControls.UI;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
/// <summary>
/// Provides a validation management for RadControl descendant editors.
/// </summary>
[ToolboxItem(true)]
[TelerikToolboxCategory(ToolboxGroupStrings.EditorsGroup)]
[Designer(DesignerConsts.RadValidationProviderDesignerString)]
[ProvideProperty("ValidationRule", typeof(RadControl))]
[ProvideProperty("IconAlignment", typeof(RadControl))]
[ProvideProperty("IconPadding", typeof(RadControl))]
public class RadValidationProviderEx : Component, ISupportInitialize, IExtenderProvider
{
#region Externs
[DllImport("user32.dll", EntryPoint = "GetClassLong")]
private static extern int GetClassLong(nint hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "SetClassLong")]
private static extern int SetClassLong(nint hWnd, int nIndex, int dwNewLong);
#endregion
#region Fields
private FilterDescriptorCollection validationRules = [];
private ValidationMode validationMode = ValidationMode.OnValidating;
private Dictionary<RadControl, ErrorIconAlignment> errorIconAlignments = [];
private Dictionary<RadControl, Padding> errorIconPadding = [];
private Dictionary<RadControl, ToolTip> controlsToToolTips = [];
#endregion
#region Properties
/// <summary>
/// Occurs before a RadControl is being validated.
/// </summary>
public event RadValidationEventHandlerEx ControlValidation;
/// <summary>
/// Occurs when the ValidionMode property changed.
/// </summary>
public event EventHandler ValidationModeChanged;
/// <summary>
/// Gets or Sets the ValidationMode.
/// </summary>
[DefaultValue(typeof(ValidationMode), "OnValidating")]
public ValidationMode ValidationMode
{
get { return validationMode; }
set
{
if (validationMode != value)
{
validationMode = value;
CallOnValidationChanged();
}
}
}
/// <summary>
/// Gets or Sets the collection of ValidationRules that belongs to this RadValidationProvider.
/// </summary>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(DesignerConsts.RadValidationProviderItemCollectionDesignerString, typeof(UITypeEditor)),
Category(RadDesignCategory.DataCategory)]
[Description("Gets a collection representing the Conditions in this ValidationProvider.")]
public FilterDescriptorCollection ValidationRules
{
get
{
return validationRules;
}
set
{
validationRules = value;
}
}
#endregion
#region Cstor
/// <summary>
/// Initializes a new instance of the RadValidationProvider class.
/// </summary>
public RadValidationProviderEx()
{
validationRules.PropertyChanged += ValidationRules_PropertyChanged;
}
/// <summary>
/// <para>Initializes a new instance of the RadValidationProvider class with the specified container control.</para>
/// </summary>
/// <param name="container">An object that implements the <see cref="T:System.ComponentModel.IContainer" /> interface, and owns the created object.</param>
public RadValidationProviderEx(IContainer container) : this()
{
container.Add(this);
}
#endregion
#region Public methods
/// <summary>
/// Remove a specific RadControl from the validation rules.
/// </summary>
/// <param name="editorControl">A RadControl descendant.</param>
public void RemoveControlFromRules(RadControl editorControl)
{
foreach (IRadValidationRuleEx rule in ValidationRules)
{
RemoveValidationRule(editorControl, rule);
}
}
/// <summary>
/// Remove a validation rule associated with the specified RadControl descendant.
/// </summary>
/// <param name="editorControl">A RadControl descendant.</param>
/// <param name="ruleToRemove">Rule to remove.</param>
public void RemoveValidationRule(RadControl editorControl, IRadValidationRuleEx ruleToRemove)
{
ruleToRemove.RemoveControl(editorControl);
}
public void BeginInit()
{
}
public void EndInit()
{
EnsureEventSubscribe();
}
/// <summary>
/// Validates all editors associated with the RadControl.
/// </summary>
/// <returns>true if all editors has been successfully validated; otherwise false.</returns>
public bool ValidateAll()
{
var valid = true;
AssociatedControls.ForEach(control =>
{
if (!Validate(control))
valid = false;
});
return valid;
}
/// <summary>
/// Validates the specified editor associated with the RadControl.
/// </summary>
/// <param name="control">A RadControl or descendant that represents the editor to be validated.</param>
/// <returns>true if the editor has been successfully validated; otherwise false.</returns>
public bool Validate(RadControl control)
{
return ValidateCore(control, EventArgs.Empty);
}
/// <summary>
/// <para>Sets the alignment of an error icon for the specified control.</para>
/// </summary>
/// <param name="control">A target RadControl.</param>
/// <param name="errorIconAlignment">An <see cref="T:System.Windows.Forms.ErrorIconAlignment" /> value that specifies the alignment to be set for the RadControl.</param>
public void SetIconAlignment(RadControl control, ErrorIconAlignment errorIconAlignment)
{
if (!errorIconAlignments.ContainsKey(control))
errorIconAlignments.Add(control, errorIconAlignment);
else
{
errorIconAlignments[control] = errorIconAlignment;
}
}
/// <summary>
/// <para>Get the alignment of an error icon for the specified RadControl.</para>
/// </summary>
/// <param name="control">A target control.</param>
/// <returns>An <see cref="T:System.Windows.Forms.ErrorIconAlignment" /> value.</returns>
[DefaultValue(ErrorIconAlignment.MiddleRight)]
public ErrorIconAlignment GetIconAlignment(RadControl control)
{
if (errorIconAlignments.ContainsKey(control))
return errorIconAlignments[control];
return ErrorIconAlignment.MiddleRight;
}
/// <summary>Sets the amount of extra space to leave between the specified control and the error icon.</summary>
/// <param name="control">The <paramref name="control" /> to set the padding for. </param>
/// <param name="errorIconPadding">The padding to add between the icon and the <paramref name="control" />. </param>
public void SetIconPadding(RadControl control, Padding errorIconPadding)
{
if (!this.errorIconPadding.ContainsKey(control))
this.errorIconPadding.Add(control, errorIconPadding);
else
{
this.errorIconPadding[control] = errorIconPadding;
}
}
/// <summary>Returns the amount of extra space to leave next to the error icon.</summary>
/// <returns>The padding to leave between the icon and the control. </returns>
/// <param name="control">The control to get the padding for. </param>
[DefaultValue(typeof(Padding), "1, 1, 1, 1")]
public Padding GetIconPadding(RadControl control)
{
if (errorIconPadding.ContainsKey(control))
return errorIconPadding[control];
return new Padding(1);
}
protected virtual void OnControlValidation(RadValidationEventArgsEx e)
{
if (ControlValidation != null)
ControlValidation(this, e);
}
private void ValidationRules_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Item[]")
EnsureEventSubscribe();
}
/// <summary>
/// Removes a rules associated with the control.
/// </summary>
/// <param name="control">A RadControl descendant</param>
public void RemoveRules(RadControl control)
{
for (var i = validationRules.Count - 1; i >= 0; i--)
{
if (((IRadValidationRuleEx)validationRules[i]).Controls.Contains(control))
validationRules.RemoveAt(i);
}
}
/// <summary>
/// Gets the collection of the controls whose values are validated.
/// </summary>
[Browsable(false)]
public List<RadControl> AssociatedControls
{
get => [.. ValidationRules.OfType<IRadValidationRuleEx>().SelectMany(n => n.Controls).OfType<RadControl>()];
}
/// <summary>
/// Clear the visual indication for the validation error.
/// </summary>
public virtual void ClearErrorStatus()
{
var controls = AssociatedControls;
for (var i = 0; i < controls.Count; i++)
{
ClearErrorStatus(controls[i]);
}
}
/// <summary>
/// Clear the visual indication for the validation error.
/// </summary>
/// <param name="associatedControl">A RadControl descendant</param>
public virtual void ClearErrorStatus(RadControl associatedControl)
{
var children = associatedControl.RootElement.Children;
if (children.Count == 0)
return;
var editorControl = associatedControl;
var controlElement = TryFindControlElement(children);//first non ValidationIconElement is a Main Element for the Control.
if (controlElement == null)
return;
var border = ValidationHelperElement.GetBorder(children);
ValidationHelperElement.RestoreBorderColor(border);
associatedControl.ElementTree.ApplyThemeToElementTree();
for (var i = children.Count - 1; i >= 0; --i)
{
if (associatedControl.RootElement.Children[i] is ValidationHelperElement)
associatedControl.RootElement.Children.RemoveAt(i);
}
controlElement.PositionOffset = new SizeF();
controlElement.MaxSize = new Size();
associatedControl.RootElement.InvalidateMeasure(true);
associatedControl.RootElement.UpdateLayout();
}
#endregion
#region Private methods
private void AssociatedControl_TextChanged(object sender, EventArgs e)
{
if (ValidationMode == ValidationMode.OnTextChange)
ValidateCore(sender, e);
}
private void AssociatedControl_Validating(object sender, CancelEventArgs e)
{
if (ValidationMode == ValidationMode.OnValidating)
ValidateCore(sender, e);
}
internal static object GetSubPropertyValue(object control, string fieldName)
{
var names = fieldName.Split('.');
if (names.Length < 2)
return control.GetType().GetProperty(fieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(control, null);
var innerValue = control.GetType().GetProperty(names[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(control, null);
var index = 1;
while (index < names.Length && innerValue != null)
{
innerValue = innerValue.GetType().GetProperty(names[index], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(innerValue, null);
index++;
}
return innerValue;
}
private bool ValidateCore(object sender, EventArgs e)
{
var control = sender as Control;
foreach (IRadValidationRuleEx ruleToEvaluete in ValidationRules)
{
if (!ruleToEvaluete.Controls.Contains(control))
continue;
var context = new ExpressionContext();
context.Clear();
var value = GetSubPropertyValue(control, ruleToEvaluete.PropertyName);//control.GetType().GetProperty(ruleToEvaluete.PropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(control, null);
if (value != null && ruleToEvaluete.Value != null)
{
try
{
// Fix for 441010
// Try to retrieve Culture from the control
var cultureInfo = RetrieveCulture(control);
value = Convert.ChangeType(value, ruleToEvaluete.Value.GetType(), cultureInfo);
}
catch
{ }
}
context.Add(ruleToEvaluete.PropertyName, value);
if (string.IsNullOrEmpty(ruleToEvaluete.Expression))
continue;
var node = DataUtils.Parse(ruleToEvaluete.Expression, ruleToEvaluete.CaseSensitive);
var result = node.Eval(null, context);
if (result is bool)
{
var boolResult = (bool)result;
var validationEventArgs = FireValidationEvent(!boolResult, (RadControl)sender, ruleToEvaluete);
if (validationEventArgs.DisplayIconAndToolTip)
boolResult = AddOrRemoveImage(validationEventArgs, (RadControl)sender, ruleToEvaluete);
else
{
boolResult = validationEventArgs.IsValid;
}
var cancelEventArgs = e as CancelEventArgs;
if (cancelEventArgs != null)
cancelEventArgs.Cancel = !boolResult;
if (!boolResult)
return false;
}
}
return true;
}
private bool AddOrRemoveImage(RadValidationEventArgsEx validationEventArgs, RadControl associatedControl, IRadValidationRuleEx rule)
{
var children = associatedControl.RootElement.Children;
if (children.Count == 0)
return true;
var editorControl = associatedControl;
var controlElement = TryFindControlElement(children);//first non ValidationIconElement is a Main Element for the Control.
if (controlElement == null)
return true;
var element = validationEventArgs.ValidationHelperElement;
var border = ValidationHelperElement.GetBorder(children);
var truncateSize = false;
if (!validationEventArgs.IsValid)
{
element ??= new ValidationHelperElement();
if (!children.Contains(element))
truncateSize = true;//size of the control should be truncate
element.ToolTipText = validationEventArgs.ErrorText;
element.AutoToolTip = true;
element.StretchHorizontally =
element.StretchVertically = false;
element.Image = validationEventArgs.ErrorImage;
element.SvgImage = validationEventArgs.ErrorSvgImage;
element.Alignment = ToContentAligment(GetIconAlignment(editorControl));
element.Padding = GetIconPadding(editorControl);
if (truncateSize)
{
var elementSize = TelerikDpiHelper.ScaleSizeF(MeasurementControl.ThreadInstance.GetDesiredSize(element, new SizeF(float.MaxValue, float.MaxValue)), controlElement.DpiScaleFactor);
var width = (controlElement.Size.Width - elementSize.Width) / controlElement.DpiScaleFactor.Width;
controlElement.MaxSize = new Size((int)width, controlElement.MaxSize.Height);
associatedControl.RootElement.ResetValue(VisualElement.BackColorProperty, ValueResetFlags.Style);
if (element.IsRightLocated)
children.Add(element);
else
{
controlElement.PositionOffset = new SizeF(elementSize.Width, 0);
children.Insert(0, element);
}
}
ValidationHelperElement.SetBorderColor(border);
if (rule.AutoToolTip)
{
var toolTipX = validationEventArgs.ToolTipX.HasValue ? validationEventArgs.ToolTipX.Value : 0;
var toolTipY = validationEventArgs.ToolTipY.HasValue ? validationEventArgs.ToolTipY.Value : associatedControl.Height + 1;
var toolTipDuration = validationEventArgs.ToolTipDuration.HasValue ? validationEventArgs.ToolTipDuration.Value : 2000;
if (validationEventArgs.ToolTip != null)
{
associatedControl.Disposed -= AssociatedControl_Disposed;
associatedControl.Disposed += AssociatedControl_Disposed;
if (controlsToToolTips.ContainsKey(associatedControl))
controlsToToolTips[associatedControl] = validationEventArgs.ToolTip;
else
{
controlsToToolTips.Add(associatedControl, validationEventArgs.ToolTip);
}
if (!validationEventArgs.EnableToolTipShadow)
ToolTipRemoveShadow(validationEventArgs.ToolTip);
validationEventArgs.ToolTip.Show(element.ToolTipText, associatedControl, toolTipX, toolTipY, toolTipDuration);
}
else
{
ShowToolTip(associatedControl, element.ToolTipText, validationEventArgs.ErrorTitle, toolTipX, toolTipY, toolTipDuration, validationEventArgs.EnableToolTipShadow);
}
}
}
else
{
ValidationHelperElement.RestoreBorderColor(border);
associatedControl.ElementTree.ApplyThemeToElementTree();
for (var i = children.Count - 1; i >= 0; --i)
{
if (associatedControl.RootElement.Children[i] is ValidationHelperElement)
associatedControl.RootElement.Children.RemoveAt(i);
}
controlElement.PositionOffset = new SizeF();
controlElement.MaxSize = new Size();
}
associatedControl.RootElement.InvalidateMeasure(true);
associatedControl.RootElement.UpdateLayout();
associatedControl.Refresh();
return validationEventArgs.IsValid;
}
private void ToolTipRemoveShadow(ToolTip toolTip)
{
var hwnd = (nint)typeof(ToolTip).GetProperty("Handle",
BindingFlags.NonPublic |
BindingFlags.Instance).GetValue(toolTip, null);
var cs = GetClassLong(hwnd, NativeMethods.GCL_STYLE);
if ((cs & NativeMethods.CS_DROPSHADOW) == NativeMethods.CS_DROPSHADOW)
{
cs &= ~NativeMethods.CS_DROPSHADOW;
SetClassLong(hwnd, NativeMethods.GCL_STYLE, cs);
}
}
private void AssociatedControl_Disposed(object sender, EventArgs e)
{
var radControl = sender as RadControl;
radControl.Disposed -= AssociatedControl_Disposed;
if (radControl != null && controlsToToolTips.ContainsKey(radControl))
{
var toolTip = controlsToToolTips[radControl];
if (toolTip != null)
{
controlsToToolTips.Remove(radControl);
toolTip.Dispose();
toolTip = null;
}
}
}
protected virtual void ShowToolTip(RadControl associatedControl, string toolTipText, string toolTipTitle, int toolTipX, int toolTipY, int toolTipDuration, bool enableToolTipShadow)
{
var toolTip = new ToolTip
{
ToolTipTitle = toolTipTitle,
BackColor = Color.Red,
ForeColor = Color.White,
OwnerDraw = true,
InitialDelay = 0,
AutoPopDelay = toolTipDuration
};
if (!enableToolTipShadow)
ToolTipRemoveShadow(toolTip);
toolTip.Draw += delegate (object sender, DrawToolTipEventArgs e)
{
e.DrawBackground();
e.DrawBorder();
using (var brush = new SolidBrush(Color.White))
using (var titleFont = new Font("Segoe UI", 8f, FontStyle.Bold))
using (var textFont = new Font("Segoe UI", 8f, FontStyle.Regular))
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
if (!string.IsNullOrEmpty(toolTipTitle))
{
e.Graphics.DrawString(toolTipTitle, titleFont, brush, new Point(2, 2));
e.Graphics.DrawString(toolTipText, textFont, brush, new Point(2, 19));
}
else
{
e.Graphics.DrawString(toolTipText, textFont, brush, new Point(2, 2));
}
}
};
if (controlsToToolTips.ContainsKey(associatedControl))
controlsToToolTips[associatedControl] = toolTip;
else
{
controlsToToolTips.Add(associatedControl, toolTip);
}
associatedControl.Disposed -= AssociatedControl_Disposed;
associatedControl.Disposed += AssociatedControl_Disposed;
toolTip.Show(toolTipText, associatedControl, toolTipX, toolTipY, toolTipDuration);
}
private ContentAlignment ToContentAligment(ErrorIconAlignment errorIconAlignment)
{
switch (errorIconAlignment)
{
case ErrorIconAlignment.TopLeft:
return ContentAlignment.TopLeft;
case ErrorIconAlignment.TopRight:
return ContentAlignment.TopRight;
case ErrorIconAlignment.MiddleLeft:
return ContentAlignment.MiddleLeft;
case ErrorIconAlignment.MiddleRight:
return ContentAlignment.MiddleRight;
case ErrorIconAlignment.BottomLeft:
return ContentAlignment.BottomLeft;
case ErrorIconAlignment.BottomRight:
return ContentAlignment.BottomRight;
default:
return ContentAlignment.MiddleRight;
}
}
private static RadElement TryFindControlElement(RadElementCollection children)
{
RadElement controlElement = null;
foreach (var child in children)
{
if (child is not ValidationHelperElement)
{
controlElement = child;
break;
}
}
return controlElement;
}
protected virtual RadValidationEventArgsEx FireValidationEvent(bool isNotValid, RadControl associatedControl, IRadValidationRuleEx rule)
{
Image image = ResourceHelper.ImageFromResource(typeof(global::Telerik.WinControls.UI.RadValidationProvider), "Telerik.WinControls.UI.Resources.error-icon.png");
var errorText = string.IsNullOrEmpty(rule.ToolTipText) ? rule.Expression : rule.ToolTipText;
var validationEventArgs = new RadValidationEventArgsEx(associatedControl, image, errorText, rule.ToolTipTitle, rule, isNotValid)
{
ValidationHelperElement = ValidationHelperElement.GetValidationElement(associatedControl.RootElement.Children)
};
validationEventArgs.ValidationHelperElement ??= new ValidationHelperElement();
OnControlValidation(validationEventArgs);
return validationEventArgs;
}
private void EnsureEventSubscribe()
{
foreach (IRadValidationRuleEx rule in ValidationRules)
{
var controls = rule.Controls;
foreach (var control in controls)
{
if (control != null)
{
control.Validating -= AssociatedControl_Validating;
control.Validating += AssociatedControl_Validating;
control.TextChanged -= AssociatedControl_TextChanged;
control.TextChanged += AssociatedControl_TextChanged;
}
}
}
}
private void CallOnValidationChanged()
{
if (ValidationModeChanged != null)
ValidationModeChanged(this, EventArgs.Empty);
}
private CultureInfo RetrieveCulture(Control control)
{
var cultureProperty = control.GetType().GetProperty("Culture", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (cultureProperty != null)
{
var cultureObject = cultureProperty.GetValue(control, null);
if (cultureObject != null && cultureObject is CultureInfo)
return cultureObject as CultureInfo;
}
return CultureInfo.CurrentCulture;
}
#endregion
#region ExtenderProvider Interface implementation
/// <summary>
/// <para>Indicates whether a control can be extended.</para>
/// </summary>
/// <param name="extendee">The control to be extended.</param>
/// <returns>true if the control can be extended otherwise false.</returns>
public bool CanExtend(object extendee)
{
return extendee is RadControl;
}
/// <summary>
/// <para>Associates a validation rule with the specified RadControl descendant.</para>
/// </summary>
/// <param name="control">A RadControl descendant that represents the editor.</param>
/// <param name="rule">A RadValidationRule descendant that represents the validation rule.</param>
[Editor(DesignerConsts.RadValidationRuleEditorString, typeof(UITypeEditor))]
public void SetValidationRule(RadControl control, FilterDescriptor rule)
{
var radValidationRule = rule as IRadValidationRuleEx;
if (radValidationRule == null)
RemoveControlFromRules(control);
else
{
radValidationRule.AddControl(control);
}
}
/// <summary>
/// <para>Returns a validation rule associated with the specified RadControl descendant.</para>
/// </summary>
/// <param name="control">A RadControl descendant.</param>
/// <returns>A RadValidationRule descendant that represents the validation rule associated with the editor. Null if no validation rule is associated with the specified control.</returns>
[Editor(DesignerConsts.RadValidationRuleEditorString, typeof(UITypeEditor))]
public FilterDescriptor GetValidationRule(RadControl control)
{
IRadValidationRuleEx ruleToEvaluete = null;
foreach (IRadValidationRuleEx rule in ValidationRules)
{
if (rule.Controls.Contains(control))
{
ruleToEvaluete = rule;
break;
}
}
return ruleToEvaluete as FilterDescriptor;
}
#endregion
}

View File

@@ -0,0 +1,154 @@
using System.ComponentModel;
using System.Drawing.Design;
using Telerik.WinControls;
using Telerik.WinControls.Data;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
/// <summary>
/// RadValidationRule provides a validation logic which compares RadControl's Property with Rule's Value.
/// </summary>
public class RadValidationRuleEx : FilterDescriptor, IRadValidationRuleEx
{
#region Fields
private List<Control> controls = [];
private string toolTipText = string.Empty;
private bool caseSensitive = false;
private bool autoToolTip = true;
private string toolTipTitle = "Validation Failed";
#endregion
#region Cstor
public RadValidationRuleEx() : base()
{
this.PropertyName = "Text";
}
public RadValidationRuleEx(string propertyName, FilterOperator filterOperator, object value) : base(propertyName, filterOperator, value)
{
}
#endregion
/// <summary>
/// <para>Associates this rule with the specified RadControl descendant.</para>
/// </summary>
/// <param name="control">A RadControl descendant that represents the editor.</param>
public virtual void AddControl(RadControl control)
{
if (control != null && !controls.Contains(control))
{
controls.Add(control);
}
}
/// <summary>
/// <para>Removes the specified RadControl descendant from this rule.</para>
/// </summary>
/// <param name="control">A RadControl descendant that represents the editor.</param>
public virtual void RemoveControl(RadControl control)
{
if (control == null)
{
return;
}
while (controls.Contains(control))
{
controls.Remove(control);
}
}
/// <summary>
/// Inherit property. Not used in RadValidation Rule
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]
public override bool IsFilterEditor
{
get { return base.IsFilterEditor; }
set { base.IsFilterEditor = value; }
}
/// <summary>
/// Associated RadControl descendants to this Rule
/// </summary>
[Editor(DesignerConsts.RadValidationRuleAssociatedControlsEditorString, typeof(UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Control> Controls
{
get
{
return controls;
}
set
{
controls = value;
}
}
/// <summary>
/// Gets or sets the Value of this rule. Controls in the rule will be evaluated against this value.
/// </summary>
[Editor(DesignerConsts.RadValidationRuleValueEditorString, typeof(UITypeEditor))]
public override object Value
{
get { return base.Value; }
set { base.Value = value; }
}
/// <summary>
/// Gets or Sets the ToolTip Text. This text will be shown as ToolTip text when rule validation fails.
/// </summary>
[DefaultValue("")]
public string ToolTipText
{
get { return toolTipText; }
set { toolTipText = value; }
}
/// <summary>
/// Enable or Disable the ToolTip when validation fails.
/// </summary>
[DefaultValue(true)]
public bool AutoToolTip
{
get { return this.autoToolTip; }
set { this.autoToolTip = value; }
}
/// <summary>
/// Gets or Sets the ToolTip Title Text. This text will be shown as ToolTip Title text when rule validation fails.
/// </summary>
[DefaultValue("Validation Failed")]
public string ToolTipTitle
{
get { return this.toolTipTitle; }
set { this.toolTipTitle = value; }
}
/// <summary>
/// Enable or Disable the case sensitive Rule's Like operator.
/// </summary>
[DefaultValue(false)]
public bool CaseSensitive
{
get { return this.caseSensitive; }
set { this.caseSensitive = value; }
}
/// <summary>
/// The Name of the Property from Control. This Property will be evaluated against the Rule's Value property.
/// </summary>
[DefaultValue("Text")]
public override string PropertyName
{
get { return base.PropertyName; }
set { base.PropertyName = value; }
}
}

View File

@@ -0,0 +1,99 @@
using System.ComponentModel;
//using System.Linq;
namespace Pilz.UI.Telerik.Controls.RadValidationProvider;
/// <summary>
/// RadValidationRuleWithTargetControl provides a validation logic which compares RadControl's Property with TargetControl's property.
/// </summary>
public class RadValidationRuleWithTargetControlEx : RadValidationRuleEx
{
private string sourceControlPropertyName = "Text";
public RadValidationRuleWithTargetControlEx()
{
}
/// <summary>
/// Gets or sets the Target Control. This control's property value will be used in the Rule evaluation.
/// </summary>
[DefaultValue(null)]
public Control TargetControl { get; set; }
/// <summary>
/// The name of the property that will be used in the Rule evaluation.
/// </summary>
[DefaultValue("Text")]
public string TargetControlPropertyName
{
get { return this.sourceControlPropertyName; }
set { this.sourceControlPropertyName = value; }
}
/// <summary>
/// Gets the Rule expression.
/// </summary>
/// <value>The Rule expression.</value>
public override string Expression
{
get
{
if (this.TargetControl != null)
{
this.Value = this.CalculateValue();
}
return base.Expression;
}
}
/// <summary>
/// Gets or sets the Value of this rule. Controls in the rule will be evaluated against this value.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override object Value
{
get
{
if (this.TargetControl != null && !string.IsNullOrEmpty(this.TargetControlPropertyName))
{
try
{
base.Value = RadValidationProviderEx.GetSubPropertyValue(TargetControl, TargetControlPropertyName);//TargetControl.GetType().GetProperty(TargetControlPropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(TargetControl, null);
}
catch { }
}
return base.Value;
}
set
{
base.Value = value;
}
}
protected virtual object CalculateValue()
{
if (this.TargetControl.Site != null)
{
return string.Format("{0}.{1}", this.TargetControl.Name, this.TargetControlPropertyName);
}
if (!string.IsNullOrEmpty(this.TargetControlPropertyName))
{
try
{
return RadValidationProviderEx.GetSubPropertyValue(TargetControl, TargetControlPropertyName);//this.TargetControl.GetType().GetProperty(this.TargetControlPropertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(this.TargetControl, null);
}
catch
{ }
}
return null;
}
}

View File

@@ -1,6 +1,4 @@
using System.Runtime.CompilerServices;
using Telerik.WinControls;
using Telerik.WinControls.Svg;
using Telerik.WinControls;
using Telerik.WinControls.UI;
using Telerik.WinControls.UI.SplashScreen;

View File

@@ -1,5 +1,7 @@
using System.ComponentModel;
using Pilz.UI.Telerik.Controls.RadValidationProvider;
using System.ComponentModel;
using Telerik.WinControls;
using Telerik.WinControls.Data;
using Telerik.WinControls.UI;
namespace Pilz.UI.Telerik.Dialogs;
@@ -13,6 +15,7 @@ public partial class RadFlyoutBase : UserControl
protected TableLayoutPanel tableLayoutPanel_ActionPanel;
protected TableLayoutPanel tableLayoutPanel_TitlePanel;
protected RadLabel radLabel_Title;
protected RadValidationProviderEx validationProvider = new();
public static RadSvgImage? CancelSvg { get; set; } = null;
public static RadSvgImage? ConfirmSvg { get; set; } = null;
@@ -95,6 +98,16 @@ public partial class RadFlyoutBase : UserControl
}
}
[Browsable(false)]
public FilterDescriptorCollection ValidationRules => validationProvider.ValidationRules;
[DefaultValue(typeof(ValidationMode), "OnValidating")]
public ValidationMode ValidationMode
{
get => validationProvider.ValidationMode;
set => validationProvider.ValidationMode = value;
}
protected RadFlyoutBase()
{
InitializeComponent();
@@ -251,7 +264,7 @@ public partial class RadFlyoutBase : UserControl
protected virtual bool ValidateOK()
{
return true;
return validationProvider.ValidateAll();
}
protected virtual void SetShowTitlePanel()

View File

@@ -1,5 +1,4 @@
using Pilz.UI.Telerik.Dialogs;
using System.Diagnostics.CodeAnalysis;
namespace Pilz.UI.Telerik.Extensions.Extensions;

View File

@@ -19,8 +19,7 @@ public static class RadListDataItemCollectionExtensions
var values = Enum.GetValues(typeof(T));
var items = new List<RadListDataItem>();
if (format == null)
format = v => Enum.GetName(typeof(T), v);
format ??= v => Enum.GetName(typeof(T), v);
if (clearCollection)
@this.Clear();

View File

@@ -9,7 +9,7 @@ public static class ThemeHelper
{
ApplyApplicationTheme(theme, getLightTheme, getDarkTheme, getDarkTheme);
}
[Obsolete()]
public static void ApplyApplicationTheme(ApplicationTheme theme, Func<RadThemeComponentBase> getLightTheme, Func<RadThemeComponentBase> getDarkTheme, Func<RadThemeComponentBase> getGrayTheme)
{
@@ -43,7 +43,7 @@ public static class ThemeHelper
themeToUse = ApplicationTheme.Dark;
else
themeToUse = ApplicationTheme.Gray;
return getTheme(new(themeToUse, highContrast));
}