Silverlight 2.0 introduktion, del 7 - Data Binding

Hvis du er .NET udvikler så kender du garanteret udtrykket DataBinding. DataBinding i .NET handler om at binde noget data/objekt (også kaldet en kilde) fast til et mål såsom en webkontrol og få ens data til at se præsentabelt ud. Det er samme princip i Silverlight (det er også .NET), men måden du DataBinder på er lidt anderledes strukket sammen end hvad du måske er vant til.

OneTime, OneWay, TwoWay

Der findes disse tre Silverlight-spefikke databinding muligheder, og udover det kan du bruge databinding som du ellers er vant til fra WPF, ASP.NET, WinForms etc.

OneTime betyder "bind kilden til målet" - også er den databinding process overstået (lidt kedeligt, men sandt). OneTime bør kun kaldes med en kilde der sjældent eller aldrig ændre sig - en liste med postnumre vil være et glimrende eksempel på OneTime databinding.

OneWay betyder "bind kilden til målet, og ændrer målet når data ændres i kilden".

TwoWay betyder "bind kilden til målet, og ændrer målet når enten data ændres i kilden eller i målet"

Eksempel

Nu sidder du sikkert og tænker "hvad snakker manden om ... kilden, målet, one-two way...bla bla ???". Så lad os da se på et eksempel omkring OneWay og TwoWay. Koden er ren test, så hån mig ikke.

<Canvas>
    <Button Click="save_Click" x:Name="save" Content="Gem" Margin="190,100,0,0"  />
    <TextBox x:Name="name" Margin="80, 130, 0, 0" Width="100" />
</Canvas>

Vi har et simpelt canvas med en knap og en textbox. Vores TextBox skal have en værdi hver gang vi trykker på knappen, og det kræver derfor en DataBinding på vores TextBox. Vi har bare ikke noget at DataBinde med lige nu...så laver vi da lige et objekt.

public class Person : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            if( PropertyChanged != null ) {
                PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
            }
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

Det er et meget simpelt objekt du ser her og du undre dig sikkert over en ting: INotifyPropertyChanged. For at vores databinding vil virke efter hensigten bliver vi nødt at implementere INotifyPropertyChanged interfacet på klasse som skal bruges i databinding processen. INotifyPropertyChanged giver os besked om hvornår en property ændres i den bagvedliggende klasse.

Som du kan se, så har jeg i vores setter (set) sørget for noget logik der kalder PropertyChanged eventen hvis den ikke er null.

I vores code-behind til .xaml filen har jeg skrevet lidt kode der rent faktisk laver bindingen mellem kontrollen og klassen.

public partial class Page : UserControl
{
    Person person = new Person();

    public Page()
    {
        InitializeComponent();
        System.Windows.Data.Binding binding = new System.Windows.Data.Binding( "Name" );
        binding.Mode = System.Windows.Data.BindingMode.OneWay;
        binding.Source = person;

        name.SetBinding( TextBox.TextProperty, binding );
    }

    private void save_Click( object sender, RoutedEventArgs e )
    {
        person.Name = DateTime.Now.Second.ToString();
    }
}

Vi fortæller via SetBinding her, at der skal bindes på TextBoxens (name) TextProperty og, at den skal føre selve bindings (binding) objektet med i kaldet. Læg også mærke til at vi har valgt OneWay til at starte med. Nu bliver det spændende.

Når vi binder med OneWay bliver vores bagved liggende kilde (Name egenskaben på Person objektet) ikke informeret om ændringer der sker i præsentationslaget. Det er tydeligt at se hvis du sætter et break point på Name egenskaben (linie 19 i Person.cs) og indtaster noget i name TextBoxen og flytter fokus væk fra TextBoxen. Hvis du ændrer BindingMode fra OneWay til TwoWay så vil du opdage at dit præsentationslag (xaml) faktisk opdatere din kilde (Name property på Person objektet). Det er lidt svært at forklare med ord, så derfor synes jeg du skal downloade kildekoden og selv steppe dig igennem koden med en debugger.

Du kan også databinde direkte fra XAML, og det gør du ved at bruge DataBinding{} i f.eks Text attributen på vores name TextBox.

<TextBox x:Name="name" Text="{Binding Path=Name, Mode=TwoWay}" Margin="80, 130, 0, 0" Width="100" />

Så skal du bare lige huske at slette alt det der står i din code-behind constructor (Page(){}) så det kommer til at se således ud:

public partial class Page : UserControl
{
    Person person = new Person();

    public Page()
    {
        InitializeComponent();

        name.DataContext = person;
    }

    private void save_Click( object sender, RoutedEventArgs e )
    {
        person.Name = DateTime.Now.Second.ToString();
    }
}

Der er et par ting som ikke er så heldige med begge løsninger. Det er weak-typing af din CLR objekter. Kig f.eks på:

<TextBox x:Name="name" Text="{Binding Path=Name, Mode=TwoWay}" Margin="80, 130, 0, 0" Width="100" />

Path=Name har intet med strongly typed at gøre og det er hverken vildt heldigt eller fedt. Det samme kan man sige om selve databindings modellen i Silverlight kontra WPF. De er vidt forskellige selvom de deler samme terminologi og i bund og grund burde være bygget på samme kerne.

Det er helt sikkert at databinding er kommet for at blive, men vælg dit mode med omhu og husk hvad kalder hvad og hvornår.