\

Školení Návrhové vzory, OOP a UML


 Thursday, 20 July 2006
Sloupec v GridView s potvrzením vymazání záznamu (na klientovi) II - sloupec se šablonou

V prvním spotu jsem ukázal, jak napsat pro GridView nový sloupec zobrazující tlačítko pro vymazání záznamu. Po stisknutí tlačítka se zobrazilo okno vytvořené v Javascriptu, ve kterém byl uživatel požádán o potvrzení vymazání záznamu. Náš nový sloupec byl potomkem všem důvěrně známého sloupce ButtonField. Jak jsme ale viděli v kódu třídy DeleteButtonColumn, neobešli jsme se bez několika ne úplně čistých praktik.

Stejný sloupec se dá také vytvořit jako potomek třídy TemplateField. Místo deklarativní šablony na stránce ale vytvoříme vlastná šablonu pro náš nový sloupec implemetací rozhraní ITemplate. Jak se asi shodneme, šablony jsou jedna z nejlepších věcí v ASP.NET již od verze 1.0 - místo toho, aby nám tvůrci ASP.NET diktovali, jak a z čeho musejí být ASP.NET prvky vystavěny, můžeme v šablonách dodat vlastní obsah ASP.NET prvku či jedné jeho vlastnosti a přitom si stále užívat všech výhod událostního modelu i předdefinovaného chování ASP.NET komponenty.

Nejprve kód našeho nového sloupce pro GridView:

namespace RStein.Web.UI
{
    /// <summary>
    /// Nový typ sloupce pro GridView obsahující tlačítko pro smazání záznamu s potvrzením na klientovi (JS)
    /// </summary>
    public class DeleteButtonField2 : TemplateField
    {
        #region Public constants
        /// <summary>
        /// Id vygenerovaného tlačítka
        /// </summary>
        public const string DELETE_BUTTON_ID = "DeleteButton";
        #endregion Public constants
        #region Private constants
        public const string DELETE_COMMAND = "Delete";
        public const string DEFAULT_BUTTON_TEXT = "Smazat";
        public const string DEFAULT_CONFIRM_MESSAGE = "Opravdu smazat záznam?";
        public const string DELETE_JS = "javascript: if (!confirm('{0}')) return false;";
        #endregion Private constants

        #region Private fields
        private DeleteButtonTemplate m_currTemplate;
        #endregion Private fields

        #region Constructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        public DeleteButtonField2()
        {
            m_currTemplate = new DeleteButtonTemplate(this);

            ItemTemplate = m_currTemplate;
            AlternatingItemTemplate = m_currTemplate;
            
            EditItemTemplate = m_currTemplate;
        }
        #endregion Constructors

        #region Public properties
        /// <summary>
        /// Typ tlačítka (odkaz-LinkButton, Běžné tlačítko-Button)
        /// </summary>
        /// <exception cref="NotSupportedEception">Pokus o nastavení vlastnosti ButtonType na nepodporovaný typ Image</exception>
        public ButtonType ButtonType
        {
            get
            {
                object buttType = ViewState["ButtonType"];

                if (buttType == null)
                {
                    return ButtonType.Button;
                }

                return ((ButtonType)buttType);
            }

            set
            {
                if (value == ButtonType.Image)
                {
                    throw new NotSupportedException("Image button is currently not supported");
                }

                ViewState["ButtonType"] = value;
            }
        }

        /// <summary>
        ///Text na tlačítku 
        /// </summary>
        public string ButtonText
        {
            get
            {
                string btnDescription = (string)ViewState["ButtonDescription"];

                if (btnDescription == null)
                {
                    return DEFAULT_BUTTON_TEXT;
                }

                return (btnDescription);
            }

            set
            {
                ViewState["ButtonDescription"] = value;
            }
        }

        /// <summary>
        ///Text varování, které se má zobrazit uživateli při pokusu vymazat záznam
        /// </summary>
        public string ConfirmMessage
        {
            get
            {
                string confirmMessage = (string)ViewState["ConfirmMessage"];

                if (confirmMessage == null)
                {
                    return DEFAULT_CONFIRM_MESSAGE;
                }

                return (confirmMessage);
            }

            set
            {
                ViewState["ConfirmMessage"] = value;
            }
        }
        #endregion Public properties
    }
}

