Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.
W dzisiejszym artykule powiemy sobie o tworzeniu kontrolki użytkownika oraz używaniu jej w naszej stronie internetowej.
Kontrolka użytkownika w ASP .NET jest to plik z rozszerzeniem .ascx, w którym grupujemy np. inne kontrolki dostępne w ASP i ustalamy im jakieś zachowanie. Plik z kontrolką jest bardzo podobny do normalnego pliku ze stroną (.aspx) i również posiada “code-behind”. Kontrolkę użytkownika można wykorzystywać na raz w kilku różnych stronach.
Tworzenie kontrolki
Kontrolka użytkownika, którą tworzymy, dziedziczy po klasie UserControl. Ta klasa z kolei dziedziczy po TemplateControl. Pełna hierarchia poniżej:

Aby stworzyć plik z kontrolką musimy kliknąć prawym przyciskiem myszy na projekt –> Add –> New Item, i wybrać Web User Control. Po utworzeniu pliku (z rozszerzeniem .ascx) widzimy, że jest on bardzo podobny do zwykłej “Web page”. Jednak na górze zamiast dyrektywy @Page jest @Control. Poniżej cały kod:
1: <%@ Control Language=”C#" AutoEventWireup=”true”
2: CodeFile=" MyWebUserControl.ascx.cs” Inherits=" MyWebUserControl” %>
Teraz możemy już przeciągać kontrolki tak jak przy zwykłej stronie. Np. możemy przeciągnąc sobie jakiś textbox, czy button tworząc w ten sposób kontrolkę do adresu. Wszystko może wyglądać tak:

Definiowanie zdarzeń
Kontrolka użytkownika może posiadać swoje własne, zamknięte zdarzenia. Zdarzenia te są wywoływane synchronicznie razem z tymi wywoływanymi na stronie.
W poprzednim przykładzie przygotowaliśmy sobie widok kontrolki. Teraz warto by było oprogramować przycisk, który może np. zapisywać adres do profilu zalogowanego użytkownika. Na poniższym przykładzie zrealizowane zostało właśnie takie zapisywanie, za pomocą własnej klasy UserProfile.
1: protected void ButtonSave_Click(object sender, EventArgs e)
2: {
3: //to do: walidacja
4: //zapisywanie nowego adresu dla usera
5: UserProfile.AddNewAddress(this.UserId, this.AddressType,
6: TextBoxAddress1.Text, TextBoxAddress2.Text,
7: TextBoxCity.Text, TextBoxState.Text, TextBoxZip.Text);
8: }
Definiowanie właściwości
Tworząc kontrolki często będziemy musieli konfigurować dane. Konfigurowanie danych w kontrolce możemy zdefiniować za pomocą właściwości, które następnie będą mogły być konfigurowane przez znaczniki na stronie, która używa kontrolki.
Właściwości tworzymy tak jak w innych klasach .NET Frameworka. Np. Aby dodać właściwości UserId oraz AddressType do napisanej przez nas kontrolki piszemy po prostu:
1: public int UserId { get; set; }
2: public UserProfile.AddressType AddressType { get; set; }
Dodawanie kontrolki do strony
Możemy dodać naszą kontrolkę do strony przez przeciągnięcie jej z Solution Explorera. Po dodaniu kontrolki, zobaczymy w Design View jej zawartość. Od strony source view kod prezentuje się następująco:
1: <%@ Page Language=”VB” AutoEventWireup=”false”
2: CodeFile=”UserProfilePage.aspx.vb” Inherits=”UserProfilePage” %>
3: <%@ Register src=”AddressUc.ascx” tagname=”AddressUc” tagprefix=”uc1” %>
4: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN”
5: “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
6: <html xmlns=”http://www.w3.org/1999/xhtml”>
7: <head runat=”server”>
8: <title>User Profile Settings</title>
9: </head>
10: <body style=”font-family: Verdana; font-size: small”>
11: <form id=”form1” runat=”server”>
12: <div>
13: <uc1:AddressUc ID=”AddressUc1” runat=”server” AddressType=”Home” />
14: </div>
15: </form>
16: </body>
17: </html>
Widzimy nową dyrektywę @Register, która jest wymagana do zarejestrowania kontrolki na stronie. Atrybut TagPrefix jest to identyfikator, do którego będziemy się odwoływać, żeby stworzyć kontrolkę na stronie. Samo stworzenie kontrolki widzimy w sekcji <div> </div>. Widzimy również atrybut AddressType, który zdefiniowaliśmy sobie w poprzednim przykładzie, jako właściwość.
Dynamiczne ładowanie kontrolki
Aby dynamicznie ładować naszą kontrolkę musimy użyć metody LoadControl. Metoda ta przyjmuje nazwę oraz ścieżkę do pliku, który zawiera definicję kontrolki. Metoda ta zwraca również referencję do kontrolki.
Przykładowo, załóżmy, że chcemy dodać kilka instancji naszej kontrolki AdressUc. Poniższy kod pokazuje jak dynamicznie wczytywać kontrolkę i dodać do form’a:
1: AddressUc addressControl = (AddressUc)LoadControl(“AddressUc.ascx”);
2: form1.Controls.Add(addressControl);
Tworzenie Templated User Control
Templated user control umożliwia separacje danych od ich prezentacji. Nie dostarcza natomiast domyślnego layoutu. Layout jest dostarczany przez developera.
Proces tworzenia możemy opisać w kilku krokach:
1. Dodajemy plik kontrolki do projektu
2. Wstawiamy wewnątrz naszej kontrolki, kontrolkę Placeholder, która definiuje miejsce dla szablonu. Później będzie można przedstawić to jako właściwość.
3. Definiujemy klasę w naszej aplikacji, która będzie kontenerem nazw. Ta klasa będzie zawierała referencje do danych naszej kontrolki. Klasa dziedziczy po Control oraz implementuje interfejs INamingContainer.
4. W code behind kontrolki implementujemy właściwość typu ITemplate.
5. Następnie należy dodać kod do metody Page_Init w naszej kontrolce. W tym miejscu testujemy powyższą właściwość. Jeśli jest ustawiona, tworzymy instancję klasy z pkt 3.
Powyższe kroki są dosyć ogólne. Stwórzmy teraz własną implementację kontrolki, bazując na tej, którą tworzyliśmy od początku artykułu. Zaczynamy od placeholder’a w naszej kontroce:
1: <%@ Control Language=”C#" AutoEventWireup=”true”
2: CodeFile=”AddressUcTemplated.ascx.cs” Inherits=”AddressUcTemplated” %>
3: <asp:PlaceHolder runat=”server” ID=”PlaceHolderAddressTemplate”>
4: </asp:PlaceHolder>
Następnie dodajemy kod do code-behind kontrolki. Kod zawiera właściwość ITemplate oraz metode Page_Init.
ITemplate używamy w celu okreslenia obszaru dla usera. Metodzie Page_Init służy do utworzenia instancji kontenera nazw i połączenia go z layoutem.
1: public partial class AddressUcTemplated :
2: System.Web.UI.UserControl
3: {
4: protected void Page_Init(object sender, EventArgs e)
5: {
6: //czyszczenie kontrolek z place holdera
7: PlaceHolderAddressTemplate.Controls.Clear();
8: if (LayoutTemplate == null)
9: {
10: PlaceHolderAddressTemplate.Controls.Add(
11: new LiteralControl(“No template defined."));
12: }
13: else
14: {
15: AddressUcContainer container = new AddressUcContainer(this.Address);
16: this.LayoutTemplate.InstantiateIn(container);
17: //dodanie kontrolki do placeholdera
18: PlaceHolderAddressTemplate.Controls.Add(container);
19: }
20: }
21: [PersistenceMode(PersistenceMode.InnerProperty)]
22: [TemplateContainer(typeof(AddressUcContainer))]
23: public ITemplate LayoutTemplate { get; set; }
24: public Address Address { get; set; }
25: }
Ostatnim krokiem jest zdefiniowanie kontenera nazw. W tym wypadku klasa dziedziczy po Control oraz implementuje interfejs INamingContainer. Wszystko wygląda tak:
1: public class AddressUcContainer : Control, INamingContainer
2: {
3: public AddressUcContainer(Address address)
4: {
5: this.Address = address;
6: }
7: public Address Address { get; set; }
8: }
Używanie Templated User Control
Podobnie jak przy zwykłej kontrolce użytkownika, templated user control przeciągamy na naszą stronę. Dyrektywa wygląda wtedy w ten sposób:
1: <%@ Register src=”AddressUcTemplated.ascx”
2: tagname=”AddressUcTemplated” tagprefix=”uc1” %>
Następnie musimy stworzyć wygląd dla naszej kontrolki. Robi się to w tagach <LayoutTemplate>, które są zdefiniowane we właściwości ITemplate. Wewnątrz szablonu możemy odwoływać się do naszego kontenera za pomocą obiektu Container. Nasz szablon prezentuje się tak:
1: <uc1:AddressUcTemplated ID=”AddressUcTemplated1”
2: runat=”server” AddressType=”Home”>
3: <LayoutTemplate>
4: <h1>Edit Home Address</h1>
5: <table>
6: <tr>
7: <td>Address Line 1:</td>
8: <td>
9: <asp:TextBox ID=”TextBoxAddress” runat=”server”
10: Text="<%#Container.Address.AddressLine1%>"></asp:TextBox>
11: </td>
12: </tr>
13: <tr>
14: <td>Address Line 2:</td>
15: <td>
16: <asp:TextBox ID=”TextBoxAddressLine2” runat=”server”
17: Text="<%#Container.Address.AddressLine2%>"></asp:TextBox>
18: </td>
19: </tr>
20: <tr>
21: <td>City:</td>
22: <td><asp:TextBox ID=”TextBoxCity” runat=”server”
23: Text="<%#Container.Address.City%>"></asp:TextBox>
24: </td>
25: </tr>
26: <tr>
27: <td>State:</td>
28: <td>
29: <asp:TextBox ID=”TextBoxState” runat=”server”
30: Text="<%#Container.Address.State%>"></asp:TextBox>
31: </td>
32: </tr>
33: <tr>
34: <td>Zip:</td>
35: <td><asp:TextBox ID=”TextBoxZip” runat=”server”
36: Text="<%#Container.Address.Zip%>"></asp:TextBox>
37: </td>
38: </tr>
39: <tr>
40: <td></td>
41: <td>
42: <asp:Button ID=”ButtonSave” runat=”server” Text=”Save” />
43: </td>
44: </tr>
45: </table>
46: </LayoutTemplate>
47: </uc1:AddressUcTemplated>
Musimy jeszcze dodac do code-behind. Kod powinien wyłować metodę Page.DataBind aby zapewnić, że kontener jest powiązny z layoutem.
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: //symulacja wpowadzania danych
4: AddressUcTemplated1.Address.AddressLine1 = “1234 Some St.";
5: AddressUcTemplated1.Address.City = “Ann Arbor”;
6: AddressUcTemplated1.Address.State = “MI”;
7: AddressUcTemplated1.Address.Zip = “48888”;
8: //bindowanie danych
9: Page.DataBind();
10: }
Wszystko wygląda tak:

To wszystko na dziś. Zapraszam na kolejny artykuł z serii, który pojawi się, jak zwykle, w piątek.