TOC

This article has been localized into Danish by the community.

Databinding:

Reaktion på ændringer

Indtil videre i dette selvstudie har vi mest lavet bindinger mellem UI elementer og eksisterende klasser, men i virkelige applikationer vil du naturligvis binde til dine egne dataobjekter. Dette er fuldstændig lige så nemt, men når du starter på det, vil du opdage noget, som vil skuffe dig. Ændringer bliver ikke automatisk afspejlet, som de blev i de forrige eksempler. Som du vil lære i denne artikel, behøver du en smule ekstra arbejde for at få dette til at ske, men heldigvis gør WPF det rimelig nemt.

Reaktion på datakildeændringer

Der er to forskellige scenarier, du måske eller måske ikke ønsker at håndtere i forbindelse med ændringer i datakilden: Ændringer til listen af elementer og ændringer i de bundne egenskaber i hver af dataobjekterne. Hvordan de håndteres, afhænger af hvad, du gør og hvad du forsøger at opnå, men WPF kommer med to meget nemme løsninger, du kan bruge: ObservableCollection klassen og INotifyPropertyChanged interfacet.

Det følgende eksempel vil vise dig hvorfor, du behøver disse to ting:

<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ChangeNotificationSample" Height="150" Width="300">
	<DockPanel Margin="10">
		<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
			<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
			<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
			<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
		</StackPanel>
		<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
	</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;

namespace WpfTutorialSamples.DataBinding
{
	public partial class ChangeNotificationSample : Window
	{
		private List<User> users = new List<User>();

		public ChangeNotificationSample()
		{
			InitializeComponent();

			users.Add(new User() { Name = "John Doe" });
			users.Add(new User() { Name = "Jane Doe" });

			lbUsers.ItemsSource = users;
		}

		private void btnAddUser_Click(object sender, RoutedEventArgs e)
		{
			users.Add(new User() { Name = "New user" });
		}

		private void btnChangeUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				(lbUsers.SelectedItem as User).Name = "Random Name";
		}

		private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				users.Remove(lbUsers.SelectedItem as User);
		}
	}

	public class User
	{
		public string Name { get; set; }
	}
}

Prøv at køre det selv og se, at selv om du tilføjer noget til listen eller ændrer navnet på en af brugerne, bliver ingenting i UI opdateret. Eksemplet er ret simpelt, med en User klasse, som vil indeholde navnet på brugeren, en ListBox til at vise dem samt nogle knapper til at manipulere både listen og dens indhold. Listens ItemsSource er lavet som en hurtig liste med et bar brugere, som vi opretter i vindue-initialiseringen. Problemet er, at ingen af knapperne ser ud til at virke. Lad os rette det i to nemme trin.

Afspejling af ændringer i listens datakilde

Det første trin er at få UI til at reagere på ændringer i listekilden (ItemsSource), som når vi tilføjer eller sletter en bruger. Det, vi har brug for, er en liste, som giver besked om ændringer af indholdet til alle kontroller, som er bundet til den. Heldigvis har WPF en listetype, som gør dette. Den hedder ObservableCollection, og den bruges stort set som en almindelig List<T> - dog med nogle få forskelle.

I det endelige eksempel herunder, har vi simpelthen erstattet List<User> med en ObservableCollection<User> - det er alt, der kræves. Dette til få Add og Delete knapperne til at virke, men det gør ikke noget for "Change name" knappen, fordi den ændring sker på det bunde dataobjekt og ikke på kuldelisten. Det andet trin vil dog håndtere dette scenarie.

Afspejling af ændringer i dataobjekterne

Det andet trin er at lade vores brugerdefinerede User klasse implementere INotifyPropertyChanged interfacet. Ved at gøre det, bliver vores User objekter i stand til at give besked til UI laget om ændringer af dets egenskaber. Det er lidt mere omstændeligt end bare at ændre listetypen, som vi gjorde ovenfor, men det er stadig en af de simpleste måder at opnå disse automatiske opdateringer.

Det færdige og fungerende eksempel

Med de to ændringer beskrevet ovenfor har vi nu et eksempel, som VIL afspejle ændringer i datakilden. Det ser således ud:

<Window x:Class="WpfTutorialSamples.DataBinding.ChangeNotificationSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ChangeNotificationSample" Height="135" Width="300">
	<DockPanel Margin="10">
		<StackPanel DockPanel.Dock="Right" Margin="10,0,0,0">
			<Button Name="btnAddUser" Click="btnAddUser_Click">Add user</Button>
			<Button Name="btnChangeUser" Click="btnChangeUser_Click" Margin="0,5">Change user</Button>
			<Button Name="btnDeleteUser" Click="btnDeleteUser_Click">Delete user</Button>
		</StackPanel>
		<ListBox Name="lbUsers" DisplayMemberPath="Name"></ListBox>
	</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace WpfTutorialSamples.DataBinding
{
	public partial class ChangeNotificationSample : Window
	{
		private ObservableCollection<User> users = new ObservableCollection<User>();

		public ChangeNotificationSample()
		{
			InitializeComponent();

			users.Add(new User() { Name = "John Doe" });
			users.Add(new User() { Name = "Jane Doe" });

			lbUsers.ItemsSource = users;
		}

		private void btnAddUser_Click(object sender, RoutedEventArgs e)
		{
			users.Add(new User() { Name = "New user" });
		}

		private void btnChangeUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				(lbUsers.SelectedItem as User).Name = "Random Name";
		}

		private void btnDeleteUser_Click(object sender, RoutedEventArgs e)
		{
			if(lbUsers.SelectedItem != null)
				users.Remove(lbUsers.SelectedItem as User);
		}
	}

	public class User : INotifyPropertyChanged
	{
		private string name;
		public string Name {
			get { return this.name; }
			set
			{
				if(this.name != value)
				{
					this.name = value;
					this.NotifyPropertyChanged("Name");
				}
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		public void NotifyPropertyChanged(string propName)
		{
			if(this.PropertyChanged != null)
				this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
		}
	}
}

Resume

Som du kan se, er implementering af INotifyPropertyChanged ret nemt, men det skaber noget ekstra kode i dine klasser, og tilføjer en smule ekstra logik til dine egenskaber. Dette er den pris, du må betale, hvis du vil binde dine egne klasser og have ændringer afspejlet i UI med det samme. Naturligvis er du kun nødt til at kalde NotifyPropertyChanged i setter-metoden på egenskaberne, du binder til - resten kan forblive på den samme måde.

ObservableCollection classen er på den anden side meget let at håndtere - det kræver ganske enkelt, at du bruger denne specifikke listetype i de situationer, hvor du ønsker ændringer i kildeliste skal afspejles i en bundet kontrol.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!