簡體   English   中英

如何驗證 UserControl 上的依賴項屬性?

[英]How to validate a dependency property on a UserControl?

我的視圖模型實現了IDataErrorInfo並包含一個經過驗證的Message屬性。

我創建了一個UserControl ,它的Text DependencyProperty綁定到Message 我的UserControl上有幾個控件綁定到Text (因此顯示Message )。

如何在我的UserControl中未直接綁定到Message的控件上顯示驗證錯誤?

一段時間后,我設法找到了一個我認為應該分享的解決方案,以防其他人發現它有用:

基本上我在我的Text DependencyProperty上添加了一個PropertyChangedCallback 在這個回調中,我獲得了Text和視圖模型上的屬性之間的綁定,並檢查它是否存在驗證錯誤。 如果發現ValidationError ,我會檢查UserControl中綁定到Text所有控件,並使用Validation.MarkInvalid為其綁定提供相同的錯誤。

編輯:

如果我將下面的代碼放在按鈕單擊事件處理程序中,則復制這樣的驗證錯誤可以正常工作。 但是,如果代碼位於TextPropertyChangedCallback ,則什么也不會發生。 有沒有人有辦法解決嗎?

// Get the binding from the Text property to the view model.
BindingExpression textBindingExpression = BindingOperations.GetBindingExpression(this,
    MyUserControl.TextProperty);

// If there is a validation error, then give it to the control bindings.
if (textBindingExpression != null && textBindingExpression.ValidationError != null) {

    Validation.MarkInvalid(this.MyTextBox.GetBindingExpression(TextBox.TextProperty),
        textBindingExpression.ValidationError);

    Validation.MarkInvalid(this.MyTextBlock.GetBindingExpression(TextBlock.TextProperty),
        textBindingExpression.ValidationError);
}

這是我想出的解決方案,它允許具有依賴屬性的UserControl從它綁定到的視圖模型“包裝”驗證。

首先,我按照這篇文章中的模式創建了所需的DataContext層次結構。

XAML:

<!-- Some boilerplate attributes snipped -->
<UserControl x:Class="App.Views.UserControls.MyUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:App.Views.UserControls"             
             Validation.ErrorTemplate="{x:Null}">

    <Grid x:Name="LayoutRoot"
          DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MyUserControl}}">
         <TextBox Grid.Row="0" Grid.Column="0" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" />
   </Grid>
</UserControl>

這樣,控件的 DataContext 就是從父級繼承的視圖模型,這是完成驗證的地方。 然后在控件的根子元素上將其覆蓋為控件本身,這允許綁定到代碼隱藏中的依賴項屬性。 另請注意,控件的ErrorTemplate已被清除 - 這是為了防止出現默認的紅色框。

現在可以非常簡單地從控件的代碼中訪問繼承的視圖模型:

private INotifyDataErrorInfo ViewModelErrors => DataContext as INotifyDataErrorInfo;

現在在用戶控件中實現INotifyDataErrorInfo並包裝視圖模型:

public bool HasErrors => ViewModelErrors.HasErrors;

public IEnumerable GetErrors(string propertyName)
{
    return ViewModelErrors.GetErrors(propertyName);
}

當您需要知道您的控件依賴屬性綁定到哪個模型屬性時,棘手的部分就出現了。 如果您可以按名稱查找已注冊的依賴屬性並詢問綁定,這會更容易,但我找不到沒有反射的方法。 因此,我使用依賴屬性的PropertyChangedCallback手動構建映射列表。 回調的參數包含所有必需的信息。

// Maps User Control properties to their View Model properties.
private readonly Dictionary<string, string> _propertyMappings = new Dictionary<string, string>();

// This should work for any property.
private static void OnDependencyPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var userControl = (MyUserControl)d;
    var dependencyPropertyName = e.Property.Name;

    // Create this mapping one time only.
    if (!userControl._propertyMappings.ContainsKey(dependencyPropertyName))
    {
        // Get the binding from the property to the view model.
        var binding = BindingOperations.GetBindingExpression(d, e.Property);

        if (binding != null)
        {
            // Create a mapping of user control property to view model property.
            // This will let us look up the error from the view model.
            var boundPropertyName = binding.ResolvedSourcePropertyName;

            userControl._propertyMappings[dependencyPropertyName] = boundPropertyName;
         }
    }
}

然后將其合並到GetErrors

public IEnumerable GetErrors(string propertyName)
{
    if (ViewModelErrors != null && _propertyMappings.ContainsKey(propertyName))
    {
        return ViewModelErrors.GetErrors(_propertyMappings[propertyName]);
    }
    else
    {
        return Enumerable.Empty<string>();
    }
}

那應該就夠了。 驗證在模型中完成,結果下拉到用戶控件。 無需復制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM