Binding



  • 0 comments  /  posted by  Denislav Savkov  on  Sep 17, 2008 (3 months ago)

    Typically validation occurs when in a two-way binding the business object (source property) is updated with data from the user input (target property). During the update there are two places where validation occurs.

    1. First, the binding engine uses a implementation of IValueConverter to convert the data from one type to the other. Usually this converter has some validation. Unfortunately, Silverlight doesn't provide a way for custom validation for binding, like in WPF. There is no ValidationRule class that is used in WPF to provide the custom rule for the validation.
    2. Second, the setter of the property being updated may contain validation.

    This means that if you want custom validation you have to write your own type converters or to place the validation inside the setters of your business object. In case the validation fails, an exception is thrown in a setter or in a converter , then a BindingValidationError event is raised (it is member of System.Windows.Controls.FrameworkElement). This makes it easy for you to prompt the user to enter correct data. BindingValidationError can be raised only when both NotifyOnValidationError and ValidatesOnExceptions are true. Setting ValidatesOnExceptions to true tells the binding engine to report validation exceptions and setting NotifyOnValidationError to true tells the binding engine to raise the BindingValidationError event when a validation error occurs. This, combined with the TwoWay mode of binding, gives us the following:

    XAML

    <TextBox x:Name="bindingTarget" Text="{Binding Path=Age,
                                                   Source={StaticResource person},
                                                   Mode=TwoWay,
                                                   NotifyOnValidationError=true,
                                                   ValidatesOnExceptions=true}" />. 
    C#
    bindingTarget.BindingValidationError +=
                 new EventHandler<ValidationErrorEventArgs>( bindingTarget_BindingValidationError );

    Here Age is a property of the object person.

    Strangely BindingValidationError won’t raise if you use custom type converters in your binding. Check out our demo and download the source.

    That's it!



  • 0 comments  /  posted by  Martin Mihaylov  on  Sep 09, 2008 (3 months ago)

    If you're not familiar with the value converters read this. The methods generated by the VisualStudio when creating a custom class that implements System.Windows.Data.IValueConverter have several arguments. One of them is of type object and is called parameter.

    public class DateTimeConverter : System.Windows.Data.IValueConverter
    {
        public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )...
       
        public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )...
    }

    In this example we bind to an object of type Book:

    public class Book
    {
        public DateTime PublishDate { get; set; }
    }

    We can pass this argument from the code or from the Xaml:

    Xaml

    <TextBlock Text="{Binding PublishDate, Converter={StaticResource DateTimeConverter}, ConverterParameter=true}"/>

    C#

    Book myBook = new Book();
    myBook.PublishDate = DateTime.Now;
     
    Binding binding = new Binding( "PublishDate" );
    binding.Source = myBook;
    binding.Converter = new DateTimeConverter();
    binding.ConverterParameter = true;

    That's it!

  • 0 comments  /  posted by  Martin Mihaylov  on  Sep 09, 2008 (3 months ago)

    First let's explain what the convertors can be used for. Imagine you bind to an object's property, but the property is not formatted to your likings. In this case you can use converters. For example we bind to the PublishDate property of a Book object and want the date to be formatted like this - "dd MMM, yyyy".

    Book myBook = new Book();
    myBook.PublishDate = DateTime.Now;

    First let's create our converter. It's a class that implements System.Windows.Data.IValueConverter (using VisualStudio you can easily generate the structure of the class).

    public class DateTimeConverter : System.Windows.Data.IValueConverter
        {
            public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
            {
                return ( ( DateTime )value ).ToString( "dd MMM, yyyy" );
            }
     
            public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )...
        }

    We have a method Convert, which takes several parameters. The value parameter will be the value of the property that we want to convert. It's of type object so we cast it to DateTime and use the ToString method of the DateTime class to format it as we please. Let's now see how to use this convertor in the Xaml. First declare the namespace of the converter:

    <UserControl x:Class="LayoutExperiments.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:LayoutExperiments">

    My DateTimeConverter class is defined in the Page.cs file, but you can put anywhere you want. The next step is to add the Resource to the UserControl of type DateTimeConverter:

    <UserControl.Resources>
        <local:DateTimeConverter x:Key="DateTimeConverter" />
    </UserControl.Resources>

    And the last thing is to configure the binding and add the converter to it:

    <TextBlock x:Name="MyText" Text="{Binding PublishDate, Converter={StaticResource DateTimeConverter}}"></TextBlock>

    This can also be done entirely in C# using the System.Windows.Data.Binding class:

    TextBlock myText = new TextBlock();
     
    Book myBook = new Book();
    myBook.PublishDate = DateTime.Now;
     
    Binding binding = new Binding("PublishDate");
    binding.Source = myBook;
    binding.Converter = new DateTimeConverter();
     
    myText.SetBinding( TextBlock.TextProperty, binding );
    LayoutRoot.Children.Add( myText );

    So thanks to this converter the value of the TextBlock will be "09 Sep, 2008" instead of "9/9/2008 3:31:PM".

    That's it!

  • 0 comments  /  posted by  Martin Mihaylov  on  Sep 09, 2008 (3 months ago)

    If you want to bind an object's property to a control's property you can do it the following way.First let's create the object taht the control will bind to (for example a class called Book):

    public class Book
    {
        public string Title { get; set; }
        public string Description { get; set; }
    }

    After that define your control in the Xaml (a TextBlock for example):

    <TextBlock x:Name="MyText" Text="{Binding Title}"></TextBlock>

    Using the binding syntax we bind the Text property to the Title property of the Book object. In the codebehind we create an instance of the Book object and set the DataContext of the control to it:

    Book myBook = new Book();
    myBook.Title = "Silverlight book";
    myBook.Description = "A book about Silverlight.";
    MyText.DataContext = myBook;

     

    This can also be done entirely in C#, using the System.Windows.Data.Binding class:

    TextBlock myText = new TextBlock();
     
    Book myBook = new Book();
    myBook.PublishDate = DateTime.Now;
     
    Binding binding = new Binding("Title");
    binding.Source = myBook;
     
    myText.SetBinding( TextBlock.TextProperty, binding );
    LayoutRoot.Children.Add( myText );

     

    We pass the name of the property we want to bind to as a parameter to the constructor of the Binding class. After that we set the source of the binding to the desired object and set the binding to the Text property of the TextBlock control.

    That's it!

  • 1 comments  /  posted by  Denislav Savkov  on  Aug 29, 2008 (4 months ago)


    Path is used to specify then name of the property of the underlying object to bind to. Additional you are able to use indirect property targeting to specify a sub-property of a property of the object. Currently it is not possible to bind to indexed properties.

    Xaml

    <TextBlock Text="{Binding Name}"/>
    <TextBlock Text="{Binding Path=Name}"/>
    <TextBlock Text="{Binding Path=Account.OpenDate}"/>
    <TextBlock Text="{Binding Path=Property1.Property2.Property3}"/>

    That's it!

  • 0 comments  /  posted by  Denislav Savkov  on  Aug 29, 2008 (4 months ago)


    Currently there is no way to do that by using binding. Instead, you can make a List<> and add the values from the enumeration in the list. Then you can use this list as data source.

    C#

    List<Dock> enumDataSource = new List<Dock>() { Dock.Left, Dock.Top, Dock.Right, Dock.Bottom };
    this.lbDock.ItemsSource = enumDataSource;

    where lbDock is of type ListBox.

    That's it!

  • 0 comments  /  posted by  Denislav Savkov  on  Aug 29, 2008 (4 months ago)

    When the value of a property is changed it should notify the bound objects that the property value has changed. You can do that by implementing changed event.

    C#

    public class Client
    {
        private string name;
        public event EventHandler NameChanged;
     
        public string Name
        {
            get
            {
                return this.Name;
            }
            set
            {
                if ( this.name == value )
                    return;
     
                this.name = value;
                this.OnNameChanged( EventArgs.Empty );
            }
        }
     
        protected virtual void OnNameChanged( EventArgs e )
        {
            if ( this.NameChanged != null )
                this.NameChanged( this, e );
        }
    }

    Note that the value is changed only if the new value is different. Thus we prevent the firing of the NameChanged event when the actual value is not changed.

    Alternative way is to implement INotifyPropertyChanged interface.

    That's it!

  • 1 comments  /  posted by  Denislav Savkov  on  Aug 29, 2008 (4 months ago)

    INotifyPropertyChanged interface is used to notify that a property has been changed and thus to force the bound objects to take the new value.

    C#

    public class Client : INotifyPropertyChanged
    {
        private string name;
        public event PropertyChangedEventHandler PropertyChanged;
     
        public string Name
        {
            get
            {
                return this.Name;
            }
            set
            {
                if ( this.name == value )
                    return;
     
                this.name = value;
                this.OnPropertyChanged( new PropertyChangedEventArgs( "Name" ) );
            }
        }
     
        protected virtual void OnPropertyChanged( PropertyChangedEventArgs e )
        {
            if ( this.PropertyChanged != null )
                this.PropertyChanged( this, e );
        }
    }

    Note that the value is changed only if the new value is different. Thus we prevent the firing of the PropertyChanged event when the actual value is not changed.

    Alternative way is to provide changed event for each property.

    That's it!