Třída DeleteButtonField2 je potomkem třídy Template Field. Ve svém konstruktoru do zděděných vlastností ItemTemplate (výchozí šablona pro řádek), AlternatingItemTemplate (šablona pro alternativní zobrazení řádku) a EditItemTemplate (šablona použitá při editaci řádku) dosadí instanci šablony DeleteButtonTemplate, jejíž kód uvidíme za chvíli. Šabloně je do kostruktoru předán odkaz na rodičovský sloupec, protože šablona DeleteButtonTemplate vytváří svůj obsah také podle hodnot veřejných vlastností třídy DeleteButtonField. Vlastnost ButtonType určuje typ zobrazovaného tlačítka. Podporujeme nyní pouze varianty Button (běžné tlačítko) a LinkButton (tlačítko ve formě hypertextového odkazu). Ve vlastnosti ButtonText je uložen popisek tlačítka a do vlastnosti ConfirmMessage můžeme uložit text potvrzující zprávy, která má být uživateli zobrazena v javascriptovém okně (text JS dialogu Confirm).

Jedním z důvodů, proč vlastnosti v třídě DeleteButtonField2 nedelegují při přístupu ke svým vlastnostem vykonání přímo na stejné vlastnosti, které by mohly být nadeklarovány v šabloně DeleteButtonTemplate,  je potřeba využívat ViewState. Náš sloupec participuje na ViewState automaticky, kdežto do šablony DeleteButtonTemplate bychom podporu pro ViewState museli dodat vlastní realizací rozhraní IStateManager. Pro naše účely je ale využití ViewState v třídě  DeleteButtonField2 dostačující.

namespace RStein.Web.UI
{
    /// <summary>
    /// Šablona s tlačítkem pro potvrzení smazání záznamu z <see cref="GridView"/>
    /// </summary>
    internal class DeleteButtonTemplate : ITemplate
    {
        #region Private members
        private DeleteButtonField2 m_parent;
        #endregion Private members
        #region Constructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        public DeleteButtonTemplate(DeleteButtonField2 parent)
        {
            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

            m_parent = parent;
        }
        #endregion Constructors

        #region Public methods
        #region ITemplate Members
        /// <summary>
        /// Metoda, v níž jsou vytvořeny ovládací prvky šablony a přidány do kolekce Controls argumentu container;
        /// </summary>
        /// <param name="container">Ovládací prvek, v němž je šablona instanciována</param>
        public void InstantiateIn(Control container)
        {
            IButtonControl newButton = null;

            if (m_parent.ButtonType == ButtonType.Button)
            {
                newButton = new Button();
                ((Button)newButton).OnClientClick = String.Format(DeleteButtonField2.DELETE_JS, m_parent.ConfirmMessage);
            }
            else
            {
                newButton = new LinkButton();
                ((LinkButton)newButton).OnClientClick = String.Format(DeleteButtonField2.DELETE_JS, m_parent.ConfirmMessage);
            }

            newButton.CommandName = DeleteButtonField2.DELETE_COMMAND;
            newButton.Text = m_parent.ButtonText;


            Control newControl = newButton as Control;
            newControl.ID = DeleteButtonField2.DELETE_BUTTON_ID;

            container.Controls.Add(newControl);

        }

        #endregion  ITemplate Members
        #endregion Public methods
    }
}

Třída DeleteButtonTemplate musí implementovat rozhraní ITemplate. Rozhraní ITemplate je složeno z jediné metody s názvem InstantiateIn. Metoda InstantiateIn dostane v argumentu container odkaz na "nadřízený" ovládací prvek, jehož součástí je šablona ITemplate, a její odpovědností je přidat do kolekce ovládacích prvků  "nadřízeného" ovládacího prvku ovládací prvky (celý obsah) šablony.

V metodě InstantiateIn vytvoříme tlačítko (Buton nebo LinkButton), kód pro pro potvrzení vymazání záznamu vložíme do vlastnosti OnClientClick, a zadáme uživatelský popisek (Text) nového tlačítka. Dále nastavíme jméno přlkazu (CommandName) tlačítka na řetězec 'Delete', který je pro GridView při zpracovávání událostí "wellknown" signálem, že si uživatel přeje vymazat záznam. Nastavíme Id nového tlačítka a nakonec jej vložíme do kolekce Controls "nadřízeného" prvku v argumentu container.

Použití nového sloupce se ničím neliší od použití sloupce vytvořeného v přechozím spotu:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<%@ Register Assembly="ClassLibrary1" Namespace="RStein.Web.UI" TagPrefix="custom" %>

<!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>Untitled Page</title>

   </head>

   <body>

      <form id="form1" runat="server">

      <div>

      <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" OnRowDeleting="GridView1_RowDeleting" DataKeyNames="Id" DataSourceID="SqlDataSource1">

         <Columns>

            <asp:BoundField DataField="Name" HeaderText="Name" SortExpression="Name" />

            <asp:BoundField DataField="State" HeaderText="State" SortExpression="State" />

            <custom:DeleteButtonField2 ButtonText="Smazat" ButtonType="Button" ConfirmMessage="Smazat z znam?"/>

         </Columns>

      </asp:GridView>

      </div>

      </form>

   </body>

</html>



Thursday, 20 July 2006 13:08:06 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  ASP.NET