【问题标题】:Silverlight validation red outline stays around element whose data has passed validation, until focused and blurredSilverlight 验证红色轮廓停留在数据已通过验证的元素周围,直到聚焦和模糊
【发布时间】:2011-12-14 00:23:34
【问题描述】:

我有一堂课如下:

public class Guardian : ModelBase, IDataErrorInfo
    {

        internal Guardian()
        {

        }

        [Required]
        [StringLength(50)]
        [Display(Name = "Guardian's First Name")]
        public string FirstName
        {
            get { return GetValue(() => FirstName); }
            set { SetValue(() => FirstName, value); }
        }

        [Required]
        [StringLength(50)]
        [Display(Name = "Guardian's Last Name")]
        public string LastName
        {
            get { return GetValue(() => LastName); }
            set { SetValue(() => LastName, value); }
        }

        [USPhoneNumber]
        [Display(Name = "Home Phone Number")]
        public string HomePhone
        {
            get { return GetValue(() => HomePhone); }
            set { SetValue(() => HomePhone, value.NormalizeNANPPhoneNumber()); }
        }

        [USPhoneNumber]
        [Display(Name = "Personal Cell")]
        public string PersonalCell
        {
            get { return GetValue(() => PersonalCell); }
            set { SetValue(() => PersonalCell, value.NormalizeNANPPhoneNumber()); }
        }

        [Required]
        [StringLength(100)]
        [Display(Name = "Address")]
        public string Address1
        {
            get { return GetValue(() => Address1); }
            set { SetValue(() => Address1, value); }
        }

        [StringLength(100)]
        public string Address2
        {
            get { return GetValue(() => Address2); }
            set { SetValue(() => Address2, value); }
        }

        [Required]
        [StringLength(100)]
        public string City
        {
            get { return GetValue(() => City); }
            set { SetValue(() => City, value); }
        }

        [Required]
        [StringLength(100)]
        public string State
        {
            get { return GetValue(() => State); }
            set { SetValue(() => State, value); }
        }

        [Required]
        [StringLength(20)]
        [USPostalCode]
        [Display(Name = "ZIP Code")]
        public string Zip
        {
            get { return GetValue(() => Zip); }
            set { SetValue(() => Zip, value); }
        }

        [Required]
        [Display(Name = "Relationship to Children")]
        public FamilyRole Relationship
        {
            get { return GetValue(() => Relationship); }
            set { SetValue(() => Relationship, value); }
        }

        internal bool IsEmpty()
        {
            return
                string.IsNullOrWhiteSpace(FirstName)
                && string.IsNullOrWhiteSpace(LastName)
                && string.IsNullOrWhiteSpace(HomePhone)
                && string.IsNullOrWhiteSpace(PersonalCell)
                && string.IsNullOrWhiteSpace(Address1)
                && string.IsNullOrWhiteSpace(Address2)
                && string.IsNullOrWhiteSpace(City)
                && string.IsNullOrWhiteSpace(State)
                && string.IsNullOrWhiteSpace(Zip)
                && Relationship == null
                ;
        }

        /// <summary>
        /// Provides support for cross-cutting concerns without having to write
        /// an attribute in Silverlight.
        /// When time allows, convert to an Attribute. The code produced then
        /// can be reused in other projects.
        /// </summary>
        /// <param name="listToAddTo"></param>
        private void CustomValidation(List<ValidationResult> listToAddTo)
        {
            if (listToAddTo == null)
                throw new ArgumentNullException("listToAddTo");
            if (string.IsNullOrWhiteSpace(HomePhone) && string.IsNullOrWhiteSpace(PersonalCell))
                listToAddTo.Add(new ValidationResult("At least one phone number must be filled in.", new string[] { "HomePhone" }));
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get
            {
                List<ValidationResult> results = new List<ValidationResult>();
                this.IsValidObject(results);
                CustomValidation(results);
                if (results.Count > 0)
                    return results[0].ErrorMessage;
                else
                    return null;
            }
        }

        public string this[string columnName]
        {
            get
            {
                List<ValidationResult> results = new List<ValidationResult>();
                this.IsValidObject(results);
                CustomValidation(results);
                var resultByColumn = results.Where(r => r.MemberNames.Contains(columnName)).ToList();
                if (resultByColumn.Count > 0)
                    return resultByColumn[0].ErrorMessage;
                else
                    return null;
            }
        }

        #endregion
    }

