Anda yang terbiasa membuat aplikasi berbasis desktop dengan menggunakan teknologi .NET, tentunya sudah familiar dengan Windows Forms, sebuah teknologi antarmuka (user interface) yang dibangun di atas teknologi GDI+ (pertama kali diperkenalkan pada WindowsXP time frame). GDI+ itu sendiri merupakan pengembangan dari teknologi GDI yang pertama kali muncul pada Windows 1.0 tahun 1985. Jadi, bisa dihitung berapa usia teknologi di balik Windows Forms yang biasa kita gunakan.
Di lain sisi, terdapat dua teknologi grafis yang sudah kondang seantero developer khususnya pemerhati grafis: OpenGL dan DirectX. Kedua teknologi ini memungkinkan kita untuk mengembangkan aplikasi yang membutuhkan pengolahan grafis lebih jauh. Tidak hanya untuk kebutuhan grafis 2D, tetapi juga 3D, dengan memanfaatkan kemampuan hardware pemroses grafis (GPU). Sehingga keduanya sangat diandalkan untuk membangun aplikasi-aplikasi berbasis grafis seperti game dan simulator 3D.
Persaingan bisnis di bidang software semakin ketat, tidak hanya faktor value, fitur, performance, dan security saja yang ditingkatkan, tetapi juga dari user experience. Pengalaman user dalam menggunakan software menjadi bagian terpenting yang harus mendapat perhatian dari para pengembang software.
Membangun sebuah software dengan efek 3D, misalnya sebuah Sistem Penjualan Produk dengan visualisasi laporan berbentuk kertas atau buku 3D, bisa saja dilakukan dengan memadukan teknologi Windows Forms dan DirectX. Membuat custom control tertentu dengan cara seperti ini (atau cara lainnya dengan hanya memanfaatkan teknologi Windows Forms) relatif menghabiskan waktu dan resource lebih banyak. Tidak jarang solusi seperti ini justru mengecewakan, hasil rendering control yang tidak sesuai dengan keinginan, perbedaan kompatibilitas antara GPU, dan sebagainya. Custom/composite control dikembangkan dengan beragam cara demi tercapainya tampilan yang penuh estetika dan dapat meningkatkan kesan baik dari user (mudah digunakan dan intuitif).
Windows Presentation Framework (WPF) hadir untuk menjawab tantangan ini. Kemampuannya dalam memanfaatkan akselerasi GPU mampu menghadirkan sebuah software dengan user experience yang tinggi melalui efek/style, animasi, dan kekuatan grafis vector. Tanpa harus menguasai beberapa teknologi tertentu (seperti menggabungkan Windows Forms dengan DirectX dan teknologi lainnya), melalui WPF kita bisa lebih mudah untuk membangun sebuah aplikasi yang kaya dengan user experience dan lebih intuitif.
WPF merupakan bagian dari .NET Framework. Pertama kali muncul pada .NET Framework vesi 3.0. Bersama dengan teknologi lainnya–yaitu WCF, WF, dan WCS–, WPF merupakan teknologi baru pada .NET Framework versi 3.0 yang merupakan superset dari .NET Framework versi 2.0. Dengan kata lain, diluar keempat teknologi baru tersebut, .NET Framework versi 2.0 tetap dipertahankan di dalam versi 3.0.
.NET Framework 3.0
.NET Framework versi 3.0 sudah terinstall secara default pada Windows Vista. Sementara, pada Windows 7, sudah terinstall .NET Framework versi 3.5 secara default. Sampai tulisan ini dibuat, .NET Framework versi terbaru adalah versi 4.0 RC (Release Candidate).
Declarative Programming
Salah satu hal besar yang terjadi pada WPF adalah penggunaan eXtensible Application Markup Language (XAML)–dibaca 'Zammel'–dengan paradigma declarative programming nya. Pada dasarnya sebuah dokumen XAML adalah file XML dengan extension .xaml. Developer bisa mengekspresikan sebuah user interface melalui dokumen XAML. Susunan control pada sebuah form, dengan efek/style yang digunakan, hingga animasi control/object bisa diekspresikan hanya melalui elemen-elemen markup dokumen XAML saja, declarative selayaknya XML. Berikut contoh sebuah file XAML bernama Profile.xaml untuk membuat sebuah tampilan profil dengan beberapa control di dalamnya:
1.
<Canvas xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
2.Width=”210″
3.Height=”70″>
4. <Label Content=”Name:” Margin=”5,10,0,0″ />
5. <TextBox Name=”textBoxName” Margin=”50,10,0,0″ Width=”150″ />
6. <Button Name=”buttonOK” Content=”OK” Margin=”150,40,0,0″ Width=”50″ />
7.</Canvas>
Jika komputer Anda terinstall .NET Framework versi 3.0 ke atas, anda dapat langsung membuka file XAML tersebut dengan menggunakan Internet Explorer. Dan hasilnya akan tampak seperti ini:
Hasil render layout pada IEXAML memberikan feel seolah kita membuat User Interface dengan menggunakan HTML. Meskipun demikian, jangan sampai salah kaprah bahwa XAML adalah WPF dan WPF adalah XAML. Karena tanpa XAML pun kita tetap bisa menggunakan teknologi WPF di dalam bahasa .NET yang kita inginkan. Sebaliknya, walaupun XAML sengaja dibuat untuk WPF, tetapi file XAML juga bisa digunakan di beberapa teknologi lainnya seperti Windows Workflow Framework (WF). Dengan kata lain, XAML hanyalah salah satu cara untuk mengakses .NET API.
Jika kode XAML di atas kita transformasikan menjadi kode dalam bahasa C#, maka akan menjadi seperti ini:
1.
Canvas canvas = new Canvas() { Width = 210, Height = 70 };
2.
Label label = new Label() { Content = “Name:”, Margin = new Thickness(5, 10, 0, 0) };
3.
canvas.Children.Add(label);
4.
5.
TextBox textBoxName = new TextBox() { Name = “textBoxName”, Margin = new Thickness(50, 10, 0, 0), Width = 150 };
6.
canvas.Children.Add(textBoxName);
7.
8.
Button buttonOK = new Button() { Name = “buttonOK”, Content = “OK”, Margin = new Thickness(150, 40, 0, 0), Width = 50 };
9.
canvas.Children.Add(buttonOK);
Canvas canvas = new Canvas() { Width = 210, Height = 70 }; Label label = new Label() { Content = “Name:”, Margin = new Thickness(5, 10, 0, 0) }; canvas.Children.Add(label); TextBox textBoxName = new TextBox() { Name = “textBoxName”, Margin = new Thickness(50, 10, 0, 0), Width = 150 }; canvas.Children.Add(textBoxName); Button buttonOK = new Button() { Name = “buttonOK”, Content = “OK”, Margin = new Thickness(150, 40, 0, 0), Width = 50 }; canvas.Children.Add(buttonOK);
Memisahkan user interface dari logic
Perlu diingat bahwa apapun yang bisa dilakukan di dalam XAML, bisa sepenuhnya dilakukan di dalam bahasa .NET pilihan kita (tanpa menggunakan XAML). Namun hal ini tidak terjadi sebaliknya. Walaupun XAML dan WPF bisa saling independen satu sama lain, kehadiran XAML tidak lain bertujuan untuk memisahkan user interface dan logic program. Sehingga, pada umumnya pengembangan aplikasi WPF memadukan antara XAML dengan kode bahasa .NET misalnya C#. Dengan pemisahan user interface pada dokumen XAML, dan logic pada bahasa .NET pilihan kita, akan mempermudah maintenance di dalam proses development.
Selain itu, pemisahan user interface ini juga memperbesar kontribusi graphic designer dalam proses development dan dapat langsung melihat sekaligus merasakan seperti apa tampilan tersebut digunakan di dalam aplikasi.
Untuk mendukung proses ini, terdapat beberapa tool yang dapat digunakan oleh graphic designer (tentunya programmer juga bisa menggunakannya) seperti Microsoft Expression Blend dan XamlPad. Expression Blend memiliki kelebihan tersendiri, selain kemampuan drag and drop control, format project Expression Blend sama dengan format project pada Visual Studio dan MSBuild, sehingga hasil kreatifitas graphic designer pada Expression Blend bisa langsung digunakan dan diasosiasikan oleh programmer dengan menggunakan Visual Studio. Visual Studio (versi 2005 ke atas) sendiri memiliki Visual Designer di dalamnya yang memudahkan penggunaan user interface berbasis WPF semudah Windows Forms (drag and drop control dan sebagainya). Tool lain yang mendukung pengembangan aplikasi berteknologi WPF adalah WpfPerf, LocBaml, Snoop, dan Zam 3D.
Hardware Acceleration
WPF dibangun di atas Direct3D–bagian dari DirectX API. Direct3D sendiri merupakan teknologi 3D dibalik console Xbox dan Xbox 360. Semua konten aplikasi WPF, baik berupa grafis 2D maupun 3D, akan dikonversi menjadi object Direct3D berupa 3D triangle (Anda yang suka utak-atik grafis 3D tentunya sudah familiar dengan istilah triangle, triangulation), texture, dan object Direct3D lainnya yang akan di-render oleh GPU. Ini menjadi sebuah catatan tersendiri, karena misalnya saja ketika kita ingin membuat sebuah object garis 3D dengan menggunakan WPF 3D, maka WPF akan menggambar dua buah 3D triangle yang kemudian akan menyusun object garis ini. Catatan ini cukup penting mengingat sebuah garis yang seharusnya hanya terdiri dari dua buah titik, di WPF menjadi 6 buah titik (tiap 3D triangle terdiri dari 3 titik). Saya akan coba membahas secara terpisah tentang hal ini pada tulisan yang lain.
Meskipun WPF memastikan aplikasi mendapatkan benefit yang besar dari GPU, WPF tidak mengharuskan kita untuk memiliki GPU berkualitas tinggi. Karena WPF memiliki software rendering pipeline sehingga hasil render object WPF di layar bisa tetap optimal.
Resolution Independence
Grafis vektor memiliki karakteristik resolution indpendence (tidak bergantung pada pixel grid), sehingga jika kita lakukan proses zoom in ataupun zoom out pada sebuah obyek vektor, kualitas gambar obyek tersebut pada layar akan tetap terjaga, tetap solid dan tidak pecah. WPF adalah teknologi yang mengusung grafis vektor. Sehingga, sebuah aplikasi WPF pada notebook 12″, akan terlihat sama bagusnya pada layar LCD 30″, dan sebaliknya. Begitu juga pada layar raksasa di lapangan sepakbola kesayangan, tampilan aplikasi WPF tersebut akan tetap terlihat bagus, solid, dan crispy.
WPF Namespaces pada XAML dan bahasa .NET
Kalau kita lihat pada kode XAML di atas, terdapat atribut XML namespace xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” pada element Canvas. Dan bila kita perhatikan, maka element Canvas ini terdapat pada namespace System.Windows.Controls. Dengan kata lain, untuk menggunakan object Canvas pada bahasa .NET pilihan kita, jangan lupa untuk menambahkan using System.Windows.Controls.
Sementara, XML namespace http://schemas.microsoft.com/winfx/2006/xaml akan terpetakan dengan namespace System.Windows.Markup. Biasanya XML namespace ini dijadikan sebagai namespace kedua pada sebuah dokumen XAML.
Untuk membedakan satu XML namepace dengan lainnya, sebuah XML namespace pada XAML harus menggunakan sebuah prefix yang unik seperti pada contoh berikut:
1.
<Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
2.
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
3.
xmlns:view=”clr-namespace:BukuQ.View;assembly=BukuQ.View”
4.
Width=”300″
5.
Height=”200″>
6.
<view:BookShelf MaximumBook=”200″ />
7.
<Window />
Perhatikan prefix “x” dan prefix “view” pada potongan XAML di atas, dan perhatikan juga bagaimana cara menggunakan element BookShelf yang terdapat pada namespace BukuQ.View. Lebih dalam tentang WPF namespace pada pembahasan selanjutnya.
XAML, CAML, dan BAML
Setiap kode pada bahasa pemrograman .NET harus melalui proses compile yang kemudian menjadi Intermediate Language (IL) sebelum dieksekusi oleh CLR (Common Language Runtime). Lalu, bagaimana dengan XAML, apakah setiap file berekstensi .xaml pada project aplikasi WPF akan ter-compile menjadi kode IL?
Ketika WPF masih pre-release, XAML bisa di-compile menjadi CAML (Compiled Application Markup Language)–dibaca “Cammel”–yang tidak lain adalah kode IL. Saat itu, selain menjadi CAML, XAML juga bisa di-compile menjadi BAML (Binary Application Markup Language)–dibaca “Bammel”. Namun pada akhirnya tim pengembang WPF di Microsoft memutuskan untuk meninggalkan CAML dan mempertahankan BAML.
BAML, sesuai representasi huruf B pada BAML yaitu Binary, adalah bentuk binary dari XAML yang sebelumnya sudah melalui proses parse dan tokenize.
BAML bukanlah kode IL, melainkan XAML yang sudah terkompres ke dalam bentuk binary sehingga mempercepat proses load dan parse. BAML memiliki ukuran lebih kecil daripada CAML, walaupun CAML memiliki kecepatan eksekusi lebih baik. Selain ukurannya yang lebih kecil, alasan tim WPF mempertahankan BAML ketimbang CAML adalah karena BAML memiliki celah security lebih sedikit, dan dengan BAML kita bisa melakukan localization setelah kompilasi.
Loose XAML dan Compiled XAML
Pada umumnya sebuah aplikasi WPF menggunakan XAML dan bahasa .NET secara bersamaan. Kode bahasa .NET dibalik XAML ini biasanya disebut dengan code-behind. Namun pada skenario tertentu, sebuah aplikasi WPF bisa dibangun hanya dengan menggunakan XAML saja. Dan pada skenario lain, aplikasi WPF memerlukan perubahan layout secara dinamis ketika run-time. Skenario seperti ini biasanya lebih cocok untuk mengunakan file XAML eksternal (baik dari lokal hardisk, LAN, atau jaringan internet), yang disebut dengan Loose XAML. Kita tidak bisa menggunakan code-behind pada Loose XAML.
.NET Framework memiliki run-time parser untuk XAML di dalam namespace System.Windows.Markup; class XamlReader dan class XamlWriter.
XamlReader memiliki beberapa static method bernama “Load” yang akan menghasilkan sebuah root element dengan tipe Object. Method ini akan melakukan parse terhadap file XAML kemudian membuat semua objects yang sesuai dengan deklarasi pada XAML tersebut.
Jika (Loose) XAML sebelumnya kita load secara manual ketika run-time, maka kita bisa melakukannya seperti berikut ini:
1.
Window window;
2.
using (FileStream fileStream = new FileStream(“C:UsersjayDocumentsVisual Studio 2010ProjectsWpfApplication1WpfApplication1Profile.xaml”, FileMode.Open, FileAccess.Read))
3.
{
4.
window = (Window)XamlReader.Load( fileStream );
5.
}
Window window; using (FileStream fileStream = new FileStream(“C:UsersjayDocumentsVisual Studio 2010ProjectsWpfApplication1WpfApplication1Profile.xaml”, FileMode.Open, FileAccess.Read)) { window = (Window)XamlReader.Load( fileStream ); }
Di lain sisi, kita bisa membaca balik BAML menjadi XAML dengan menggunakan method XamlWriter.Save seperti pada contoh berikut:
1.
System.Uri uri = new System.Uri(“Profile.xaml”, System.UriKind.Relative);
2.
Window window=(Window)Application.LoadComponent(uri);
3.
4.
string xaml = XamlWriter.Save(window);
System.Uri uri = new System.Uri(“Profile.xaml”, System.UriKind.Relative); Window window=(Window)Application.LoadComponent(uri); string xaml = XamlWriter.Save(window);
Berbeda dengan Loose XAML, sebuah Compiled XAML bisa memiliki code-behind dengan cara menambahkan attribute x:Class pada root element.
1.
<Window xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
2.
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
3.
xmlns:view=”clr-namespace:BukuQ.View;assembly=BukuQ.View”
4.
x:Class=”BukuQ.Desktop.ShellForm”
5.
Width=”300″
6.
Height=”200″>
7.
<view:BookShelf MaximumBook=”200″ />
8.
<Window />
Proses kompilasi XAML dimulai dengan merubah XAML menjadi BAML, kemudian meng-embed format binary tersebut ke dalam assembly sebagai binary resource, yang pada akhirnya WPF framework akan melakukan 'plumbing' untuk menyambungkan antara XAML dan code-behind.
Walaupun Loose XAML memiliki keunggulan dalam me-render layout secara dinamis, proses load dan parse Loose XAML memakan resource dan waktu lebih banyak dibandingkan dengan BAML. Dan tentunya BAML memiliki keunggulan dengan dukungan code-behind nya.
Code-behind dan generated code
Berikut ini sebuah XAML dengan nama class “FormProcess” yang memiliki element Button dengan nama “buttonOK”.
1.
<Window x:Class=”Zammel.FormProcess”
2.
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
3.
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
4.
Title=”Process” Height=”65″ Width=”150″>
5.
<Button x:Name=”buttonOK” Content=”OK” Click=”buttonOK_Click” />
6.
</Window>
Dan memiliki code-behind seperti berikut:
1.
namespace Zammel
2.
{
3.
/// <summary>
4.
/// Interaction logic for FormProcess.xaml
5.
/// </summary>
6.
public partial class FormProcess : Window
7.
{
8.
public FormProcess()
9.
{
10.
InitializeComponent();
11.
}
12.
13.
private void buttonOK_Click(object sender, RoutedEventArgs e)
14.
{
15.
// Do something.
16.
}
17.
}
18.
}
namespace Zammel { /// /// Interaction logic for FormProcess.xaml /// public partial class FormProcess : Window { public FormProcess() { InitializeComponent(); } private void buttonOK_Click(object sender, RoutedEventArgs e) { // Do something. } } }
Di sini kita bisa lihat, nama class pada code-behind sama dengan nilai atribut x:Class pada XAML, yaitu “FormProcess”. XAML dan code behind ini adalah sebuah class yang sama, yang secara fisik terpisahkan oleh dua file yang berbeda (partial classes). Perhatikan pada XAML, nama class dituliskan lengkap dengan namespace nya.
Sebuah bahasa .NET tidak harus memiliki kemampuan partial classes untuk dapat menggunakan code-behind dari XAML. Untuk bahasa .NET yang tidak mendukung partial classes (seperti J# dan C++/CLI), solusinya adalah dengan melakukan subclassing melalui tambahan keyword (atribut) Subclass pada XAML:
1.
<Window x:Class=”Zammel.FormProcess” x:Subclass=”Zammel.FormProcessView”
2.
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
3.
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
4.
Title=”Process” Height=”65″ Width=”150″>
5.
<Button x:Name=”buttonOK” Content=”OK” Click=”buttonOK_Click” />
6.
</Window>
Dari kode tersebut, terdefinisi sebuah class bernama FormProcess yang merupakan code-behind sebagai base class, dan class FormProcessView sebagai sub class nya.
Ketika proses kompilasi XAML dan code-behind nya, terjadi proses pembuatan auto generated partial class yang memiliki suffix .g.cs untuk C# atau .vb.cs untuk VB. 'g' di sini maksudnya generated. Untuk contoh di atas, akan terbentuk file bernama FormProcess.g.cs (karena saya menggunakan bahasa C# pada code-behind).
Setiap element yang terdapat pada XAML akan menjadi private property pada generated code. Generated code ini juga memiliki method bernama InitializeComponent yang akan dipanggil dari constructor code-behind. Fungsinya untuk loading BAML yang sudah ter-embed, yang kemudian akan terjadi proses assign property-property tersebut sesuai deklarasi masing-masing element pada XAML, dan akhirnya akan menghubungkan setiap event yang terdeklarasi pada element-element XAML dengan masing-masing event handler pada code-behind (jika ada).
1.
/// <summary>
2.
/// FormProcess
3.
/// </summary>
4.
public partial class FormProcess : System.Windows.Window, System.Windows.Markup.IComponentConnector {
5.
6.
#line 5 “….FormProcess.xaml”
7.
internal System.Windows.Controls.Button buttonOK;
8.
9.
#line default
10.
#line hidden
11.
12.
private bool _contentLoaded;
13.
14.
/// <summary>
15.
/// InitializeComponent
16.
/// </summary>
17.
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
18.
public void InitializeComponent() {
19.
if (_contentLoaded) {
20.
return;
21.
}
22.
_contentLoaded = true;
23.
System.Uri resourceLocater = new System.Uri(“/WpfApplication10;component/FormProcess.xaml”, System.UriKind.Relative);
24.
25.
#line 1 “….FormProcess.xaml”
26.
System.Windows.Application.LoadComponent(this, resourceLocater);
27.
28.
#line default
29.
#line hidden
30.
}
31.
32.
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
33.
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
34.
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes")]
35.
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) {
36.
switch (connectionId)
37.
{
38.
case 1:
39.
this.buttonOK = ((System.Windows.Controls.Button)(target));
40.
41.
#line 5 “….FormProcess.xaml”
42.
this.buttonOK.Click += new System.Windows.RoutedEventHandler(this.buttonOK_Click);
43.
44.
#line default
45.
#line hidden
46.
return;
47.
}
48.
this._contentLoaded = true;
49.
}
50.
}