Powtórne wykorzystanie kontrolek użytkownika (.ascx) w ASP.NET

Kontrolki użytkownika (User Controls) są elementem ASP.NET, który jest bardzo często wykorzystywany przez programistów. Zwłaszcza przy złożonych projektach ich obecność jest koniecznością. Alternatywą są oczywiście „zwykłe” kontrolki (Custom Controls), czyli klasy dziedziczące z Control (i w górę). Ich przewaga nad kontrolkami użytkownika polega na tym, że łatwo upakować je w bibliotekę, co umożliwia proste współdzielenie pomiędzy różnymi projektami. Wadą jest natomiast ich skomplikowana natura – od programisty wymagają znacznie większej wiedzy a przede wszystkim ilości czasu potrzebnego na oprogramowanie.Niemniej nie o tworzeniu kontrolek dziś. Dziś postaram się odpowiedzieć na pytanie: jak zachować zalety jednych i drugich korzystając wyłącznie z kontrolek użytkownika.

Metod jest kilka – przedstawię je pobieżnie a na koniec przedstawię własną kompilację wszystkich najlepszych cech tych rozwiązań (mam nadzieję :) ).

Metoda 1 – referencyjno kopiująca

Najprostszą a zarazem najmniej elegancką metodą jest stworzenie biblioteki kontrolek w oparciu o projekt aplikacji (Web Application). Do rozwiązania dodajemy projekt, który będzie stanowił bibliotekę. Do projektu dodajemy kontrolki użytkownika. Następnie w docelowym projekcie dodajemy referencję do uprzednio utworzonego projektu. Niestety sama referencja sprawy nie załatwi. Potrzebujemy jeszcze pliki .ascx. Możemy je dodać do projektu co najmniej na dwa sposoby:

  • poprzez skrypt wykonywany przed budową projektu (pre build), który skopiuje pliki z jednego projektu do drugiego
  • poprzez dodanie ich do projektu

Aby dodać istniejące pliki nie robiąc ich kopii w oknie dodawania istniejącego elementu wybieramy opcję Add As Link.

Tym oto sposobem możemy cieszyć się plikami. Pozostają drobne nieścisłości jak definicje CodeBehind w plikach .ascx, ale dopóki nie zmieniamy nic w tych plikach z poziomu projektu docelowego jesteśmy bezpieczni.

Metoda 2 – odnośniki

Metoda ta różni się od pierwszej tym że podpięciu (Add As Link) ulegają wszystkie pliki wchodzące w skład danej kontrolki (.ascx, .ascx.cs, .ascx.designer.cs…).

Metoda 3 – konwersja kontrolek użytkownika na „zwykłe” kontrolki

Metoda ta opiera się na kompilacji kontrolek użytkownika do postaci bibliotek. Szczegóły można znaleźć w artykule, w serwisie CodeProject, którego autorem jest Dimitar Madjarov. Artykuł dotyczy Visual Studio 2005, ale jest nadal aktualny.

Wadą tej metody jest to, iż w wyniku dostajemy oddzielne biblioteki dla każdej kontrolki o usystematyzowanych ale nieeleganckich nazwach :)

Metoda moja

Metoda ta łączy wszystkie dobre cechy wyżej wymienionych i wprowadza nieco porządku w chaosie :) . Gotowanie zacznijmy od przygotowania składników:

Oczywiście wszystko działa również na Visual Studio 2008, ale nie mogę odmówić sobie zabawy najnowszą wersją (naturalnie należy pobrać odpowiednią wersję Web Deployment Project).

Zacznijmy od stworzenia pustego rozwiązania. Następnie dodajemy do rozwiązania projekt witryny (Web Site). Należy o tym pamiętać – musi to być Web Site a nie Web Application. Dla ustalenia uwagi nazwijmy ją Web.Commons.

Do witryny dodajemy projekt wdrożenia (Web Deployment). Robimy to klikając prawym przyciskiem myszy na „projekcie” i wybierając Add Web Deployment Project. Projekt nazwijmy Web.Commons.Deployment. Kolejną czynnością będzie ustawienie odpowiednich opcji we właściwościach (Property Pages).

Na początek wyłączmy możliwość aktualizacji witryny:

Następnie ustalamy, że wszystkie elementy wynikowe mają się znaleźć w jednym zestawie wyjściowym:

Na koniec unikamy dodawania niepotrzebnych śmieci:

Aby dokończyć tworzenie struktury rozwiązania dodajemy do niego nowy projekt aplikacji web (Web Application), który będzie docelowym miejscem wykorzystania biblioteki. Nazwijmy go Web.

