如何在WPF视图中获取无效值

时间:2018-09-18 18:05:43

标签: c# wpf mvvm

如果我的WPF视图的文本框绑定了十进制(或任何其他数字格式),则当我输入字母或任何其他invald字符且该值未传输到视图模型(永远不会到达设置器上的断点)。如果输入数字,一切正常。要禁用我的保存按钮(ICommand),我想在我的视图模型中获取信息,即视图中存在类似于MVVM的错误。非常欢迎记录此行为的提示!

所以目标情况如下:

current situation as is by WPF standard

我想要的是禁用“保存并关闭”: enter image description here

XAML:

    <TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged}"/>

ViewModel

  public int Punkte_Seite_max
{
  get { return _punkte_Seite_max; }
  set
  {
    _punkte_Seite_max = value;
    Changed(); //INotifyPropertyChanged  call
  }
}

2 个答案:

答案 0 :(得分:1)

What you want to be using is INotifyDataErrorInfo documentation found here. This lets you provide custom validation on the properties that you have bound to your ViewModel.

This is a sample I have shamelessly copied from CodeProject but I have done so to prevent any link rot. I have also tried to adapt it slightly to match your example.

ViewModel

public class ViewModel : INotifyDataErrorInfo
{
    // A place to store all error messages for all properties.
    private IDictionary<string, List<string>> propertyErrors = new Dictionary<string, List<string>>();

    public string Preis
    {
        get { return _preis; }
        set
        {
            // Only update if the value has actually changed.
            if (!string.Equals(_preis, value, StringComparison.Ordinal))
            {
                _preis = value;
                Changed();

                this.Validate();
            }
        }
    }

    // The event to raise when the error state changes.
    public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;     

    // A method of getting all errors for the given known property.
    public System.Collections.IEnumerable GetErrors(string propertyName)
    {
        if (propertyName != null)
        {
            if (propertyErrors.TryGetValue(propertyName, out var errors))
            {
                return errors;
            }
        }

        return null;
    }

    // Whether there are any errors on the ViewModel
    public bool HasErrors
    {
        get
        {    
            return propertyErrors.Values.Any(r =>r.Any());
        }
    }

    private void Validate()
    {
        // 1. HERE YOU CAN CHECK WHETHER Preis IS VALID AND ANY OTHER PROPERTIES
        // 2. Update the 'propertyErrors' dictionary with the errors
        // 3. Raise the ErrorsChanged event.
    }
}

XAML

You will need to change your XAML to something like this:

<TextBox>
    <Binding Path="Preis" UpdateSourceTrigger="PropertyChanged" ValidatesOnNotifyDataErrors="True"/>
</TextBox>

答案 1 :(得分:1)

多亏了Bijington,我走上了正确的道路,找到了一个既可以满足MVVM又不需要背后代码的答案。如果有人有兴趣,这是我对这个问题的解决方案。

在视图中产生了上面显示的错误,因为WPF中没有从字母到整数的转换器(应该有一个)。为了解决此问题,绑定中必须具有 NotifyOnValidationError = True

 <TextBox Text="{Binding Path=SelectedItem.Punkte_Seite_max, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True}"

这引发了一个冒泡的 Validation.Error 事件,该事件可以在树中的任何位置捕获。我决定通过路由事件触发器捕获它,如下所示: XAML:

<Window
...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" >
    <i:Interaction.Triggers>
    <userInterface:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}" >
        <userInterface:ViewErrorCounterAction ViewErrorCounter="{Binding Path=ViewValidationErrorCount, Mode=TwoWay}"/>
    </userInterface:RoutedEventTrigger>
</i:Interaction.Triggers>

因此双向绑定是指向我的视图模型的MVVM-okayish链接。

ViewErrorCounterAction基于this SO answer

public class ViewErrorCounterAction : TriggerAction<DependencyObject> {
public ViewErrorCounterAction()
{
    ViewErrorCounter = 0;  // initalize with 0 as there should not be such errors when the window is loaded
}

public int ViewErrorCounter
{
    get
    {
        return System.Convert.ToInt32(GetValue(ViewErrorCounterProperty));
    }
    set
    {
        SetValue(ViewErrorCounterProperty, value);
    }
}

public static readonly DependencyProperty ViewErrorCounterProperty = DependencyProperty.Register("ViewErrorCounter", typeof(int), typeof(ViewErrorCounterAction), new PropertyMetadata(null));

protected override void Invoke(object parameter)
{
    var e = (ValidationErrorEventArgs)parameter;
    if ((e.Action == ValidationErrorEventAction.Added))
        ViewErrorCounter = ViewErrorCounter + 1;
    else if ((e.Action == ValidationErrorEventAction.Removed))
        ViewErrorCounter = ViewErrorCounter - 1;
}
}

最终路由的事件触发器基于https://sergecalderara.wordpress.com/2012/08/23/how-to-attached-an-mvvm-eventtocommand-to-an-attached-event/

希望这会有所帮助,如果有更好的解决方法,请多多指教我如何更好地解决此问题的评论:)