问题 UpdateSourceTrigger =明确


我正在创建一个包含多个文本框的WPF窗口,当用户按下“确定”按钮时,我希望所有文本框都被评估为非空白。 我知道我必须使用TextBoxes和'Explicit'的'UpdateSourceTrigger',但我是否需要为每个人调用'UpdateSource()'? 例如

<TextBox Height="23" 
     HorizontalAlignment="Left" 
     Margin="206,108,0,0" 
     Text="{Binding Path=Definition, UpdateSourceTrigger=Explicit}"
     Name="tbDefinitionFolder" 
     VerticalAlignment="Top" 
     Width="120" />

<TextBox Height="23" 
     HorizontalAlignment="Left" 
     Margin="206,108,0,0" 
     Text="{Binding Path=Release, UpdateSourceTrigger=Explicit}"
     Name="tbReleaseFolder" 
     VerticalAlignment="Top" 
     Width="120" />

...

BindingExpression be = tbDefinitionFolder.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
BindingExpression be2 = tbReleaseFolder.GetBindingExpression(TextBox.TextProperty);
be2.UpdateSource();

3436
2018-01-10 15:02


起源



答案:


另一种方法是将UpdateSourceTrigger设置为PropertyChanged。

然后从INotifyPropertyChanged和IDataErrorInfo继承您的VM。这是一个例子......

public class MyViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private string myVar;
    public string MyProperty
    {
        [DebuggerStepThrough]
        get { return myVar; }
        [DebuggerStepThrough]
        set
        {
            if (value != myVar)
            {
                myVar = value;
                OnPropertyChanged("MyProperty");
            }
        }
    }
    private void OnPropertyChanged(string prop)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(pro));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public string Error
    {
        get { return String.Empty; }
    }
    public string this[string columnName]
    {
        get
        {
            if (columnName == "MyProperty")
            {
                if (String.IsNullOrEmpty(MyProperty))
                {
                    return "Should not be blank";
                }
            }
            return null;
        }
    }
}

假设您的一个TextBox绑定到上面声明的'MyProperty'。索引器在IDataErrorInfo中实现,并在“MyProperty”更改时被调用。在索引器主体中,您可以检查值是否为空并返回错误字符串。如果错误字符串为非null,则用户在TextBox上获得一个漂亮的装饰器作为视觉提示。因此,您可以一次性执行验证并提供UI体验。

如果您使用上面编码的两个接口并使用UpdateSourceTrigger = PropertyChanged,则所有这些都是免费的。 UpdateSourceTrigger = Explicit的使用对于提供您描述的验证来说是一种巨大的过度杀伤。

TextBox的Xaml将是......

 <TextBox DataContext="{StaticResource Vm}" Text="{Binding MyProperty,
                UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, 
                NotifyOnSourceUpdated=True, Mode=TwoWay}" Width="200" Height="25"/>

4
2018-01-10 16:04



+1因为这种数据验证应该在ViewModel中完成 IDataErrorInfo - Rachel
感谢Garry Ive按照你的建议实现它,我现在遇到的问题是当对话框启动时文本框出现错误,即红色? - Chris Milburn
@Rachel我不明白为什么他的方法仍然在没有调用的情况下更新源代码 UpdateSource()。 - Igor
@Igor The UpdateSourceTrigger=PropertyChanged 在绑定中告诉绑定在属性更改时随时更新源。如果您希望它仅在您致电时更新 UpdateSource(),你需要设置 UpdateSourceTrigger=Explicit - Rachel
@Rachel完全正确,但在我的示例中,我使用带有文本框模板列的DataGrid UpdateSourceTrigger 设置 Explicit 并且它仍然更新焦点丢失的来源。 - Igor


答案:


另一种方法是将UpdateSourceTrigger设置为PropertyChanged。

然后从INotifyPropertyChanged和IDataErrorInfo继承您的VM。这是一个例子......