我为这个类实现了 IDataErrorInfo。一切正常,我的问题真的很烦人,但一个大到足以支付账单的人说需要解决。我有一个单独的 void 进行额外的验证,由 IDataErrorInfo 成员调用。它会检查是否填写了至少一个电话号码。

这个类的一个实例在我的模型上,称为 CurrentGuardian,该模型是以下弹出窗口的 DataContext:

<controls:ChildWindow xmlns:my="clr-namespace:Microsoft.Windows.Controls"  
                                        x:Class="Tracktion.Controls.CheckInWindows.AddGuardian"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                 xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
                 Width="575" Height="326" 
                 Title="Add Parent/Guardian" HasCloseButton="False" 
                                        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<controls:ChildWindow.Resources>
    <Style TargetType="TextBlock">
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="FontFamily" Value="Segoe UI" />
        <Setter Property="FontSize" Value="12" />
    </Style>
</controls:ChildWindow.Resources>

<Grid x:Name="LayoutRoot" Margin="2">
    <Grid.RowDefinitions>
        <RowDefinition Height="44" />
        <RowDefinition Height="215*" />
        <RowDefinition Height="45" />
    </Grid.RowDefinitions>

    <TextBlock Height="23" Name="textBlock1" Text="Please fill out the form below. Fields marked with an asterisk are required." VerticalAlignment="Top" TextAlignment="Center" />
    <TextBlock Height="23" Margin="0,21,0,0" Name="textBlock2" Text="When done, click Add Another Guardian or Continue Adding Children below." VerticalAlignment="Top" TextAlignment="Center" />

    <sdk:Label Grid.Row="1" Height="22" HorizontalAlignment="Left" Margin="0,11,0,0" Name="label1" VerticalAlignment="Top" Width="142" Content="* Guardian's First Name:" />
    <sdk:Label Content="* Guardian's Last Name:" Height="22" HorizontalAlignment="Left" Margin="0,46,0,0" Name="label2" VerticalAlignment="Top" Width="142" Grid.Row="1" />
    <sdk:Label Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="0,81,0,0" Name="label3" VerticalAlignment="Top" Width="142" Content="* Home Phone:" />
    <sdk:Label Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="0,116,0,0" Name="label4" VerticalAlignment="Top" Width="120" Content="* Personal Cell:" />
    <sdk:Label Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="302,11,0,0" Name="label5" VerticalAlignment="Top" Width="76" Content="* Address:" />
    <sdk:Label Content="* What is your relationship to the child or children?" Height="23" HorizontalAlignment="Left" Margin="0,155,0,0" Name="label6" VerticalAlignment="Top" Width="360" Grid.Row="1" />

    <TextBox Text="{Binding Path=CurrentGuardian.FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="148,5,0,0" Name="textBox1" VerticalAlignment="Top" Width="135" />
    <TextBox Text="{Binding Path=CurrentGuardian.LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="148,40,0,0" Name="textBox2" VerticalAlignment="Top" Width="135" />
    <TextBox Text="{Binding Path=CurrentGuardian.HomePhone, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="148,75,0,0" Name="txtHomePhone" VerticalAlignment="Top" Width="135" LostFocus="PhoneNumber_LostFocus" />
    <TextBox Text="{Binding Path=CurrentGuardian.PersonalCell, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="148,110,0,0" Name="txtCellPhone" VerticalAlignment="Top" Width="135" LostFocus="PhoneNumber_LostFocus" />

    <my:WatermarkedTextBox Text="{Binding Path=CurrentGuardian.Address1, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="366,5,0,0" x:Name="textBox5" VerticalAlignment="Top" Width="184" Watermark="Line 1" />
    <my:WatermarkedTextBox Text="{Binding Path=CurrentGuardian.Address2, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="366,40,0,0" x:Name="textBox6" VerticalAlignment="Top" Width="184" Watermark="Line 2" />
    <my:WatermarkedTextBox Text="{Binding Path=CurrentGuardian.City, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="366,75,0,0" x:Name="textBox7" VerticalAlignment="Top" Width="184" Watermark="City" />
    <ComboBox ItemsSource="{Binding Path=States}" DisplayMemberPath="Abbreviation" SelectedValuePath="Abbreviation" SelectedValue="{Binding Path=CurrentGuardian.State, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="366,110,0,0" Name="comboBox1" VerticalAlignment="Top" Width="88" />
    <my:WatermarkedTextBox Text="{Binding Path=CurrentGuardian.Zip, Mode=TwoWay, ValidatesOnDataErrors=True}" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="460,110,0,0" x:Name="textBox8" VerticalAlignment="Top" Width="90" Watermark="ZIP" />
    <ComboBox DisplayMemberPath="Name" Height="28" HorizontalAlignment="Left" ItemsSource="{Binding Path=Relationships}" Margin="302,149,0,0" Name="comboBox2" SelectedItem="{Binding Path=CurrentGuardian.Relationship, Mode=TwoWay, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="249" Grid.Row="1" />

    <Button Content="Cancel" Grid.Row="2" Height="37" HorizontalAlignment="Left" Margin="0,8,0,0" Name="btnCancel" VerticalAlignment="Top" Width="93" Style="{StaticResource RedButton}" Click="btnCancel_Click" />
    <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">
        <Button Content="Add Another Guardian" Height="37" Margin="5,8,0,0" Name="btnAddGuardian" VerticalAlignment="Top" Style="{StaticResource OrangeButton}" HorizontalAlignment="Right" Width="159" Click="btnAddGuardian_Click" />
        <Button Content="Continue" Height="37" Margin="5,8,0,0" Name="btnContinue" VerticalAlignment="Top" Style="{StaticResource GreenButton}" HorizontalAlignment="Right" Padding="20,0" Click="btnContinue_Click" />
        <!-- TODO: Visibility set when accessing this screen through check in screen. -->
        <Button Content="Check In" Margin="5,8,0,0" Name="btnCheckIn" Visibility="Collapsed" Style="{StaticResource GreenButton}" Click="btnCheckIn_Click" />
    </StackPanel>
