imdadhusen
imdadhusen

Reputation: 2494

Validation fired but Red Border does not appear with User Control in Silverlight 4

I have created custom User Control which contain TextBox and PasswordBox. I bind TextBox to UserName and PassowrdBox also. The UserName is defined in my LoginViewModel class with [Required] attribute. Now my cursor is leaving from TextBox without entering any value then UserName property fire property changeed notifcation (INotifyPropertyChanged), but dose not mark my Textbox (which is inside the User Control) with Red border.

Following is code of my User Control.

RestrictedBox.xaml

<Grid x:Name="LayoutRoot" Background="Transparent" Margin="0" >
        <TextBox x:Name="txtTextBox" HorizontalAlignment="Stretch" Height="25" />
        <PasswordBox x:Name="txtPasswordBox" HorizontalAlignment="Stretch" Height="25" />
</Grid>

RestrictedBox.xaml.cs

public partial class RestrictedBox : UserControl
    {
        #region Properties
        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(RestrictedBox), new PropertyMetadata("", ValueChanged));
        private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        public bool Updateable
        {
            get { return (bool)GetValue(UpdateableProperty); }
            set { SetValue(UpdateableProperty, value); }
        }
        public static readonly DependencyProperty UpdateableProperty = DependencyProperty.Register("Updateable", typeof(bool), typeof(RestrictedBox), new PropertyMetadata(UpdateableChanged));
        private static void UpdateableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        public bool Redactable
        {
            get { return (bool)GetValue(RedactableProperty); }
            set { SetValue(RedactableProperty, value); }
        }
        public static readonly DependencyProperty RedactableProperty = DependencyProperty.Register("Redactable", typeof(bool), typeof(RestrictedBox), new PropertyMetadata(RedactableChanged));
        private static void RedactableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
        }
        #endregion
        #region Constructors
        public RestrictedBox()
        {
            InitializeComponent();
            txtTextBox.SetBinding(TextBox.TextProperty, new Binding { Source = this, Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay});
            txtTextBox.SetBinding(TextBox.VisibilityProperty, new Binding("Redactable") { Source = this, Converter = new BoolToVisibilityConverterReverse() });
            txtPasswordBox.SetBinding(PasswordBox.PasswordProperty, new Binding { Source = this, Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay });
            txtPasswordBox.SetBinding(TextBox.VisibilityProperty, new Binding("Redactable") { Source = this, Converter = new BoolToVisibilityConverter() });
       }
        #endregion
    }

Following is the code where i used my Custom User Control

LoginView.xaml

<Control:RestrictedBox x:Name="UserName" VerticalAlignment="Top" TabIndex="2" Grid.Row="1"  Grid.Column="1" HorizontalAlignment="Stretch" Height="40" Value="{Binding Path=LoginModelValue.UserName, Mode=TwoWay, ValidatesOnNotifyDataErrors=True, ValidatesOnExceptions=True,
 ValidatesOnDataErrors=True, NotifyOnValidationError=True}" Validatevalue:UpdateSourceTriggerHelper.UpdateSourceTrigger="True" Redactable="True" Updateable="True"  />

LoginView.xaml.cs

[Export(typeof(LoginView))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public partial class LoginView : UserControl, IPageTitle
    {
        #region Constuctors
        public LoginView()
        {
            InitializeComponent();
        }
        [Import]
        public LoginViewModel ViewModel
        {
            get {return this.DataContext as LoginViewModel;}            
            set { DataContext = value; }
        }
        #endregion
    }

LoginViewModel.cs

 [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class LoginViewModel : INotifyPropertyChanged, IRegionMemberLifetime
    {
        private LoginModel _LoginModelValue;
        public LoginModel LoginModelValue
        {
            get { return _LoginModelValue; }
            set
            {
                _LoginModelValue = value;
                OnPropertyChanged("LoginModelValue");
            }
        }
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged = delegate { };
        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        void LoginModelValue_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (LoginModelValue.IsValidObject())
            {
                LoginCommand.RaiseCanExecuteChanged();
                IsEnabled = LoginModelValue.IsValidObject();
                SetIncorrectLogin(!IsEnabled);
            }
        }
        #endregion
    }

Can anybody has idea why i am not getting Red Border surrounded with my TextBox which is inside my Custom User Control?

Any help, suggestions and comments would be highly appreciated!

Thanks,

Imdadhusen

Upvotes: 2

Views: 1765

Answers (2)

imdadhusen
imdadhusen

Reputation: 2494

Now i resolved issue using following code. I have replaced following line

txtTextBox.SetBinding(TextBox.VisibilityProperty, new Binding("Redactable") { Source = this, Converter = new BoolToVisibilityConverterReverse() });

with

 this.MapBinding(RestrictedControl.ValueProperty, txtTextBox, TextBox.TextProperty);

and added following code. that's it.

namespace QSys.Library.Helpers
{
    public static class FrameworkElementExtension
    {
        public static void MapBinding(this FrameworkElement element, DependencyProperty firstProperty, FrameworkElement targetElement, DependencyProperty secondProperty)
        {
            BindingExpression firstExpression = element.GetBindingExpression(firstProperty);
            if (firstExpression != null && firstExpression.ParentBinding != null)
            {
                targetElement.SetBinding(secondProperty, firstExpression.ParentBinding);
            }
        }
    }
}

I specially thanks to everybody how was looking for this. and i am also very thanksful Rakesh Gunijan (http://www.codeproject.com/Articles/293302/Silverlight-user-control-validation) how expain in very much clear.

Thanks,

Imdadhusen

Upvotes: 1

vortexwolf
vortexwolf

Reputation: 14037

As I've already said, the validation works only for one binding and is not inherited by consequent bindigns as in your case.

The easiest way would be to add the Required annotation directly to the Value property of your control and validate it once again:

[Required]
public string Value
{
    get { return (string)GetValue(ValueProperty); }
    set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(string), typeof(RestrictedBox), new PropertyMetadata("", ValueChanged));
private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var rb = d as RestrictedBox;
    Validator.ValidateProperty(rb.Value, new ValidationContext(rb, null, null) { MemberName = "Value" });
}

And add the ValidatesOnExceptions attribute to your bindings so that validation works:

txtTextBox.SetBinding(TextBox.TextProperty, new Binding { Source = this, Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay, 
    ValidatesOnExceptions = true });
//...
txtPasswordBox.SetBinding(PasswordBox.PasswordProperty, new Binding { Source = this, Path = new PropertyPath("Value"), Mode = BindingMode.TwoWay, 
    ValidatesOnExceptions = true });
//...

Another way: to remove all the properties and bind the RestrictedBox control directly to your view model.

<TextBox x:Name="txtTextBox" HorizontalAlignment="Stretch" Height="25" 
        Text="{Binding LoginModelValue.UserName, Mode=TwoWay, ValidatesOnExceptions=True}" />
<PasswordBox x:Name="txtPasswordBox" HorizontalAlignment="Stretch" Height="25" 
            Password="{Binding LoginModelValue.UserName, Mode=TwoWay, ValidatesOnExceptions=True}" />

These solutions seem far from ideal, but actually the validation by data annotations is not good by design. I would recommend to use the INotifyDataErrorInfo interface.

Upvotes: 1

Related Questions