XAML 验证规则在设计时验证OnTargetUpdated空引用异常

mwecs4sa  于 2022-12-16  发布在  其他
关注(0)|答案(3)|浏览(121)

我正在尝试编写一个ValidationRule来检查字符串是否为空:

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;
  }
}

在我的窗口中,我是这样使用它的:

<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>

如果我启动设计器,我会得到这个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()

为什么会发生这种情况?如果我不激活ValidatesOnTargetUpdated,它就会工作。但是我必须在窗口加载时进行验证。
谢谢你的回答,祝你有美好的一天。

bmp9r5qi

bmp9r5qi1#

编辑:比代码隐藏更好的答案
在VS 2017中,在XAML编辑器中启用项目代码也为我修复了这个问题。

---上一个答案---
我也遇到了同样的问题,但托马斯五世的回答不幸不起作用。我可以通过将ValidationRules添加到代码隐藏中来解决这个问题。也许这不是最理想的方法,但它确实纠正了这个问题。
你也可以考虑在托马斯V的设计师检查中 Package 代码隐藏逻辑,但对我来说没有它也能工作。
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>

代码隐藏:

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();
    }
juzqafwq

juzqafwq2#

其他两个答案对我都不起作用,但我发现将以下属性添加到ValidationRule子类中可以解决这个问题:

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

我认为这样做的原因是,不管出于什么原因,设计器无法访问ValidationRule基类中的属性。因此,此代码通过重写ValidatesOnTargetUpdated属性并在设置被重写的属性时设置基类属性来解决此问题。

g6ll5ycj

g6ll5ycj3#

警告未测试!

我猜Validate()方法中的值在设计时是空的,所以你应该检查当前是否在设计时,然后返回有效的值,比如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;
  }
}

相关问题