</Grid>

需要一个电话号码。您可以输入两者,但至少需要输入一个。当表单绑定到空的 CurrentGuardian 时,第一个电话号码字段 Home Phone 以红色突出显示。当两个数字都没有数据时,在聚焦和模糊该字段后,它会保持红色。如果我输入电话号码,该字段将变为黑色。删除数字会将其变为红色。到目前为止,一切都很好 - 这是预期的行为。现在,如果我没有为家庭电话输入一个号码,但是我为个人手机输入了一个电话号码,当我关闭个人手机时,家庭电话号码会一直以红色突出显示,直到我点击它。一旦我点击它,红色轮廓就会消失。如何使字段验证?我目前在两个字段的模糊事件中都有以下代码:

    private void PhoneNumber_LostFocus(object sender, RoutedEventArgs e)
    {
        KioskCheckIn2 model = (KioskCheckIn2)this.DataContext;
        model.CurrentGuardian.IsValidObject(); // revalidate
        txtHomePhone.GetBindingExpression(TextBox.TextProperty).UpdateSource();
        txtCellPhone.GetBindingExpression(TextBox.TextProperty).UpdateSource();
    }

我有 ValidationResult 用于检查两个数字,只返回 HomePhone 字段作为违规控件,因为我无法弄清楚如何强制重新验证控件,因此两个红色轮廓都会消失。

提前致谢!

