I Create A Custom View named ScrollView
The ability of this view is just like its name
this view of children include only Label,
And support scroll and click,even drop
In actual use,scrolling and clicking works well,dropping was too
but the Label will invisible when drop
this label still occupy the layout,just invisible
the drop func will still execute when i drop once again in that invisible label
I execute apps on Android
ScrollPicker of XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DebugTest.MyView.ApplyView.ScrollPicker">
<ContentView.Content>
<ScrollView x:Name="ScrollView" x:FieldModifier="Public">
<StackLayout x:Name="MainView" x:FieldModifier="Public">
</StackLayout>
</ScrollView>
</ContentView.Content>
</ContentView>
And C# Code
public partial class ScrollPicker : ContentView
{
public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(StackOrientation), typeof(ScrollPicker), StackOrientation.Vertical, propertyChanged: Orientation_Changed);
public static readonly BindableProperty ItemSourceProperty = BindableProperty.Create("ItemSource", typeof(List<string>), typeof(ScrollPicker), propertyChanged: ItemSource_Changed);
private static void Orientation_Changed(BindableObject bindable, object oldValue, object newValue)
{
if(((StackOrientation)newValue) == StackOrientation.Horizontal)
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Horizontal;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Horizontal;
}
else
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Vertical;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Vertical;
}
}
private static void ItemSource_Changed(BindableObject bindable, object oldValue, object newValue)
{
var view = ((ScrollPicker)bindable);
view.ItemSourceList = (List<string>)newValue;
view.ResetLabelList();
}
public StackOrientation Orientation
{
get { return (StackOrientation)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public List<string> ItemSource
{
get { return (List<string>)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public DropGestureRecognizer DropGR;
public event EventHandler<IndexArgs> ItemClickEvent;
public event EventHandler<IndexArgs> DropEvent;
public List<string> ItemSourceList;
public List<Label> LabelList = new List<Label>();
public ScrollPicker()
{
InitializeComponent();
ItemClickEvent += ScrollPicker_ItemClickEvent;
DropEvent += ScrollPicker_DropEvent;
}
private void ScrollPicker_DropEvent(object sender, EventArgs e)
{
}
private void CE_Click(Element obj)
{
if(LabelList != null && LabelList.Count != 0)
{
if(LabelList.Exists(label => label == obj))
{
int index = LabelList.IndexOf((Label)obj);
ItemClickEvent.Invoke(obj, new IndexArgs(index));
}
}
}
private void ScrollPicker_ItemClickEvent(object sender, EventArgs e)
{
}
private void ResetLabelList()
{
MainView.Children.Clear();
LabelList.Clear();
if (ItemSourceList != null && ItemSourceList.Count != 0)
{
for(int i=0;i< ItemSourceList.Count;i++)
{
//创建Label
Label temp = CreateSubView(ItemSourceList[i]);
//绑定CE
ClickEffect CE = new ClickEffect();
CE.Click += CE_Click;
Global.ClickEffect_BindClickEffect(temp, CE);
//添加Drop
DropGR = new DropGestureRecognizer();
DropGR.DragOver += DropGR_DragOver;
DropGR.DragLeave += DropGR_DragLeave;
DropGR.Drop += DropGR_Drop;
temp.GestureRecognizers.Add(DropGR);
//添加视图
LabelList.Add(temp);
MainView.Children.Add(temp);
if (i == 0) temp.Opacity = 0.1;
}
}
}
private void DropGR_DragOver(object sender, DragEventArgs e)
{
}
private void DropGR_DragLeave(object sender, DragEventArgs e)
{
}
private void DropGR_Drop(object sender, DropEventArgs e)
{
var view = (sender as GestureRecognizer).Parent as View;
int index = MainView.Children.IndexOf(view);
DropEvent.Invoke(view, new IndexArgs(index));
}
private Label CreateSubView(string text)
{
Label label = new Label()
{
Text = text,
FontSize = 20,
WidthRequest = 75,
VerticalTextAlignment = TextAlignment.Start,
HorizontalTextAlignment = TextAlignment.Start,
};
return label;
}
}
public abstract partial class ScrollPickerItemView : ContentView
{
}
public class IndexArgs: EventArgs
{
public int Index;
public IndexArgs(int index =0)
{
Index = index;
}
}
public enum ScrollPickerOrientation
{
Vertical,
Horizontal
}
How Use In Page:
......
<AbsoluteLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,0.1" BackgroundColor="#66CCFF">
<Label AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,0.2,1"
Text="Project:" FontSize="Large" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<applyview:ScrollPicker x:Name="ProjectPicker" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="1,0,0.8,1" Orientation="Horizontal"
ItemClickEvent="ProjectPicker_ItemClickEvent" DropEvent="ProjectPicker_DropEvent"/>
</AbsoluteLayout>
I don't know how to solve this problem. Can someone help me?
I'm afraid I've found the problem
I added a PropertyChanged callback function to each label. I was surprised to find that when I triggered the drop function, the text of the label changed and became Empty
That's why he disappeared!!!
I'm very sorry, but it's a shame
Although the problem has been solved, I still don't know why the text of label becomes null when I trigger the drop function
Related
I am trying to validate a entry value using the entry behaviors. In order to do that I need to pass the value to behavior class from another control in the same xaml.
I have below two controls
<Entry
x:Name="RegisterQty"
Grid.Column="0"
WidthRequest="120"
TextChanged="RegisterQty_TextChanged"
TextColor="Black"
HorizontalOptions="Start"
VerticalOptions="Center"
FontAttributes="Bold"
PlaceholderColor="Black"
Keyboard="Numeric"
FontSize="20">
<Entry.Behaviors>
<local:RegisterQtyBehavior LoadQty = "{Binding BindingContext.qty, Source={x:Reference RegisterQty}} "/>
</Entry.Behaviors>
</Entry>
public class RegisterQtyBehavior : Behavior<Entry>
{
public static readonly BindableProperty LoadQtyProperty = BindableProperty.Create(nameof(LoadQty), typeof(double), typeof(RegisterQtyBehavior), 0);
public double LoadQty
{
get { return (double)GetValue(LoadQtyProperty); }
set { SetValue(LoadQtyProperty, value); }
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
double result = LoadQty;
bool isValid = double.TryParse(args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Red : Color.Default;
}
}
I want to pass label binding Qty to entry control. How could i achieve this? Adding directly LoadQty = "{Binding Qty}"
does not work.
According to your description, you want to parameter in entry behaviors using binding, am I right? if yes, I do one sample you can take a look:
<Entry x:Name="registerqty">
<Entry.Behaviors>
<local:RegisterQtyBehavior LoadQty="{Binding BindingContext.qty, Source={x:Reference registerqty}}}" />
</Entry.Behaviors>
</Entry>
public partial class Page37 : ContentPage, INotifyPropertyChanged
{
private int _qty;
public int qty
{
get { return _qty; }
set
{
_qty = value;
RaisePropertyChanged("qty");
}
}
public Page37 ()
{
InitializeComponent ();
qty = 100;
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class RegisterQtyBehavior:Behavior<Entry>
{
public static readonly BindableProperty LoadQtyProperty = BindableProperty.Create("LoadQty", typeof(int), typeof(RegisterQtyBehavior), defaultValue: 0);
public int LoadQty
{
get { return (int)GetValue(LoadQtyProperty); }
set
{
SetValue(LoadQtyProperty,value);
}
}
protected override void OnAttachedTo(Entry entry)
{
entry.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(entry);
}
protected override void OnDetachingFrom(Entry entry)
{
entry.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(entry);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
//double result;
//bool isValid = double.TryParse(args.NewTextValue, out result);
//((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
if(!string.IsNullOrEmpty(args.NewTextValue))
{
if (Convert.ToInt32(args.NewTextValue) > LoadQty)
{
((Entry)sender).TextColor = Color.Red;
}
else
{
((Entry)sender).TextColor = Color.Default;
}
}
}
}
I have a custom checkbox control that I created with an ICommand property and the corresponding bindable property (my checkbox is a Xamarin.Forms XAML Page), the code is:
CheckBox.xaml
<Image x:Name="imgCheckBox"
WidthRequest="20"
HeightRequest="20"/>
<Label x:Name="lblCheckBox"
TextColor="Black"
VerticalOptions="CenterAndExpand"/>
<TapGestureRecognizer Tapped="OnCheckBoxTapped"/>
CheckBox.xaml.cs
public partial class CheckBox : ContentView
{
private static ImageSource uncheckedImage;
private static ImageSource checkedImage;
public CheckBox()
{
InitializeComponent();
uncheckedImage = ImageSource.FromResource("cbUnchecked.png");
checkedImage = ImageSource.FromResource("cbChecked.png");
imgCheckBox.Source = uncheckedImage;
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create<CheckBox, bool>(
checkbox =>
checkbox.IsChecked,
false,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, newValue);
}
});
public bool IsChecked
{
set { SetValue(IsCheckedProperty, value); }
get { return (bool)GetValue(IsCheckedProperty); }
}
void OnCheckBoxTapped(object sender, EventArgs args)
{
IsChecked = !IsChecked;
if (IsChecked)
{
imgCheckBox.Source = checkedImage;
}
else
{
imgCheckBox.Source = uncheckedImage;
}
}
public static readonly BindableProperty CheckBoxCommandProperty =
BindableProperty.Create<CheckBox, ICommand>(
checkbox =>
checkbox.CheckBoxCommand,
null,
BindingMode.TwoWay,
propertyChanged: (bindable, oldValue, newValue) =>
{
CheckBox checkbox = (CheckBox)bindable;
EventHandler<bool> eventHandler = checkbox.CheckedChanged;
if (eventHandler != null)
{
eventHandler(checkbox, checkbox.IsChecked);
}
});
public event EventHandler<bool> CheckedChanged;
public ICommand CheckBoxCommand
{
get { return (ICommand)GetValue(CheckBoxCommandProperty); }
set { SetValue(CheckBoxCommandProperty, value); }
}
}
This checkbox implementation is on another Page called TermsAndConditionsPage, that is also a a Xamarin.Forms XAML Page, the code of the implementation is:
<toolkit:CheckBox Text="{Binding txtCheckBox}"
FontSize="Small"
CheckBoxCommand="{Binding OnCheckBoxTapChanged}"
IsChecked="{Binding IsCheckedChanged, Mode=TwoWay}"/>
<Button Text="Next"
Command="{Binding Next_OnClick}"
IsEnabled="{Binding Next_IsEnabled}"
HorizontalOptions="CenterAndExpand"
Clicked="OnNextClicked"/>
The Code Behind of this page is empty (Constructur with InitializeComponent()).
I also have the ViewModel of this page with this code:
TermsAndConditionsViewModel.cs
private string _txtCheckBox;
public string txtCheckBox
{ get { return _txtCheckBox; }
set
{
_txtCheckBox = value;
OnPropertyChanged("txtCheckBox");
}
}
private bool _Next_IsEnabled;
public bool Next_IsEnabled
{
get { return _Next_IsEnabled; }
set
{
_Next_IsEnabled = value;
OnPropertyChanged("Next_IsEnabled");
}
}
private bool _IsCheckedChanged;
public bool IsCheckedChanged
{
get { return _IsCheckedChanged; }
set
{
_IsCheckedChanged = value;
OnPropertyChanged("IsCheckedChanged");
}
}
public ICommand Next_OnClick { get; set; }
public ICommand OnCheckBoxTapChanged { get; set; }
public TermsAndConditionsViewModel()
{
txtCheckBox = "I agree with the terms and conditions";
Next_OnClick = new Command(NextClicked);
OnCheckBoxTapChanged = new Command(CheckBoxTapped);
}
private void CheckBoxTapped()
{
if (IsCheckedChanged)
{ Next_IsEnabled = true; }
else
{ Next_IsEnabled = false; }
}
private void NextClicked()
{ App.Current.MainPage = new Views.HelloWorld(); }
#region INPC
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{ PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
Now, the question time: the problem I'm having is the CheckBoxTapped Command is not working, I mean, it doesn't do anything, although the checkbox image changes every time I touch it, it does not change the Next_IsEnabled property of my button. I'd like to know what I am missing here to make this command work properly.
EDIT
What I'm looking for is a Command that behaves similarly to the one that Buttons have.
Thanks all for your time!
Since the original answer is now obsolete, here is the new method:
using System.Windows.Input;
public partial class MyControlExample : ContentView
{
// BindableProperty implementation
public static readonly BindableProperty CommandProperty =
BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(MyControlExample), null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
// Helper method for invoking commands safely
public static void Execute(ICommand command)
{
if (command == null) return;
if (command.CanExecute(null))
{
command.Execute(null);
}
}
public MyControlExample()
{
InitializeComponent();
}
// this is the command that gets bound by the control in the view
// (ie. a Button, TapRecognizer, or MR.Gestures)
public Command OnTap => new Command(() => Execute(Command));
}
Something like that (pseudocode):
public class YourClassName : View
{
public YourClassName()
{
var gestureRecognizer = new TapGestureRecognizer();
gestureRecognizer.Tapped += (s, e) => {
if (Command != null && Command.CanExecute(null)) {
Command.Execute(null);
}
};
var label = new Label();
label.GestureRecognizers.Add(gestureRecognizer);
}
public static readonly BindableProperty CommandProperty =
BindableProperty.Create<YourClassName, ICommand>(x => x.Command, null);
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
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.
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 { ... } }
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**)