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

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

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

V tomto díle se podíváme hlouběji na to, co nám nabízejí RIA Services, a pak si ukážeme, jak upravit konvence Caliburn.Micro. Nezapomeňte si z webu stáhnout poslední verzi Coprojectu.

Metadata

V druhém díle jsme si řekli, že RIA Services používají metadata k nastavení, jaká data se mají přenášet na klienta. To ale není vše, metadata mohou obsahovat i informace o validačních pravidlech nebo popisek editačního prvku. Otevřete soubor Coproject.Web/Services/CoprojectService.metadata.cs.

Řekněme, že chceme upravit popisky v detailu.

Pak musíme v ToDoItemMetadata upravit následující property:

[Display(Name="Task", Description="Describe the To-do task.")]
public string Content { get; set; }

[Display(Name = "Deadline", Description = "The latest possible date the task must be finished.")]
public Nullable DueDate { get; set; }

[Include]
[Association("FK_ToDoItems_Users", "UserID", "UserID", IsForeignKey = true)]
[Display(Name="Assigned user")]
public User User;

Když teď znovu spustíte aplikaci, uvidíte toto:

Pozn.: Pokud používáte automatické generování položek v DataFormu, můžete využít další property atributu Display: AutoGenerateField, Order, Groupname. Detaily najdete na MSDN.

Validace

Metadata využijeme také pro definici validačních pravidel. Upravte následující:

[Display(Name = "Task", Description = "Describe the To-do task.")]
[Required]
[StringLength(40)]
public string Content { get; set; }

[Range(typeof(DateTime), "1.1.2010", "31.12.2019")]
[Display(Name = "Deadline", Description = "The latest possible date the task must be finished.")]
public Nullable DueDate { get; set; }

Když teď spustíte aplikaci, můžete vidět něco takového (všimněte si, že tlačítko Save je vypnuté, jelikož entita není validní):

V namespace System.ComponentModel.DataAnnotations můžete najít další užitečné atributy. Například pokud bychom nastavili tento atribut na DueData:

[Editable(false)]

tak by textbox pro tuto property byl disablovaný dokonce i v edit módu.

Sdílený kód

Jistě jste si všimli, že pole Assigned user zobrazuje pouze příjmení přiřazeného uživatele. To je ideální příležitost, jak ukázat sdílení kódu RIA Services mezi serverovou a klientskou částí aplikace. Sdílený kód se musí nacházet v souboru, jehož jméno končí na .shared.cs. V projektu Coproject.Web vytvořte nový soubor Models/User.shared.cs s tímto obsahem:

namespace Coproject.Web.Models
{
	public partial class User
	{
		public string FullName
		{
			get
			{
				return string.Format("{0} {1}", this.FirstName, this.LastName);
			}
		}
	}
}

Je důležité, abyste použili tento namespace, jelikož rozšiřujeme partial třídu, kterou vytváří RIA Services. Je taky potřeba odstranit “using System.Web”, protože tento soubor bude zkopírovaný do klientského projektu a tam neexistuje reference na tuto knihovnu.

Teď můžeme následovně upravit ToDoItemView:

<dataform:datafield propertypath="User">
	<textblock text="{Binding User.FullName}">
</textblock></dataform:datafield>

V aplikaci se teď zobrazí celé jméno.

image_8

Stejným způsobem můžete sdílet například validační funkce nebo další logiku.

Filtrování na enter

Od filtru obvykle uživatel očekává, že po stisknutí Enteru se automaticky začnou načítat data. Pojďme naimplementovat tuto funkcionalitu.

Nejrychlejší řešení, které vás napadne, bude nejspíše přidat tento atribut do textboxu Filter:

cal:Message.Attach="[KeyDown] = [HandleKeyInFilter($eventargs)]"

a pak přidat tento kód do view modelu:

public IEnumerable<iresult> HandleKeyInFilter(System.Windows.Input.KeyEventArgs eventargs)
{
	if (eventargs.Key == System.Windows.Input.Key.Enter)
	{
		yield return LoadData().ToSequential();
	}

	yield break;
}

