0

我正在尝试在 MVVM 中的文本框上实现一些简单的验证

    public string Property
    {
        get
        {
            if (App.PropertyStorageContainer != null)
            {
                return App.PropertyStorageContainer.Property;
            }
            else
            {
                return null;
            }
        }
        set
        {
            App.PropertyStorageContainer.Property = value;
            RaisePropertyChanged("Property");
        }
    }

然后在我的 PropertyStorageContainer 类中

private string _property;
public string Property
    {
        get
        {               
            return App.PropertyStorageContainer.Property;
        }
        set
        {
            if(value meets some condition)
            {
                _property = value;
            }
            else
            {
               _property = someothervalue;
            }
        }
    }

.

   <TextBox Width="50" TextAlignment="Center" Text="{Binding Property, Mode=TwoWay, NotifyOnValidationError=True}" MaxLength="3"></TextBox>                          

这样做的目的是验证盒子里有什么。现在,如果我直接从我的代码中设置这个值,那么一切都会按我的预期工作。它尝试 SET 值,然后调用 RaiseProperyChanged,然后 GET 值(因为验证可能与最初输入的值不同)。检索到的最终值确实显示在视图上,所以我知道 TwoWay 绑定正在工作。

我遇到的问题是当 SET 的输入来自用户绑定的 XAML 属性/直接时。在这种情况下,调用了 SET 方法,执行了验证,但 GET 永远不会发生。这会导致屏幕上的文本框中保留未验证的值。

我的第一个问题是这是一个错误还是预期的行为?我可以看到,当输入直接来自用户时,他们可能如何尝试通过删除最后一个 GET 来节省性能,因为 GET 应该没有什么新东西。但如果不是,那么我的所有设置方式可能会干扰 GET 被调用。

第二个问题当然是解决这个问题的任何建议。我已经阅读了一些关于其他验证方法的建议,但是我的程序已经在 PROD 上运行,并且建议的大多数更改都涉及到我的大量返工,所以我希望找到一种方法让它调用 GET any设置属性的时间。

4

2 回答 2

1

我做了几个假设,因为我不确定我是否完全理解你的代码,但我认为你可以考虑实现自定义验证规则。首先,由于您的自定义 ValidationRule 将负责验证,您可以从模型类的属性定义中获取逻辑并“简化”您的 poco:

class PropertyStorageContainer
{
    public string Property { get; set; }
}

您似乎希望您的视图模型充当模型类的基本包装器。同样,我将根据您的场景描述假设这是有效的:

class PropertyStorageContainerViewModel : INotifyPropertyChanged
{
    private PropertyStorageContainer model;

    public PropertyStorageContainerViewModel(PropertyStorageContainer model)
    {
        this.model = model;
    }

    public string Property
    {
        get
        {
            if (model != null)
            {
                return model.Property;
            }
            else
            {
                return null;
            }
        }
        set
        {
            if (model.Property != value)
            {
                model.Property = value;
                RaisePropertyChanged("Property");
            }
        }
    } 
    // INotifyPropertyChanged implementation...
}

现在创建一个扩展 System.Windows.Controls.ValidationRule 的新类并覆盖抽象的 Validate 方法以实现您的验证逻辑。例如,我创建了一个规则,只检查字符串是 null 还是空(假设这将是一个无效的场景):

class IsNullOrEmptyValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        string s = (value ?? string.Empty).ToString();
        if (string.IsNullOrEmpty(s))
        {
            // Invalid...
            return new ValidationResult(false, "Please enter a value.");
        }
        else
        {
            // Valid...
           return new ValidationResult(true, null);
        }
    }
}

现在对于 XAML... 这是一个 TextBox 示例,它将验证规则添加到其绑定验证规则(可以是多个规则)。

 <TextBox Name="textBox1" Width="50" FontSize="12"
        Validation.ErrorTemplate="{StaticResource validationTemplate}"
        Style="{StaticResource textBoxInError}">
    <TextBox.Text>
        <Binding Path="Property" UpdateSourceTrigger="PropertyChanged" >
            <Binding.ValidationRules>
                <local:IsNullOrEmptyValidationRule />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox> 

然后在某处(例如,Window.Resources)定义以下资源(上面引用)。首先是一个 ControlTemplate 来定义 TextBox 在无效状态下的外观:

<ControlTemplate x:Key="validationTemplate">
    <DockPanel>
        <TextBlock Foreground="Red" FontSize="15" Text="!!!" />
        <AdornedElementPlaceholder/>
    </DockPanel>
</ControlTemplate>

此外,您可以定义样式触发器来显示错误消息。这里我只是将它绑定到 TextBox 的 ToolTip 属性:

<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" 
                    Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                    Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>
于 2013-01-16T19:09:16.380 回答
0

你现在正进入 INPC 地狱。我去过那里,并不好玩。
这是一个很大的禁忌,特别是因为如果在此类类上进行了任何映射,那么这些 getter 和 setter 将在其 WPF 绑定上下文之外被调用,并且会失败。

保持简单:直接绑定到App.PropertyStorageContainer.Property

对于第二种情况,要么:

  • 使用数据验证
  • 让属性不是通过绑定而是通过命令设置,您可以在其中进行这种值交换。

帮自己一个忙,不要滥用属性的 get/set

于 2013-01-16T17:03:36.653 回答