public class MyViewModel : INotifyPropertyChanged, IDataErrorInfo
{
    private string myVar;
    public string MyProperty
    {
        [DebuggerStepThrough]
        get { return myVar; }
        [DebuggerStepThrough]
        set
        {
            if (value != myVar)
            {
                myVar = value;
                OnPropertyChanged("MyProperty");
            }
        }
    }
    private void OnPropertyChanged(string prop)
    {
        if(PropertyChanged!=null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(pro));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public string Error
    {
        get { return String.Empty; }
    }
    public string this[string columnName]
    {
        get
        {
            if (columnName == "MyProperty")
            {
                if (String.IsNullOrEmpty(MyProperty))
                {
                    return "Should not be blank";
                }
            }
            return null;
        }
    }
}

假设您的一个TextBox绑定到上面声明的'MyProperty'。索引器在IDataErrorInfo中实现,并在“MyProperty”更改时被调用。在索引器主体中,您可以检查值是否为空并返回错误字符串。如果错误字符串为非null,则用户在TextBox上获得一个漂亮的装饰器作为视觉提示。因此,您可以一次性执行验证并提供UI体验。

如果您使用上面编码的两个接口并使用UpdateSourceTrigger = PropertyChanged,则所有这些都是免费的。 UpdateSourceTrigger = Explicit的使用对于提供您描述的验证来说是一种巨大的过度杀伤。

TextBox的Xaml将是......

 <TextBox DataContext="{StaticResource Vm}" Text="{Binding MyProperty,
                UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, 
                NotifyOnSourceUpdated=True, Mode=TwoWay}" Width="200" Height="25"/>

4
2018-01-10 16:04



+1因为这种数据验证应该在ViewModel中完成 IDataErrorInfo - Rachel
感谢Garry Ive按照你的建议实现它,我现在遇到的问题是当对话框启动时文本框出现错误,即红色? - Chris Milburn
@Rachel我不明白为什么他的方法仍然在没有调用的情况下更新源代码 UpdateSource()。 - Igor
@Igor The UpdateSourceTrigger=PropertyChanged 在绑定中告诉绑定在属性更改时随时更新源。如果您希望它仅在您致电时更新 UpdateSource(),你需要设置 UpdateSourceTrigger=Explicit - Rachel
@Rachel完全正确,但在我的示例中,我使用带有文本框模板列的DataGrid UpdateSourceTrigger 设置 Explicit 并且它仍然更新焦点丢失的来源。 - Igor


如果你使用 Explicit 你需要打电话 UpdateSource

我不确定这是否是你尝试做的最好的方法,我几乎从不使用 Explicit,如果我不想立即应用更改,我宁可绑定到对象的副本,或者如果要取消编辑,我会存储副本并还原所有内容。


5
2018-01-10 15:33



因此,如果您有一个带有多个编辑框的表单,当用户按下“提交”(例如汽车保险详细信息表格)时需要对其进行检查,您将如何设计它? - Chris Milburn
@ChrisMilburn:我可能会补充一下 验证规则 到绑定,然后在实际进行之前查看每个控件是否有效。 这里有一些关于这个问题的文章。 - H.B.


使用UpdateSourceTrigger = Explicit有一些很好的理由,而不是其他值。想象一下,您必须检查输入的值是否唯一,这将通过读取数据库来完成。这可能需要一些时间,即使0.3秒也是不可接受的。使用PropertyChanged时,每次用户按下键时都会执行此数据库检查,这会使用户界面无法响应。如果UpdateSourceTrigger = LostFocus并且用户将在控件之间快速切换(如果您按住Tab,控件之间存在闪电般的快速循环),则会发生同样的事情。因此,我们的目标是在关键时刻(通常在保存数据之前)立即验证所有内容。这种方法需要最少的代码,这会将数据从视图推送到视图模型并强制验证。代码背后没有验证代码或其他应用程序逻辑,因此MVVM纯粹主义者可以相对平静。我在VB.NET中创建了功能完备的示例,它将Caliburn.Micro用于MVVM和IoC。你可以在这里下载: https://drive.google.com/file/d/0BzdqT0dfGkO3OW5hcjdBOWNWR2M


2
2018-02-14 14:38





将其设置为更简单 UpdateSourceTrigger=PropertyChanged 虽然它会在每次值变化时更新基础变量(对于输入的每个字母)


-1
2018-01-10 15:36