XAML.cz Magazín moderních technologií založených na XAML

Coproject – demo RIA aplikace krok za krokem, díl 11

Napsáno pro Silverlight od Augustin Šulc [08.03.2011]

V tomto díle aktualizujeme seznam po uložení dat a taky přidáme do aplikace ikony. Můžete použít zdrojové kódy z minulého dílu, nebo si je stáhnout z codeplexu.

Event aggregator

Pokud chcete, aby jedna komponenta vaší aplikace informovala o nějaké události jinou část aplikace, obvykle k tomu použijete standardní .NET události. Problém s tímto přístupem je v tom, že “odběratel” události musí mít přístup ke komponentě, která událost vyvolává. Navíc nesmíte zapomenout se od události “odregistrovat”. V případech, kdy může být jedna událost vyvolána z více míst (například logování událostí v aplikaci), je celé použití tohoto konceptu velice komplikované.

Tady nám opět pomůže Caliburn.Micro a jeho implementace Event aggregatoru. Event aggregator si můžete představit jako centrální komponentu, ke které se hlásíte k odebírání událostí a zároveň ji využíváte k vyvolávání událostí. Odběratel tedy nemusí vůbec vědět, kdo danou událost vyvolal.

My využijeme Event aggregator k tomu, abychom informovali ToDo seznam, že došlo ke změně dat a mělo by dojít k aktualizaci.

Nejprve si musíme připravit třídu, která bude reprezentovat tuto událost. V klientském projektu vytvořte složku Events a vložte do ní tuto třídu:

public class ToDoItemUpdatedEvent
{
	public int ToDoItemID { get; set; }
	public ToDoItemUpdatedEvent(int toDoItemID)
	{
		ToDoItemID = toDoItemID;
	}
}

Otevřete ToDoItemViewModel a nechte MEF, aby sem importoval instanci Event aggregatoru:

[Import]
public IEventAggregator EventAggregator { get; set; }

Funkci Save pak upravte takto:

public IEnumerable Save()
{
	(Item as IEditableObject).EndEdit();
	IsReadOnly = true;
	#region Fix DataForm bug
	IsReadOnly = false; IsReadOnly = true;
	#endregion

	yield return new SaveDataResult(_context);

	EventAggregator.Publish(new ToDoItemUpdatedEvent(Item.ToDoItemID));
}

Nová funkce Save tedy po uložení dat vyvolá událost, která informuje o změně dat. Už nás tady nezajímá, kdo (pokud vůbec) tuto událost odchytí a jak na ni zareaguje.

Abychom mohli implementovat i druhý konec události, potřebujeme propojit Event aggregator s ToDoListsViewModelem – upravte jeho konstruktor takto:

[ImportingConstructor]
public ToDoListsViewModel(IEventAggregator eventAggregator)
{
	DisplayName = "To Do";
	Description = "To-do lists";

	eventAggregator.Subscribe(this);
}

Tímto řekneme Event aggregatoru, aby v případě vyvolání události informoval i ToDoListsViewModel. Jak ale Event aggregator pozná, jaké události nás zajímají, a jak o nich má “informovat”? To záleží na tom, jaký interface IHandle třída implementuje. My potřebujeme implementovat IHandle, což provedeme přidáním této funkce do ToDoListsViewModelu:

public void Handle(ToDoItemUpdatedEvent message)
{
	LoadData().ToSequential().Execute(null);
}

Nezapomeňte, že funkce LoadData vrací IEnumerable – abychom ji tedy doopravdy spustili, musíme na ní spustit enumeraci. Toho dosáhneme zabalením do SequentialResultu a potom spuštěním tohoto resultu.

Jediný problém je v tom, že funkce LoadData vyžaduje parametr filter, který ale není v handleru události znám. Naštěstí ale můžeme napojit textbox Filter na property, ve které bude tato hodnota uložena. Přidejte tuto property do ToDoListsViewModelu:

public string Filter { get; set; }

Pak už jen odstraňte parametr filter z LoadData (hodnotu vezměte z property Filter).

Spusťte aplikaci a všimněte si, že pokud uložíte ToDoItem, změna se automaticky projeví i v seznamu.

Ikony

Pojďme ještě trochu zkrášlit aplikaci tím, že místo tlačítek s popisky využijeme ikony.

Vybalte ikony z tohoto souboru do Assets/Icons. Sluší se poznamenat, že tyto ikony jsou od firmy Axialis.

Otevřete ToDoListsView a upravte tlačítko LoadData:

OK, tento způsob funguje, ale více tlačítek s ikonou bych tímto způsobem rozhodně psát nechtěl. Proto si vytvoříme složku Controls a do ní přidáme vlastní prvek ImageButton:

public class ImageButton : Button
{
	public string ImageName
	{
		get { return (string)GetValue(ImageNameProperty); }
		set { SetValue(ImageNameProperty, value); }
	}

	public static readonly DependencyProperty ImageNameProperty =
		DependencyProperty.Register("ImageName", typeof(string), typeof(ImageButton), new PropertyMetadata(OnImageNamePropertyChanged));

	public static void OnImageNamePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
	{
		ImageButton button = d as ImageButton;
		string newValue = e.NewValue as string;

		if (newValue.IsNullOrWhiteSpace())
		{
			button.Content = null;
			return;
		}

		newValue = "/Coproject;component/Assets/Icons/{0}.png".FormatWith(newValue);
		ImageSource image = new BitmapImage(new Uri(newValue, UriKind.Relative));
		button.Content = new Image { Source = image };
	}
}

Použili jsme jako základ originální tlačítko a jen jsme k němu přidali property ImageName. Jelikož ale chceme, aby pro tuto property fungovalo i bindování a nastavování přes styly, musíme ji implementovat jako dependency property. Když dojde ke změně hodnoty property ImageName, vložíme do tlačítka obrázek. Hezčím řešením by jistě bylo vkládat obrázek přes property ContentTemplate ve stylu a pak pouze nabindovat jeho property Source na ImageName. Problém je ale v tom, že v definici stylů (template binding) nemůžete použít convertery, takže by bylo nutné psát název obrázku v plném znění (/Coproject;component/Assets/Icons/Search.png). Proto radši zůstaneme u původního řešení.

Otevřete Assets/Cosmopolitan/Custom.xaml  a přidejte do něj namespace:

xmlns:local="clr-namespace:Coproject.Controls"

Na konec souboru pak (před značku ) vložte tento styl:





			

Komentáře

ukládám komentář, vyčkejte prosím..
  1. Buďte první, kdo napíše komentář.

@xamlcz

  • RT @jvanrhyn: XAML, It's a bit like olives. Takes a while to get used to. But once you're used to it. It is actually pretty good. <3 XAML
  • RT @moser_christian: WPF Inspector 0.9.7 is released. It supports .NET 3.5 and 4.0 The project is now open source and available on CodeP ...
  • Jeff Handley oznámil vydání WCF RIA Services v.1.0 SP1 RTM http://bit.ly/gOgckn ke stažení na http://bit.ly/gVAXdK
  • jedna výzva pro Brno. Byl někdo z vás na přednášce o RIA v MS Akvárku? Dejte o sobě vědět. Děkuji
  • také jste uvažovali o tom, že zkusíte na projekt použít Caliburn Micro nebo naopak Prism 4? A co tak obojí, šlo by to nebo ne? Již brzy