TOC

This article has been localized into Swedish by the community.

Databindning:

Reagera på ändringar

Hittills i kursen har vi främst skapat bindningar mellan UI-element och existerande klasser, men i verkliga tillämpningar kommer du säkert att skapa bindningar till dina egna dataobjekt. Detta är inte särskilt svårt, men när du väl har skapat några sådana bindningar kanske du upptäcker något som gör dig besviken: Ändringar kommer inte automatiskt att ge effekter som motsvarar de vi fick i tidigare exempel. I det här avsnittet kommer du att lära dig att du behöver lägga ner lite extra arbete för att nå önskad effekt, men som tur är blir det ganska lätt med hjälp av WPF.

Reagera på ändringar hos datakällan

Det finns två olika scenarion som du kanske ställs inför i samband med ändringar i datakällan: Ändringar av poster i en lista och ändringar av bindningsegenskaper för dessa poster. Hur man ska hantera dessa varierar beroende på vad man håller på med och på vad man vill åstadkomma, men WPF tillhandahåller två väldigt enkla lösningar: klassen ObservableCollection och gränssnittet INotifyPropertyChanged.

Följande exempel visar varför vi behöver dessa två alternativ:

<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; }
	}
}

Kör exemplet på din dator och lägg märke till att när du gör ett tillägg i listan eller ändrar ett namn i listan sker ingen uppdatering av användargränssnittet (UI). Detta är ett enkelt exempel med en användarklass som innehåller användarnamn, en ListBox som presenterar dem och några knappar för manipulering av dess innehåll. Problemet är bara att ingen av knapparna tycks fungera. Låt oss åtgärda detta i två enkla steg.

Återspegla ändringar i listans datakälla

Det första steget är att få UI att svara på ändringar i listan (ItemsSource) då vi lägger till eller tar bort en användare. Vad vi behöver är en lista som kan meddela alla destinationer då innehållet ändras. Som tur är tillhandahåller WPF en listtyp som gör just detta. Den kallas "ObservableCollection" och du kan använda den precis som en vanlig lista List<T>, med bar ett fåtal skillnader.

I det färdiga exemplet nedan har vi helt enkelt ersatt List<User> med ObservableCollection<User> - det är allt som behövs! Med denna ändring kommer ”Add”- och ”Delete”-knapparna att fungera, men fortfarande händer ingenting om vi klickar på ”ChangeName”-knappen, därför att det som avses är en ändring av det bundna dataobjektet inte av listan. Steg nummer två adresserar detta scenario.

Återspegla av ändringar av dataobjekt

Som ett andra steg låter vi vår egna ”User”-klass implementera gränssnittet "InotifyPropertyChanged". När vi gjort det kan våra objekt meddela egenskapsändringar till UI. Denna modifiering är något större än att bara ändra listtyp som vi gjorde ovan, men är ändå ett av de enklaste sätten att åstadkomma den avsedda automatiska uppdateringen.

Färdigt fungerande exempel

Med de två modifieringar som beskrivits ovan har vi nu fått ett exempel där ändringar i datakällan återspeglas. Vår kod ser nu ut så här:

<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));
		}
	}
}

Sammanfattning

Som du har sett är det ganska enkelt att implementera gränssnittet ”INotifyPropertyChanged”, men det krävs lite extra kod i klasserna och lite extra logik hos egenskaperna. Detta är priset man måste betala om man vill skapa bindningar för sina egna klasser som gör att ändringar genast återspeglas i UI. Uppenbarligen är ett anrop till ”NotifyPropertyChanged” i ”setter” för den egenskap du vill binda allt du behöver – resten av koden kan förbli som den är.

Å andra sidan är ”ObservableCollection” väldigt lätt att använda – det enda som erfordras är att du använder denna som listtyp när du vill att ändringar i listan skall nå en bunden destination.


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!