5

I am trying to write a ValidationRule that checks if a string is null:

public class NotNullValidationRule : ValidationRule
{
  public override ValidationResult Validate(object value, CultureInfo cultureInfo)
  {
    string str = value as string;

    return string.IsNullOrEmpty(str) ? new ValidationResult(false, Application.Current.FindResource("EmptyStringNotAllowed")) : ValidationResult.ValidResult;
  }
}

In my Window I am using it like this:

<TextBox
    Name="TxtDescription"
    Width="Auto"
    controls:TextBoxHelper.Watermark="{DynamicResource Description}">
    <TextBox.Text>
        <Binding Path="MachineToEdit.Description">
            <Binding.ValidationRules>
                <validation:NotNullValidationRule ValidatesOnTargetUpdated="True"/>
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

If I start the Designer I get this NullReferenceException:

   at System.Windows.Data.BindingExpression.RunValidationRule(ValidationRule validationRule, Object value, CultureInfo culture)
   at System.Windows.Data.BindingExpression.ValidateOnTargetUpdated()
   at System.Windows.Data.BindingExpression.TransferValue(Object newValue, Boolean isASubPropertyChange)
   at System.Windows.Data.BindingExpression.Activate(Object item)
   at System.Windows.Data.BindingExpression.AttachToContext(AttachAttempt attempt)
   at System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Task.Run(Boolean lastChance)
   at MS.Internal.Data.DataBindEngine.Run(Object arg)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at Microsoft.VisualStudio.DesignTools.DesignerContract.Isolation.DesignerProcess.RunApplication()
   at Microsoft.VisualStudio.DesignTools.DesignerContract.Isolation.DesignerProcess.<>c__DisplayClass5_0.<Main>b__0()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

Why does this happen? If I dont activate ValidatesOnTargetUpdated it is working. But I have to validate when the window is loading.

Thank you for all answers and have a nice day.

3 Answers 3

1

EDIT: Better answer than code-behind

In VS2017, enabling project code in the XAML editor also fixed it for me.

Enable Project Code Button

--- Previous answer ---

I was having the same issue and Thomas V's answer unfortunately did not work. I was able to fix the issue by moving the addition of the ValidationRules to the codebehind. Perhaps not the most ideal method, but it did correct the issue.

You could also look at wrapping the codebehind logic in Thomas V's designer check, but it worked without that for me.

XAML:

<TextBox x:Name="FirstNameTextBox">
    <TextBox.Text>
        <Binding x:Name="FirstNameTextBoxBinding"
                 Path="TheNewUser.TheNewUser.GivenName"
                 UpdateSourceTrigger="PropertyChanged" 
                 Mode="TwoWay"
                 NotifyOnValidationError="True"
                 Delay="500" />
    </TextBox.Text>
</TextBox>

CodeBehind:

public NewUserWizard_Info_View()
    {
        InitializeComponent();

        Loaded += TriggerValidationOnLoaded;

        FirstNameTextBoxBinding.ValidationRules.Add(new ValidateEmptyOrNull()
        {
            ValidatesOnTargetUpdated = true
        });            
    }

    private void TriggerValidationOnLoaded(object obj, RoutedEventArgs e)
    {
     // This is needed to trigger the validation on first load
        FirstNameTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }
1
  • If you're using an x64 configuration, the designer can't run your project code and the button will be greyed out.
    – Mastax
    Commented Oct 15, 2020 at 16:32
0

Neither of the other answers worked for me, but I found that adding the following property to my ValidationRule sub-class fixed it:

private bool _ValidatesOnTargetUpdated;
public new bool ValidatesOnTargetUpdated
{
  get => _ValidatesOnTargetUpdated;
  set
  {
    _ValidatesOnTargetUpdated = value;
    base.ValidatesOnTargetUpdated = value;
  }
}

I think the reason this works is that, for whatever reason, the designer cannot access the property in the ValidationRule base class. So this code works around that by overriding the ValidatesOnTargetUpdated property and sets the base class property when the overridden property is set.

0

Warning not tested!

I'm guessing that value is null in method Validate() at design time. So you should check if currently in design time and then return something valid like ValidationResult.ValidResult.

public class NotNullValidationRule : ValidationRule
{
  public override ValidationResult Validate(object value, CultureInfo cultureInfo)
  {

   if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) 
    {
        return ValidationResult.ValidResult;
    }

    string str = value as string;

    return string.IsNullOrEmpty(str) ? new ValidationResult(false, Application.Current.FindResource("EmptyStringNotAllowed")) : ValidationResult.ValidResult;
  }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.