.NET 3.0 en liten översikt - del 6 (WPF) - sista delen
Förord
Microsoft har nyligen släppt version 3.0 av .NET frameworket. Vad innehåller det för nytt och användbart då? Vi ska granska en del av nyheterna i en liten artikelserie. Vi fortsätter att titta på Windows Presentation Foundation (WPF).Innehåll
»»
»
»
»
»
»
»
»
»
Relaterade artiklar
» .NET 3.0 en liten översikt - del 1» .NET 3.0 en liten översikt - del 2
» .NET 3.0 en liten översikt - del 3
» .NET 3.0 en liten översikt - del 4 (WPF)
» .NET 3.0 en liten översikt - del 5 (WPF)
Vi diskuterade WPF allmänt i första WPF artikeln och började gå igenom chattklienten i vår fallstudie.
Vi ska i den här artikeln titta på resten av klienten.
Vi tittar först på deklarationen av den (tråkiga) första sidan. Vi radar upp våra kontroller i ett rutnät och sätter namn på textboxarna så att vi kan nå dom från code behind senare.
I den här filen har jag valt att använda mej av en vanlig Click event för att hantera knapptryckningar. Vi kommer längre fram i artikeln att se en annan möjlighet.
I händelsehanteraren för click eventet skapar vi en instans av ChatPage (som vi snart skall se på) och Remote och navigerar till chattsidan med hjälp av NavigationService (som tillhandahålls av Page basklassen)
Nåja, nu börjar vi komma till dom intressanta delarna:
Så ser vår chatt sida ut väldigt grovt taget, vi ärver Page klassen igen, och mappar in en del xml namnrymder mot motsvarande .NET namnrymder.
Nedan kommer resurserna vi använder på vår sida. I och med att vi deklarerar dom här gäller dom enbart för denna sida, mera allmängiltiga resurser kan med fördel sättas i App.xaml som då gäller för hela applikationen.
Vad vi egentligen gör här är att deklarativt skapa instanser av våra objekt och sätta dom i en dictionary.
DataTemplaten (med nyckel messageTemplate) kommer alltså att användas för varje enskilt meddelande. Vi vill visa meddelandet i en horisontell stack panel och visa tidsstämpel, avsändare och meddelande. För att vara lite kreativ har jag valt att visa tidsstämpeln som en analog klocka. (Klock konverteraren tittade vi på i förra artikeln)
Även här har jag använt mej av ett rutnät för layouten, men kommer inte att kopiera in hela den koden här av utrymmesskäl. All kod kommer att finnas i programarkivet för den som är intresserad.
Några väl valda delar kommer jag dock att ta upp. Vi visar våra meddelanden i en listbox, och binder den till Messages egenskapen av data kontexten (mer om det senare). För att listboxen skall fatta hur vi vill visa våra meddelande sätter vi ItemTemplate som beskriver hur varje item i listan skall visas.
Jag satt textboxens data kontext tomt meddelande objekt av den enkla anledningen att det blev enklast så :) Data kontext är alltså det objekt som databindningen utgår från. Vi binder Text egenskapen till meddelande objektets Content egenskap, och bindningen skall fungera i båda riktningarna. För markeringen av fula ord har jag använt mej av en datatrigger som alltså triggas av specifik data. För att göra det enkelt för mej har jag hårdkodat vilka ord som skall betraktas som fula, detta är kanske inte den mest verklighetsnära implementationen.
När datatriggern indikerar fula ord rödmarkerar jag texten och gör den fet.
Den snurrande sänd knappen kan göras med hjälp av animationer som triggas av MouseEnter och MouseLeave händelserna. Rotationen görs genom att animera vinkeln på en rotationstransform. Denna transform är satt som knappens RenderTransform, och det gör att layouten inte påverkas av vår transformation.
Den observante märkte kanske att jag inte kopplat nån Click händelse i förra kodbiten, men däremot satt Command. Genom att använda kommandon kan man koppla t.ex. kortkommandon, knappar och menyval till samma kommando samt även automatiskt kontrollera när dessa skall vara aktiva och när de inte ska vara det. (Du minns kanske när du skrev nåt i stil med btnGörNåt.Enabled = ; på en massa ställen i din WinForms applikation, detta slipper du nu). Vi kommer att titta på code behind för detta längre fram i artikeln.
Speglingar kan vara nog så snygga, men i och med att dom är så enkla att åstadkomma i WPF finns det en viss risk för att dom överanvänds.
Spegligen kan helt enkelt åstadkommas genom att måla på en Canvas med en VisualBrush (som i princip är en visuell klon av nåt annat, denna klon kan dessutom transformeras på lämpligt sätt) För att få till "fade out" effekten har jag bara satt en OpacityMask på Canvas elementet.
Code behind filen är egentligen fruktansvärt simpel.
Vi vill enbart att vårt kommando skall vara aktivt när det finns nåt i textrutan.
Sedan sätter vi sidans data kontext till vårt Remote objekt för att databindningen ska funka.
Det var allt för den här gången. Hoppas att du fått ut nåt av den här artikelserien och blivit sugen att testa på.
Som vanligt, frågor och kommentarer mottages tacksamt!
Vi ska i den här artikeln titta på resten av klienten.
Connect.xaml
Vi tittar först på deklarationen av den (tråkiga) första sidan. Vi radar upp våra kontroller i ett rutnät och sätter namn på textboxarna så att vi kan nå dom från code behind senare.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Connect" Margin="5"
>
Padding="5" VerticalAlignment="Center"
HorizontalAlignment="Stretch">net.pipe://localhost/Chat
Padding="5" VerticalAlignment="Center"
HorizontalAlignment="Stretch">John Doe
I den här filen har jag valt att använda mej av en vanlig Click event för att hantera knapptryckningar. Vi kommer längre fram i artikeln att se en annan möjlighet.
Connect.xaml.cs
I händelsehanteraren för click eventet skapar vi en instans av ChatPage (som vi snart skall se på) och Remote och navigerar till chattsidan med hjälp av NavigationService (som tillhandahålls av Page basklassen)
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LemonDesign.Chat.Client.ViewModel;
namespace LemonDesign.Chat.Client {
///
/// Interaction logic for Connect.xaml
///
public partial class Connect : System.Windows.Controls.Page {
public Connect() {
InitializeComponent();
}
private void ButtonOnClick(object sender, RoutedEventArgs e) {
ChatPage page = new ChatPage();
page.Remote = new Remote(txtUrl.Text, txtName.Text);
NavigationService.Navigate(page);
}
}
}
ChatPage.xaml
Nåja, nu börjar vi komma till dom intressanta delarna:
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:src="clr-namespace:LemonDesign.Chat.Client"
xmlns:vm="clr-namespace:LemonDesign.Chat.Client.ViewModel"
xmlns:contract="clr-namespace:LemonDesign.Chat.Contract;assembly=LemonDesign.Chat.Contract"
Title="ChatPage"
>
...
Så ser vår chatt sida ut väldigt grovt taget, vi ärver Page klassen igen, och mappar in en del xml namnrymder mot motsvarande .NET namnrymder.
Resurser
Nedan kommer resurserna vi använder på vår sida. I och med att vi deklarerar dom här gäller dom enbart för denna sida, mera allmängiltiga resurser kan med fördel sättas i App.xaml som då gäller för hela applikationen. Vad vi egentligen gör här är att deklarativt skapa instanser av våra objekt och sätta dom i en dictionary.
FontWeight="Bold" FontFamily="Verdana"/>
DataTemplaten (med nyckel messageTemplate) kommer alltså att användas för varje enskilt meddelande. Vi vill visa meddelandet i en horisontell stack panel och visa tidsstämpel, avsändare och meddelande. För att vara lite kreativ har jag valt att visa tidsstämpeln som en analog klocka. (Klock konverteraren tittade vi på i förra artikeln)
Layout
Även här har jag använt mej av ett rutnät för layouten, men kommer inte att kopiera in hela den koden här av utrymmesskäl. All kod kommer att finnas i programarkivet för den som är intresserad.Några väl valda delar kommer jag dock att ta upp. Vi visar våra meddelanden i en listbox, och binder den till Messages egenskapen av data kontexten (mer om det senare). För att listboxen skall fatta hur vi vill visa våra meddelande sätter vi ItemTemplate som beskriver hur varje item i listan skall visas.
Name="lstMessages" Background="AliceBlue" ItemsSource="{Binding Path=Messages}"
ItemTemplate="{StaticResource messageTemplate}">
Input rutan
Jag satt textboxens data kontext tomt meddelande objekt av den enkla anledningen att det blev enklast så :) Data kontext är alltså det objekt som databindningen utgår från. Vi binder Text egenskapen till meddelande objektets Content egenskap, och bindningen skall fungera i båda riktningarna. För markeringen av fula ord har jag använt mej av en datatrigger som alltså triggas av specifik data. För att göra det enkelt för mej har jag hårdkodat vilka ord som skall betraktas som fula, detta är kanske inte den mest verklighetsnära implementationen.När datatriggern indikerar fula ord rödmarkerar jag texten och gör den fet.
VerticalContentAlignment="Center" Grid.Column="0" Grid.Row="1" Background="GhostWhite"
Text="{Binding Path=Content, Mode=TwoWay}">
Snurrande knappen
Den snurrande sänd knappen kan göras med hjälp av animationer som triggas av MouseEnter och MouseLeave händelserna. Rotationen görs genom att animera vinkeln på en rotationstransform. Denna transform är satt som knappens RenderTransform, och det gör att layouten inte påverkas av vår transformation.
Kommandon
Den observante märkte kanske att jag inte kopplat nån Click händelse i förra kodbiten, men däremot satt Command. Genom att använda kommandon kan man koppla t.ex. kortkommandon, knappar och menyval till samma kommando samt även automatiskt kontrollera när dessa skall vara aktiva och när de inte ska vara det. (Du minns kanske när du skrev nåt i stil med btnGörNåt.Enabled =
Speglingar i "vattnet"
Speglingar kan vara nog så snygga, men i och med att dom är så enkla att åstadkomma i WPF finns det en viss risk för att dom överanvänds.Spegligen kan helt enkelt åstadkommas genom att måla på en Canvas med en VisualBrush (som i princip är en visuell klon av nåt annat, denna klon kan dessutom transformeras på lämpligt sätt) För att få till "fade out" effekten har jag bara satt en OpacityMask på Canvas elementet.
ChatPage.xaml.cs
Code behind filen är egentligen fruktansvärt simpel.
Vi vill enbart att vårt kommando skall vara aktivt när det finns nåt i textrutan.
Sedan sätter vi sidans data kontext till vårt Remote objekt för att databindningen ska funka.
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using LemonDesign.Chat.Client.ViewModel;
namespace LemonDesign.Chat.Client {
///
/// Interaction logic for ChatPage.xaml
///
public partial class ChatPage : Page {
public readonly static RoutedUICommand SendMessage;
static ChatPage() {
SendMessage = new RoutedUICommand("Send message", "SendMessage", typeof(ChatPage));
}
public ChatPage() {
InitializeComponent();
CommandBindings.Add(new CommandBinding(SendMessage, SendMessageExecuted, SendMessageCanExecute));
}
void SendMessageExecuted(object sender, ExecutedRoutedEventArgs e) {
m_remote.Send(txtMessage.Text);
}
void SendMessageCanExecute(object sender, CanExecuteRoutedEventArgs e) {
e.CanExecute = !string.IsNullOrEmpty(txtMessage.Text);
}
private Remote m_remote;
public Remote Remote {
get { return m_remote; }
set {
m_remote = value;
DataContext = m_remote;
}
}
}
}
Det var allt för den här gången. Hoppas att du fått ut nåt av den här artikelserien och blivit sugen att testa på.
Som vanligt, frågor och kommentarer mottages tacksamt!
Pelle Johansson
Hej, mycket bra skriven artikel- stort tack för ditt bidrag och hoppas det kommer mer. Såg att exempelkoden finns i filarkivet men undrar också om den finns skarpt så man kan titta på den på någon server?