【问题讨论】:

    标签: silverlight validation xaml


    【解决方案1】:

    尝试在ChildWindow 中使用DataForm not。众所周知,这是错误的。您也可以尝试申请these fixes。也许 Silverlight 5 的情况有所改善,我还没有检查过。

    一般来说,DataForm 控件在 Toolkit 的“预览”质量范围内,ChildWindow 也不完美,因此在某些情况下可能会出现错误。你也有源代码来做进一步的修复。 ;)

    【讨论】:

    • 不过,我没有使用 DataForm。现在我已经休息了一夜并有时间考虑它,我将尝试使用 INotifyDataErrorInfo 并看看它是如何工作的。
    【解决方案2】:

    实现 INotifyDataErrorInfo,而不是 IDataErrorInfo,似乎可以解决问题。 IDataErrorInfo 很好,但无法让 UI 知道除当前属性之外的其他属性何时发生更改。我尝试允许这两种实现,但它有点奇怪,所以我改变了它,所以它只实现了 INotifyDataErrorInfo。

        public class Guardian : ModelBase, /*IDataErrorInfo,*/ INotifyDataErrorInfo
        {
    
            internal Guardian()
            {
    
            }
    
            [Required]
            [StringLength(50)]
            [Display(Name = "Guardian's First Name")]
            public string FirstName
            {
                get { return GetValue(() => FirstName); }
                set { SetValue(() => FirstName, value); }
            }
    
            [Required]
            [StringLength(50)]
            [Display(Name = "Guardian's Last Name")]
            public string LastName
            {
                get { return GetValue(() => LastName); }
                set { SetValue(() => LastName, value); }
            }
    
            [USPhoneNumber]
            [Display(Name = "Home Phone Number")]
            public string HomePhone
            {
                get { return GetValue(() => HomePhone); }
                set { SetValue(() => HomePhone, value.NormalizeNANPPhoneNumber()); }
            }
    
            [USPhoneNumber]
            [Display(Name = "Personal Cell")]
            public string PersonalCell
            {
                get { return GetValue(() => PersonalCell); }
                set { SetValue(() => PersonalCell, value.NormalizeNANPPhoneNumber()); }
            }
    
            [Required]
            [StringLength(100)]
            [Display(Name = "Address")]
            public string Address1
            {
                get { return GetValue(() => Address1); }
                set { SetValue(() => Address1, value); }
            }
    
            [StringLength(100)]
            public string Address2
            {
                get { return GetValue(() => Address2); }
                set { SetValue(() => Address2, value); }
            }
    
            [Required]
            [StringLength(100)]
            public string City
            {
                get { return GetValue(() => City); }
                set { SetValue(() => City, value); }
            }
    
            [Required]
            [StringLength(100)]
            public string State
            {
                get { return GetValue(() => State); }
                set { SetValue(() => State, value); }
            }
    
            [Required]
            [StringLength(20)]
            [USPostalCode]
            [Display(Name = "ZIP Code")]
            public string Zip
            {
                get { return GetValue(() => Zip); }
                set { SetValue(() => Zip, value); }
            }
    
            [Required]
            [Display(Name = "Relationship to Children")]
            public FamilyRole Relationship
            {
                get { return GetValue(() => Relationship); }
                set { SetValue(() => Relationship, value); }
            }
    
            internal bool IsEmpty()
            {
                return
                    string.IsNullOrWhiteSpace(FirstName)
                    && string.IsNullOrWhiteSpace(LastName)
                    && string.IsNullOrWhiteSpace(HomePhone)
                    && string.IsNullOrWhiteSpace(PersonalCell)
                    && string.IsNullOrWhiteSpace(Address1)
                    && string.IsNullOrWhiteSpace(Address2)
                    && string.IsNullOrWhiteSpace(City)
                    && string.IsNullOrWhiteSpace(State)
                    && string.IsNullOrWhiteSpace(Zip)
                    && Relationship == null
                    ;
            }
    
            protected override void PropertyHasChanged(string propertyName)
            {
                base.PropertyHasChanged(propertyName);
                if (ErrorsChanged != null)
                    this.GetType().GetProperties().ToList().ForEach(p => ErrorsChanged(this, new DataErrorsChangedEventArgs(p.Name)));
            }
    
            /// <summary>
            /// Provides support for cross-cutting concerns without having to write
            /// an attribute in Silverlight.
            /// When time allows, convert to an Attribute. The code produced then
            /// can be reused in other projects.
            /// </summary>
            /// <param name="listToAddTo"></param>
            private void CustomValidation(List<ValidationResult> listToAddTo)
            {
                if (listToAddTo == null)
                    throw new ArgumentNullException("listToAddTo");
                if (string.IsNullOrWhiteSpace(HomePhone) && string.IsNullOrWhiteSpace(PersonalCell))
                    listToAddTo.Add(new ValidationResult("At least one phone number must be filled in.", new string[] { "HomePhone", "PersonalCell" }));
            }
    
            List<ValidationResult> getErrorList()
            {
                List<ValidationResult> results = new List<ValidationResult>();
                this.IsValidObject(results);
                CustomValidation(results);
                return results;
            }
    
            /*
            #region IDataErrorInfo Members
    
            public string Error
            {
                get
                {
                    List<ValidationResult> results = getErrorList();
                    if (results.Count > 0)
                        return results[0].ErrorMessage;
                    else
                        return null;
                }
            }
    
            public string this[string columnName]
            {
                get
                {
                    List<ValidationResult> results = getErrorList();
                    var resultByColumn = results.Where(r => r.MemberNames.Contains(columnName)).ToList();
                    if (resultByColumn.Count > 0)
                        return resultByColumn[0].ErrorMessage;
                    else
                        return null;
                }
            }
    
            #endregion
             */
    
            #region INotifyDataErrorInfo Members
    
            public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    
            public System.Collections.IEnumerable GetErrors(string propertyName)
            {
                List<ValidationResult> results = getErrorList();
                return results.Where(e => e.MemberNames.Contains(propertyName));
            }
    
            public bool HasErrors
            {
                get 
                {
                    List<ValidationResult> results = getErrorList();
                    return results.Count > 0;
                }
            }
    
            #endregion
        }
    

    现在,当我第一次进入屏幕时,两个电话字段都会突出显示。在一个字段中输入有效的电话号码会使两个电话号码字段都通过验证,并且每个字段周围的红线都会消失。请注意,我将第二个字段放回 CustomValidation 下,

    listToAddTo.Add(new ValidationResult("At least one phone number must be filled in.", new string[] { "HomePhone", "PersonalCell" }));
    

    ...这样如果输入的格式不正确,两个属性都可以在它们周围显示红线。

    这是我为处理 IDataErrorInfo 和 INotifyDataErrorInfo 的验证而编写的一些代码。您必须对其进行一些修改以适应您的基类(如果有的话),但我希望这可以帮助某人:

    namespace CLARIA.Infrastructure
    {
        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.ComponentModel;
        using System.ComponentModel.DataAnnotations;
    
        /// <summary>
        /// Builds upon ModelBase with built-in validation.
        /// </summary>
        public abstract class ValidatableModelBase : ModelBase,
        #if SILVERLIGHT
            INotifyDataErrorInfo
        #else
            IDataErrorInfo
        #endif
    
        {
    
            private List<ValidationResult> GetErrorList()
            {
                List<ValidationResult> results = new List<ValidationResult>();
                this.IsValidObject(results);
                CustomValidation(results);
                return results;
            }
    
            /// <summary>
            /// Allows the derived class to override and add custom validation.
            /// The validation results generated from this method should be added
            /// to the collection <see cref="addResultsToThisList"/>.
            /// </summary>
            /// <param name="addResultsToThisList"></param>
            protected virtual void CustomValidation(List<ValidationResult> addResultsToThisList) {}
    
        #if SILVERLIGHT
    
            #region INotifyDataErrorInfo Members
    
            protected override void PropertyHasChanged(string propertyName)
            {
                base.PropertyHasChanged(propertyName);
                // Force re-validation of every property.
                if (ErrorsChanged != null)
                    this.GetType().GetProperties().ToList().ForEach(p => ErrorsChanged(this, new DataErrorsChangedEventArgs(p.Name)));
            }
    
            public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
    
            public System.Collections.IEnumerable GetErrors(string propertyName)
            {
                List<ValidationResult> results = GetErrorList();
                return results.Where(e => e.MemberNames.Contains(propertyName));
            }
    
            public bool HasErrors
            {
                get
                {
                    List<ValidationResult> results = GetErrorList();
                    return results.Count > 0;
                }
            }
    
            #endregion
    
        #else
    
            #region IDataErrorInfo Members
    
            public string Error
            {
                get
                {
                    List<ValidationResult> results = GetErrorList();
                    if (results.Count > 0)
                        return results[0].ErrorMessage;
                    else
                        return null;
                }
            }
    
            public string this[string columnName]
            {
                get
                {
                    List<ValidationResult> results = GetErrorList();
                    var resultByColumn = results.Where(r => r.MemberNames.Contains(columnName)).ToList();
                    if (resultByColumn.Count > 0)
                        return resultByColumn[0].ErrorMessage;
                    else
                        return null;
                }
            }
    
            #endregion
    
        #endif
    
    
        }
    
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多