Nie pozostało nic innego jak dodać do biblioteki jakieś elementy. Możemy utworzyć je na dwa sposoby – z oddzielnym plikiem zawierającym kod, oraz lub jako samodzielny plik .ascx. W każdym przypadku nieco inaczej musimy zmodyfikować utworzone pliki, aby wszystko działało po naszej myśli.

Kontrolka użytkownika z oddzielnym plikiem kodu

Jako pierwszą dodamy kontrolkę FirstControl.ascx :) :

pamiętając o zaznaczeniu opcji Place code in separate file.

Przechodzimy do edycji pliku kodu i zmieniamy nazwę klasy na FirstControlBase oraz dodajemy deklarację przestrzeni nazw Web.Commons.

W pliku .ascx zmieniamy dyrektywę:

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="FirstControl.ascx.cs" Inherits="FirstControl" %>

na:

<%@ Control Language="C#" AutoEventWireup="true" ClassName="Web.Commons.FirstControl"
    CodeFile="FirstControl.ascx.cs" Inherits="Web.Commons.FirstControlBase" %>

Najistotniejszym elementem jest atrybut ClassName, który zawiera pełną nazwę typu, jaki zostanie wygenerowany. Gdybyśmy nie zmienili nazwy klasy w pliku kodu na FirstControlBase, kompilator zgłosiłby błąd zduplikowanego typu.

Pozostaje dodać kod do pliku .ascx:


<asp:Button runat="server" ID="PostBackButton" Text="Do not touch me!"
    onclick="PostBackButton_Click" />

oraz .cs:

namespace Web.Commons
{
    public partial class FirstControlBase : System.Web.UI.UserControl
    {
        protected void PostBackButton_Click(object sender, EventArgs e)
        {
            PostBackButton.Text = "You have touched me :( ";
        }
    }
}

Samodzielna kontrolka użytkownika

Dodajemy drugą kontrolkę – SecondControl.ascx, tym razem odznaczając opcję Place code in separate file:

Podobnie jak uprzednio dokonujemy modyfikacji dyrektywy Control z:

<%@ Control Language="C#" ClassName="SecondControl" %>

na:

<%@ Control Language="C#" ClassName="Web.Commons.SecondControl" %>

Pozostaje dodać ciało kontrolki oraz odpowiednie metody:

<script runat="server">
    protected void PostBackButton_Click(object sender, EventArgs e)
    {
        PostBackButton.Text = "You have clicked me!";
    }
</script>

<asp:Button runat="server" ID="PostBackButton" Text="Click me!"
    onclick="PostBackButton_Click" />

Korzystanie z jednego pliku ma tą zaletę, iż  nie wprowadzamy dodatkowej klasy, uzyskujemy bardziej elegancką hierarchię, ale bardziej zaśmiecamy plik .ascx. Co wybrać – to już Wasza decyzja.

Oczywiście nic nie stoi na przeszkodzie aby do biblioteki dodać inne typy – niekoniecznie kontrolki. Dodajemy je w katalogu App_Code. Typy zostaną zawarte w wynikowym zestawie. Na koniec kompilujemy rozwiązanie. W katalogu wyjściowym dla projektu Web.Commons.Deployment powinien znajdować się plik Web.Commons.dll.

W tym miejscu dodajemy do projektu Web referencje do wygenerowanego pliku. Gdy podejrzymy jego zawartość w eksploratorze obiektów, wszystko wygląda w porządku:

Do projektu dodajmy przykładową stronę: Default.aspx, gdzie zarejestrujemy i wykorzystamy kontrolki z biblioteki:


<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Web.Default" %>
<%@ Register Assembly="Web.Commons" Namespace="Web.Commons" TagPrefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <c:FirstControl runat="server" ID="FC" />
        <c:SecondControl runat="server" ID="SC" />
    </div>
    </form>
</body>
</html>

Oczywiście rejestrację biblioteki/kontrolek można przeprowadzić w pliku Web.Config, aby nie dodawać każdorazowo dyrektyw do kodu stron.

Całe rozwiązanie po wszystkich zabiegach prezentuje się mniej więcej tak: Działa tak: klik, klik

Wada metody jest taka, że nie można dodać bezpośrednich referencji pomiędzy projektami, tylko trzeba dodać odwołanie do fizycznej biblioteki DLL. No ale nie można mieć wszystkiego. Niewątpliwą zaletą jest, że musimy borykać się tylko z problemem zarządzania jednym zestawem, a nie szeregiem plików .ascx, .cs…

Mam nadzieję, że informacje te pomogą Wam w lepszym zarządzaniu Waszymi rozwiązaniami.

2 thoughts on “Powtórne wykorzystanie kontrolek użytkownika (.ascx) w ASP.NET

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

*

Możesz użyć następujących tagów oraz atrybutów HTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>