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

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

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.

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

Related

Xamarin forms turns dark and appshell doesn't work anymore when switching language

I implemented language changing trough AppResources.resx files, I have 2 of these: AppResources.resx and AppResources.fr.resx. Switching the language with the following code:
private void Language_switch(object sender, EventArgs e)
{
var lang_switch = Lang.Text;
if (lang_switch == "FR")
{
CultureInfo language = new CultureInfo("fr");
Thread.CurrentThread.CurrentUICulture = language;
AppResources.Culture = language;
}
else
{
CultureInfo language = new CultureInfo("");
Thread.CurrentThread.CurrentUICulture = language;
AppResources.Culture = language;
}
Application.Current.MainPage = new NavigationPage(new PointsPage());
}
The language switches fine but whenever I do, the app turns dark and the AppShell seems to break, it only shows the top bar with toolbar items i made (in what seems to be the standard xamarin color) and showing what seems to be it trying to show the navigation at the bottom, but this only looks like a bar but doesn't seem to have navigation on it and doesn't have any of the labels for navigation.
The content on the page also seems to overlap with this bar going over it if I scroll down. If I press the switch button again it does still switch languages but stay in this dark mode. I don't have any of the dark color set in my app and don't have a dark mode implemented.
It also seems to be doing this on every single page I do it on. How can i stop this from happening so it uses the layout i have made for my app and doesn't turn dark?
Edit: I found out that problem isn't in the language switch. When I go to another page just using
Application.Current.MainPage = new NavigationPage(new PointsPage());
and removing the the language switch code it still does the weird thing where it changes colors. From what it looks like to me is that the page gets put on top without the AppShell moving to be on top of that. Is there a way to reload the AppShell?
Edit2: I managed to fix it. As I suspected the AppShell wasn't reloading and wasn't being put on top of the reloaded page. I added
Application.Current.MainPage = new AppShell();
underneath the page reload and now everything is working
When you use the .resx file to make the localization, create the resx file with the matched file name, when you change the system language, reopen the app would show the matched resource your set in .resx.
For more details about it, you could refer to the MS docs. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/localization/text?pivots=windows
Code sample: https://github.com/xamarin/xamarin-forms-samples/tree/main/UsingResxLocalization
If you want to change it at runtime, you could use ResourceManager. It provides convenient access to culture-specific resources at runtime.
I make a simple example for your reference.
MainPage: String1, Settings are the key in .resx file.
<StackLayout>
<Label Text="{Binding Resources[String1]}"
VerticalOptions="Center"
HorizontalOptions="Center" />
<Button Text="{Binding Resources[Settings]}"
HorizontalOptions="Center"
Clicked="Button_Clicked" />
</StackLayout>
Code behind:
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainPageViewModel();
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new SettingsPage());
}
SettingsPage:
<StackLayout>
<Label Text="{Binding Resources[PickLng]}" />
<Picker ItemsSource="{Binding Languages}" SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}" />
</StackLayout>
Code behind:
public SettingsPage()
{
InitializeComponent();
BindingContext = new SettingsViewModel();
}
ViewModel:
public class CultureChangedMessage
{
public CultureInfo NewCultureInfo { get; private set; }
public CultureChangedMessage(string lngName)
: this(new CultureInfo(lngName))
{ }
public CultureChangedMessage(CultureInfo newCultureInfo)
{
NewCultureInfo = newCultureInfo;
}
}
public class LocalizedResources : INotifyPropertyChanged
{
const string DEFAULT_LANGUAGE = "en";
readonly ResourceManager ResourceManager;
CultureInfo CurrentCultureInfo;
public string this[string key]
{
get
{
return ResourceManager.GetString(key, CurrentCultureInfo);
}
}
public LocalizedResources(Type resource, string language = null)
: this(resource, new CultureInfo(language ?? DEFAULT_LANGUAGE))
{ }
public LocalizedResources(Type resource, CultureInfo cultureInfo)
{
CurrentCultureInfo = cultureInfo;
ResourceManager = new ResourceManager(resource);
MessagingCenter.Subscribe<object, CultureChangedMessage>(this,
string.Empty, OnCultureChanged);
}
private void OnCultureChanged(object s, CultureChangedMessage ccm)
{
CurrentCultureInfo = ccm.NewCultureInfo;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class ViewModelBase : INotifyPropertyChanged
{
public LocalizedResources Resources
{
get;
private set;
}
public ViewModelBase()
{
Resources = new LocalizedResources(typeof(AppResources), App.CurrentLanguage);
}
public void OnPropertyChanged([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class MainPageViewModel : ViewModelBase
{ }
public class SettingsViewModel : ViewModelBase
{
public List<string> Languages { get; set; } = new List<string>()
{
"EN",
"FR"
};
private string _SelectedLanguage;
public string SelectedLanguage
{
get { return _SelectedLanguage; }
set
{
_SelectedLanguage = value;
SetLanguage();
}
}
public SettingsViewModel()
{
_SelectedLanguage = App.CurrentLanguage;
}
private void SetLanguage()
{
App.CurrentLanguage = SelectedLanguage;
MessagingCenter.Send<object, CultureChangedMessage>(this,
string.Empty, new CultureChangedMessage(SelectedLanguage));
}
}

BInding textColor in Xamarin

In a Xamarin app I’m trying to bind a textcolor with a property in Message model.
public class Message : INotifyPropertyChanged
{
public string text { get; set; }
public Color color { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
The task is, when I click on a label in a collectionview the text should change to Gray.
I can change the color in the ObservableCollection:
this.messages = new ObservableCollection();
(that’s works, and if I delete an entry in the ObservableCollection the screen updates)
But when I change the color in the message model, the screen doesn’t update.
I use MVVMhelpers, and I would like to use that to solve the problem, if possible.
best regards..
You could change the item color to gray when you click the item to triger the SelectionChanged event of CollectionView.
Xaml:
<CollectionView ItemsSource="{Binding messages}" SelectionMode="Single" SelectionChanged="CollectionView_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding text}" TextColor="{Binding color}"></Label>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code behind:
public partial class Page2 : ContentPage
{
public ObservableCollection<Message> messages { get; set; }
public Page2()
{
InitializeComponent();
messages = new ObservableCollection<Message>()
{
new Message(){ text="A", color="Red"},
new Message(){ text="B", color="Red"},
new Message(){ text="C", color="Red"},
};
this.BindingContext = this;
}
private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var previousItem = e.PreviousSelection.FirstOrDefault() as Message;
var currentItem = e.CurrentSelection.FirstOrDefault() as Message;
currentItem.color = "Gray";
if (previousItem!=null)
{
previousItem.color = "Red";
}
}
}
public class Message : INotifyPropertyChanged
{
private string _text;
public string text
{
get
{
return _text;
}
set
{
_text = value;
OnPropertyChanged("text");
}
}
private string _color;
public string color
{
get
{
return _color;
}
set
{
_color = value;
OnPropertyChanged("color");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
super, great thanks.
I should also add
<DataTemplate x:DataType="{x:Type Models:Message}">

ReactiveList does not update in the GUI

I'm trying to make good use of ReactiveList and I think I'm close.
My expectation is that only "toyota" is shown after the user presses the filter button
XAML (yes, quick n dirty, no command for the Filter)
<Window
x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525">
<StackPanel>
<ComboBox
ItemsSource="{Binding Path=CarsVM}"
DisplayMemberPath="Name" />
<Button
Click="ButtonBase_OnClick">
Filter
</Button>
</StackPanel>
</Window>
The code
using System.Windows;
using ReactiveUI;
namespace WpfApplication1
{
public partial class MainWindow
{
private readonly ViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new ViewModel();
DataContext = _viewModel;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
_viewModel.ChangeFilter();
}
}
}
public class CarViewModel : ReactiveObject
{
private bool _isVisible = true;
public CarViewModel(string name)
{
Name = name;
}
public bool IsVisible
{
get { return _isVisible; }
set
{
_isVisible = value;
this.RaiseAndSetIfChanged(ref _isVisible, value);
}
}
public string Name { get; set; }
}
public class ViewModel
{
private readonly ReactiveList<CarViewModel> _cars = new ReactiveList<CarViewModel>
{
new CarViewModel("bmw"),
new CarViewModel("toyota"),
new CarViewModel("opel")
};
public ViewModel()
{
_cars.ChangeTrackingEnabled = true;
CarsVM = _cars.CreateDerivedCollection(x => x, x => x.IsVisible);
}
public IReactiveDerivedList<CarViewModel> CarsVM { get; set; }
public void ChangeFilter()
{
foreach (var car in _cars)
{
car.IsVisible = car.Name.Contains("y");
}
}
}
Your bug is in the setter of IsVisible. By pre-assigning the value of _isVisible, RaiseAndSetIfChanged always thinks that the value has never changed. Remove _isVisible = value; and everything should work.

Binding listbox with MVVM Windows Phone

Hi i'm new using MVVM and i'm trying to binding a listbox but it doesn't work. Here's my code
Model
public class Musicmodel : INotifyPropertyChanged
{
//variables privadas
private String _artista;
private Uri _href;
private String _informacion;
private Double _Dvalue;
public String artista
{
get
{
return this._artista;
}
set
{
this._artista= value;
this.RaisePropertyChanged("artista");
}
}
public Uri href {
get {
return this._href;
}
set
{
this._href = value;
this.RaisePropertyChanged("href");
}
}
public String informacion {
get
{
return this._informacion;
}
set
{
this._informacion = value;
this.RaisePropertyChanged("informacion");
}
}
public Double Dvalue
{
get
{
return this._Dvalue;
}
set
{
this._Dvalue = value;
this.RaisePropertyChanged("Dvalue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
ViewModel
public class DownloadFileViewModel : INotifyPropertyChanged
{
private WebClient clienteDownload;
private ObservableCollection<Model.Music>_musicSource= new ObservableCollection<Model.Music>();
public ObservableCollection<Model.Music> musicSource
{
get
{
return this._musicSource;
}
set
{
this._musicSource = value;
RaisePropertyChanged("musicSource");
}
}
private int index = 0;
//request para descargar la canción
public void request(Model.Musicmodel item)
{
this.clienteDownload = new WebClient();
this.clienteDownload.DownloadProgressChanged += new DownloadProgressChangedEventHandler(clienteDownload_DownloadProgressChanged);
//agregamos el item al music
this.musicSource.Add(item);
this.clienteDownload.OpenReadAsync(this.musicSource[index].href);
}
private void clienteDownload_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
this.musicSource[index].Dvalue=(double)e.ProgressPercentage;
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
View
<ListBox x:Name="list" ItemsSource="{Binding musicSource}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding artista}"/>
<ProgressBar Value="{Binding Dvalue}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code Behind
protected override void OnNavigatedTo(NavigationEventArgs e)
{
DownloadFileViewModel download = new DownloadFileViewModel();
Model.Music newMusic = new Model.Music() { href = new Uri("http://media1.li.ru/b/4/mp3/2/95366/953662_14Friday_Im_In_Love.mp3", UriKind.Absolute), artista = "the cure" };
download.request(newMusic);
this.DataContext = download;
base.OnNavigatedTo(e);
}
I've debuged this and the download works fine and my ObservableCollection fills correctly whithout any problem but when i try to binding my listbox fails.
please what do i'm doing wrong?
thanks
The problem is quite simple. You initialize your musicSource property at the begining in
private ObservableCollection<Model.Music>_musicSource= new ObservableCollection<Model.Music>();
And then just add stuff to it after the request completes. The RaiseProperyChanged("Property") will only fire when you add a new observable collection but not when you add items to it.
Add this line again to the end of the request (when you populate your musicSource)
RaisePropertyChanged("musicSource");
This will trigger another update in the view
EDIT:
Another approach is to have an additional field like
private ObservableCollection<Model.Music>_anotherMusicSource= new ObservableCollection<Model.Music>();
And do everything on it and after that just say:
musicSource = _anotherMusicSource;
This will then trigger the notification and everything should work
You have an underscore in your property name
private ObservableCollection<Model.Musicmodel> musicSource= new ObservableCollection<Model.Musicmodel>();
public ObservableCollection<Model.Musicmodel> _musicSource
{
get
{
return this.musicSource;
}
set
{
this.musicSource = value;
RaisePropertyChanged("musicSource");
}
}
You have this mixed up - the underscore should (traditionally) be on the private member, not the public - your binding is targeting musicSource which is private
The standard convention which is advocated for .NET is:
// Private member variables
private int _someInteger;
// Public ones
public int SomeInteger { get { ... } set { ... } }

Silverlight 4, Validation error state doesn't get reflected in my own custom UserControl

Validation error state doesn't get reflected in my UserControl. I was following this similar StackOverflow question to solve it. I am not using an MVVM approach by choice. It's using WCF RIA Services with Entity Framework.
But it didn't seem to help me, what am I missing, or why is my scenario different?
Note: If I put a TextBox (not inside the UserControl) in my main page, it shows validation error.
This is my UserControl code:
<UserControl x:Class="TestApp.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="25" d:DesignWidth="120">
<Grid x:Name="LayoutRoot" Background="White">
<TextBox x:Name="TextBox" />
</Grid>
</UserControl>
and this is the code behind of the UserControl:
public partial class MyUserControl : UserControl, INotifyDataErrorInfo
{
public MyUserControl()
{
InitializeComponent();
this.TextBox.BindingValidationError += MyUserControl_BindingValidationError;
Loaded += MyUserControl_Loaded;
this.TextBox.Unloaded += MyUserControl_Unloaded;
}
private void MyUserControl_Loaded(object sender, RoutedEventArgs e)
{
this.TextBox.SetBinding(TextBox.TextProperty,
new Binding()
{
Source = this,
Path = new PropertyPath("Value"),
Mode = BindingMode.TwoWay,
NotifyOnValidationError = false,
ValidatesOnExceptions = true,
ValidatesOnDataErrors = true,
ValidatesOnNotifyDataErrors = true
});
}
private void MyUserControl_Unloaded(object sender, RoutedEventArgs e)
{
this.TextBox.ClearValue(TextBox.TextProperty);
}
public static DependencyProperty ValueProperty =
DependencyProperty.Register("Value",
typeof(string), typeof(MyUserControl),
new PropertyMetadata(null));
public static void ValuePropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
((MyUserControl)dependencyObject).NotifyErrorsChanged("Value");
}
public string Value
{
get { return ((string)base.GetValue(ValueProperty)).Trim(); }
set { base.SetValue(ValueProperty, string.IsNullOrEmpty(value) ? " " : value.Trim()); }
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public IEnumerable GetErrors(string propertyName)
{
IEnumerable returnValue = null;
var errorMessage = "";
if (propertyName == "Value")
{
if (Validation.GetErrors(this).Count == 0)
{
errorMessage = "";
}
else
{
errorMessage = Validation.GetErrors(this).First().ErrorContent.ToString();
}
if (String.IsNullOrEmpty(errorMessage))
{
returnValue = null;
}
else
{
returnValue = new List<String> { errorMessage };
}
}
return returnValue;
}
public bool HasErrors
{
get { return Validation.GetErrors(this).Any(); }
}
private void MyUserControl_BindingValidationError(object sender, System.Windows.Controls.ValidationErrorEventArgs e)
{
this.NotifyErrorsChanged("Value");
}
public void NotifyErrorsChanged(string propertyName)
{
if (ErrorsChanged != null)
{
ErrorsChanged(this, new System.ComponentModel.DataErrorsChangedEventArgs(propertyName));
}
}
}
I am using it like this in my main page:
<my:MyUserControl x:Name="UC"
Value="{Binding Path=Days, Mode=TwoWay,
NotifyOnValidationError=True,
ValidatesOnNotifyDataErrors=True}" />
I am also using validation attributes in System.ComponentModel.DataAnnotations, this is how it looks in RIAService.metadata.cs class:
internal sealed class tblRiskRegisterMetadata
{
//Metadata classes are not meant to be instantiated.
private tblRiskRegisterMetadata()
{ }
[Range(0, 1000, ErrorMessage = "Days should be 0-100")]
public int Days{ get; set; }
}
new PropertyMetadata(null) -> new PropertyMetadata(null, **ValuePropertyChangedCallback**)

Resources