Tento postup by sice zajisté fungoval, porušuje ale filosofii M-V-VM – view model by neměl být závislý na KeyEventArgs. Další možné řešení by bylo upravit Caliburn.Micro.Parser.CreateTrigger, aby podporoval jiné akce, než Event (například EnterPressed, nebo Gesture Key: Enter z “velkého” Caliburnu). Nechci zde ale měnit zdrojové kódy C.M.

Poslední možnost, která mě napadá, je rozšířit TextBox o událost EnterKeyDown. Vytvořte tedy prvek ExtendedTextBox:

namespace Coproject.Controls
{
	public class ExtendedTextBox : TextBox
	{
		public event EventHandler EnterKeyDown;

		protected override void OnKeyDown(KeyEventArgs e)
		{
			base.OnKeyDown(e);

			if (e.Key == Key.Enter)
			{
				OnEnterKeyDown();
			}
		}

		protected void OnEnterKeyDown()
		{
			var handler = EnterKeyDown;
			if (handler != null)
			{
				handler(this, EventArgs.Empty);
			}
		}
	}
}

Aby se použil výchozí styl i na tento nový textbox, musíme přidat následující styl do Custom.xaml:

<style basedon="{StaticResource DefaultTextBoxStyle}" targettype="local:ExtendedTextBox" type="text/css">

Pak už jen nový textbox použijeme v ToDoListsView:

<local:ExtendedTextBox x:Name="Filter" Style="{StaticResource FilterTextBoxStyle}" 
						cal:Message.Attach="[EnterKeyDown] = [LoadData]" />

Osobně mám tento postup radši, protože view model není závislý na implementaci view.

Úpravy konvencí Caliburn.Micro

Jelikož jsme si během psaní Coprojectu vytvořili několik vlastních ovládacích prvků, bylo by hezké, kdyby C.M uměl využít výchozí konvence i pro tyto prvky. Otevřete AppBootstrapper a do funkce Configure() přidejte volání nově vytvořené funkce InitializeConventions:

private void InitializeConventions()
{
	ConventionManager.AddElementConvention<BusyIndicator>(BusyIndicator.IsBusyProperty, "IsBusy", "Loaded");
	ConventionManager.AddElementConvention<ExtendedTextBox>(ExtendedTextBox.TextProperty, "Text", "EnterKeyDown");
}

První parametr určuje výchozí property prvku, do které se bindují hodnoty z view modelu. Pokud tedy pojmenujeme BusyIndicator “Busy”, tak bude jeho property IsBusy navázaná na property view modelu stejného jména.

Druhý parametr určuje, která property prvku se má použít, když je prvek použit jako parametr akce. Pokud si vzpomenete na funkci LoadData(string filter), tak tento parametr byl zodpovědný za to, že obsah property Text textboxu Filter byl předán jako parametr funkce LoadData.

Třetí parametr definuje událost prvku, která vyvolá akce na tento prvek navázané.

Díky těmto novým konvencím můžeme upravit prvky v ToDoListsView následovně:

<local:ExtendedTextBox x:Name="Filter" Style="{StaticResource FilterTextBoxStyle}" cal:Message.Attach="LoadData" />
<toolkit:BusyIndicator x:Name="Busy_IsBusy"  Grid.RowSpan="2" />

Počítadlo otevřených detailů

Při experimentování s otevíráním a zavíráním detailů se mi hodilo počítadlo otevřených detailů. Pokud si ho chcete přidat taky, vložte tento kód do ToDoListsView, nad Toolbar:

<StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,-25,0,0">
	<TextBlock Text="Opened details: " Style="{StaticResource StatusTextBlockStyle}" />
	<TextBlock x:Name="Items_Count" Style="{StaticResource StatusTextBlockStyle}" />
</StackPanel>

Teď už byste měli mít docela slušnou představu o využití RIA Services a Caliburn.Micro v Silverlight aplikacích, jejich další využití v praxi už ale bude na vás.

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