\

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


 Sunday, 29 October 2006
Analytická hádanka - žhnoucí vztahy mezi třídami
Hádanka

Slovo "žhnoucí" v nadpisu spotu není zavedením dlouho očekávané extenze do UML pro označení libidinózního vztahu mezi třídami :-D, ale jen a pouze hodnotí vztahy, které jsou kvalitními kandidáty na epicentra pořádného požáru, v němž veškeré výhody OOP návrhu jsou devótně obětovány božstvu "najděte v aplikaci podstatná jména, nasekejte z nich třídy a pak si dejte panáka na další úspěch". ;-)

Diagram je záměrně neúplný, takže bych chtěl slyšet, jak by se dal upravit/rozvinout a jaké předpoklady jsou v něm obsaženy.

 

diagram tříd - hádanka na vztahy

 

 

zatím jde jen o triviální problém, ale na subtilnější kličky narazíme brzy v dalších hádankách :)

 



Sunday, 29 October 2006 22:41:37 (Central Europe Standard Time, UTC+01:00)       
Comments [23]  


Kontrola duplicitního spuštění aplikace v Compact .Net Frameworku - Windows CE

I když samotný Compact .Net Framework v souladu s doporučeními Microsoftu pro vývoj Windows Mobile aplikací zajišťuje, že je vždy spuštěna nanejvýš jedna instance aplikace, v Compact .Net Frameworku spuštěném na "čistých" Windows CE již zmíněné pravidlo neplatí a kontrolu na opakované spuštění aplikace musíme doplnit sami.

Tento kód funguje pro Compact .Net Framework od verze 1.x a byl otestován na Windows CE 4.2 a vyšších.

private const int ERROR_ALREADY_EXISTS = 183;

[DllImport("CoreDll.dll")]
private static extern int GetLastError(); 

[DllImport("CoreDll.dll", EntryPoint="CreateMutexW")]

private static extern int CreateMutex( IntPtr
lpMutexAttributes, bool InitialOwner, string MutexName );

public static bool AppAlreadyStarted() 
{ 
string myID =
System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;

if( CreateMutex(IntPtr.Zero, true, myID) != 0 )
{
    return (GetLastError() == ERROR_ALREADY_EXISTS);
} 

return false; 
}

Můžeme také najít formulář již spuštěné aplikace podle titulku, přenést jej do popředí a duplicitní instanci aplikace ukončit.

namespace DeviceApplication1
{
static class Program
{
[DllImport("coredll", EntryPoint="FindWindow")] 
private static extern IntPtr FindWindow( 
string lpClassName, 
string lpWindowName); 


[DllImport("coredll", EntryPoint="SetForegroundWindow")] 
private static extern bool SetForegroundWindow(IntPtr hWnd); 

[MTAThread]
static void Main()
{


IntPtr mainWindowHwnd = FindWindow(null, "DeviceMain");


if (mainWindowHwnd.Equals(IntPtr.Zero)) 
{ 
   Application.Run(new Form1()); 
} 
else 
{
   SetForegroundWindow(mainWindowHwnd); 
   Application.Exit(); 
} 

}

}


Sunday, 29 October 2006 20:52:11 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework


 Monday, 09 October 2006
ASP.NET - jednoduchý přístup z kódu na webovém formuláři k prvkům deklarovaným v šabloně
.Net Framework V ASP.NET 1.x jsme při přístupu k prvkům v šabloně (šablonou rozumíme vlastnost serverového ovládacího prvku typu ITemplate) museli zavolat metodu FindControl a předat ji Id hledaného prvku. A protože návratovou hodnotou metody FindControl je pouze rozhraní třídy Control, byli jsme nuceni přetypovávat na odvozený ovládací prvek.
  Label lblMess = LoginControl1.FindControl("lblMessage") as Label;

U prvků DataList, Repeater a dalších, kteří používají šablony pro opakování stejného obsahu pro každý řádek v datovém zdroji (ItemTemplate, AlternateItemTemplate) je použití metody FindControl odůvodněné. Ovládací prvky v šabloně jsou vytvořeny opakovaně pro všechny řádky v datovém zdroji a rodičovský prvek šablony implementací rozhraní INamingContainer zajišťuje, že každá instance šablony je v html formuláři složena z html elementů s unikátními hierarchickými názvy. Metoda FindControl na řádku  Datalistu, Repeateru si můžeme zjednodušeně představit jako "překladač" dlouhého, hierarchického a automaticky generovaného Id na jednoduché Id zadané v šabloně. Vidíme-li na stránce Id LoginControl1_ctl00_lblMessage, můžeme se k prvku s Id 'lblMessage' dostat tak, jak jsme si ukázali v kódu výše.

Proč si ale komplikovat život, když máme na stránce vždy maximálně jednu instanci šablony? Šablona prvku WizardStep bude na stránce právě jednou, protože šablonu jednoho kroku v průvodci (asp:wizard) nepoužíváme pro opakovanou instanciaci stejného obsahu, ale jen pro vytvoření vlastního vzhledu jednoho kroku průvodce. Šablonu nám prvek wizard nabízí jen proto, abychom si mohli vytvořit pěkné vlastní uživatelské rozhraní a nebyli sešněrováni ve svém tvůrčím rozletu představami ASP.NET týmu. Pak ale není žádný důvod, abychom k prvkům šablony přistupovali přes metodu FindControl. Prvky jsou na stránce pouze jednou a je bezpečné na ně odkazovat přímo na úrovni stránky jako na každý jiný ovládací prvek v ASP.NET formuláři, protože nehrozí kolize jejich Id.

ASP.NET 2.0 dovoluje u každé vlastnosti typu ITemplate určit, jestli bude šablona instanciována na jedné stránce opakovaně, anebo zda se na šablona stránce vyskytne nanejvýš jednou. Informaci o tom, jak budete šablonu používat, nese nový atribut TemplateInstance, kterým dekorujete vlastnost ITemplate.

 [TemplateInstance(TemplateInstance.Single)]

Hodnotu Single z enumerace TemplateInstance ASP.NET interpretuje jako příkaz k vygenerování typových proměnných na úrovni stránky pro všechny ovládací prvky v šabloně.

Když atribut TemplateInstance nepoužijeme nebo zvolíme režim TemplateInstance.Multiple, k vygenerování proměnných nedojde a ASP.NET 2.0 se k prvkům k šabloně chová stejně jako ASP.NET 1.x.

Zde je jednoduchý serverový ovládací prvek LoginControl, který obsahuje dvě šablony - jednu pro anonymní uživatele a druhou, asi nepřekvapivě,  pro přihlášené uživatele. Obě šablony jsou dekorovány atributem TemplateInstance s hodnotou TemplateInstance.Single - jako autoři ovládacího prvku víme, že určitě nebudeme používat více instancí jedné šablony.

namespace RStein.Web.UI.WebControls
{
    /// <summary>
    /// Serverový ovládací prvek, který dovoluje definovat odlišné šablony pro anonymního a přihlášeného uživatele
    /// </summary>
    [DefaultProperty("AnonymousUserMessage")]
    [DefaultEvent("LoginRequest")]
    [ToolboxData("<{0}:LoginControl runat=server></{0}:LoginControl>")]
    public class LoginControl : CompositeControl
    {
        

        #region Delegates
        public delegate void LoginItemCommandEventHandler (object sender, CommandEventArgs e);
        #endregion Delegates
        #region Public Constants
        /// <summary>
        /// popisek na tlačítko pro událost <see cref="LoginRequest"/>
        /// </summary>
        public const string LOGIN_BUTTON_NAME = "Login";
        
        /// <summary>
        /// Konstanta reprezentuje popisek na tlačítko pro událost <see cref="LogoutRequest"/>
        /// </summary>
        public const string LOGOUT_BUTTON_NAME = "Logout";
        
        #endregion Public Constants

        #region Events Keys

        /// <summary>
        /// Klíč události <see cref="LoginRequest"/>
        /// </summary>
        public static readonly Object LoginRequestKey = new Object();
        
        /// <summary>
        /// Klíč události <see cref="LoginRequest"/>
        /// </summary>
        public static readonly Object LogoutRequestKey = new Object();
        
        /// <summary>
        /// Klíč události <see cref="ItemCommand"/>
        /// </summary>
        public static readonly Object ItemCommandKey = new Object();
        
        #endregion Events Keys
        

        #region Public Events
        /// <summary>
        /// Událost 'Přihlásit uživatele' je vyvolána po kliknutuí na tlačítko s popiskem <see cref="LoginButtonName"/>
        /// </summary>
        public event EventHandler LoginRequest
        {
            add
            {
                Events.AddHandler(LoginRequestKey, value);
            }
            
            remove
            {
                Events.RemoveHandler(LoginRequestKey, value);
            }
        }
        
        /// <summary>
        /// Událost 'Odhlásit  uživatele' je vyvolána po kliknutuí na tlačítko s popiskem <see cref="LogoutButtonName"/>
        /// </summary>
        public event EventHandler LogoutRequest
        {
            add
            {
                Events.AddHandler(LogoutRequestKey, value);
            }
            
            remove
            {
                Events.RemoveHandler(LogoutRequestKey, value);
            }
        }
        
        /// <summary>
        /// Událost zprostředkovává události vnořených ovládacích prvků
        /// </summary>
        public event LoginItemCommandEventHandler ItemCommand
        {
            add
            {
                Events.AddHandler(ItemCommandKey, value);
            }
            
            remove
            {
                Events.RemoveHandler(ItemCommandKey, value);
            }
        }
        
        #endregion Public Events
        
        
        #region Private variables
        private LoginControlContent m_content;
        private ITemplate m_anonymousTemplate;
        private ITemplate m_loggedInTemplate;
        #endregion Private variables
        
        #region Contructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        public LoginControl()
        {

        }
        #endregion Contructors
        
        
        #region Public properties
    
        /// <summary>
        /// Vlastnost dovoluje přistupovat k prvkům šablony přes FindControl
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public LoginControlContent Content
        {
            get
            {
                EnsureChildControls();
                return m_content;
            }
        }

        /// <summary>
        /// Šablona pro přihlášeného uživatele
        /// </summary>
        [Browsable(false)]
        [DefaultValue(null)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(LoginControlContent))]
        [TemplateInstance(TemplateInstance.Single)]
        public ITemplate LoggedInTemplate
        {
            get
            {
                return m_loggedInTemplate;
            }

            set
            {
                m_loggedInTemplate = value;
            }
        }

        /// <summary>
        /// Šablona pro nepřihlášeného uživatele
        /// </summary>
        [Browsable(false)]
        [DefaultValue(null)]
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(LoginControlContent))]
        [TemplateInstance(TemplateInstance.Single)]
        public ITemplate AnonymousTemplate
        {
            get
            {
                return m_anonymousTemplate;
            }

            set
            {
                m_anonymousTemplate = value;
            }
        }
        
        /// <summary>
        /// Informace zobrazená nepřihlášenému uživateli
        /// </summary>
        [Category("Behavior")]
        [Bindable(true)]
        [DefaultValue("")]
        [Description("Informace zobrazená nepřihlášenému uživateli")]
        public string AnonymousUserMessage
        {
            get
            {
                string message = (string) ViewState["AnonymousUserMessage"];
                return (message == null ? String.Empty : message);
            }
            
            set
            {
                ViewState["AnonymousUserMessage"] = value;
            }
        }

        /// <summary>
        /// Informace zobrazená přihlášenému uživateli
        /// </summary>
        [Category("Behavior")]
        [Bindable(true)]
        [DefaultValue("")]
        [Description("Informace zobrazená přihlášenému uživateli")]
        public string LoggedInMessage
        {
            get
            {
                string message = (string) ViewState["LoggedInMessage"];
                return (message == null ? String.Empty : message);
            }
            set
            {
                ViewState["LoggedInMessage"] = value;
            }
        }
        #endregion Public properties        
        
        #region Protected properties        
        
        /// <summary>
        /// Prvek bude uzavřen v HTML značce DIV
        /// </summary>
        protected override HtmlTextWriterTag TagKey
        {
            get
            {
                return HtmlTextWriterTag.Div;
            }
        }

        #endregion Protected properties        

        #region Protected methods
        
    
        /// <summary>
        /// Vytvoření vnořených ovládacích prvků
        /// </summary>
        protected override void CreateChildControls()
        {
            
            Controls.Clear();
            
            if (Page.User.Identity.IsAuthenticated)
            {
                m_content = new LoginControlContent(Page.User.Identity.Name, AnonymousUserMessage, LoggedInMessage);
                ITemplate template = null;

                if (m_loggedInTemplate != null)
                {
                    template = m_loggedInTemplate;
                }
                else
                {
                    template = new DefaultLoggedInTemplate();
                }
                
                template.InstantiateIn(m_content);
                
            }
            else
            {
                m_content = new LoginControlContent(String.Empty, AnonymousUserMessage, LoggedInMessage);
                
                ITemplate template = null;
                
                if (m_anonymousTemplate != null)
                {
                    template = m_anonymousTemplate;
                }
                else
                {
                    template = new DefaultAnonymousTemplate();
                }
                
                template.InstantiateIn(m_content);
                    
                
            }
            
            this.Controls.Add(m_content);
            
        }

        /// <summary>
        /// Přpsání metody, která zachycuje bublané události
        /// </summary>
        /// <param name="source">Zdroj bublané události</param>
        /// <param name="args">Argumenty bublané události</param>
        /// <returns><see cref="true"/> - Událost byla zpracována, <see cref="false"/> - Událost nebyla zpracována</returns>
        protected override bool OnBubbleEvent(object source, EventArgs e)
        {
            CommandEventArgs ex = e as CommandEventArgs;
            if (ex != null)
            {
                if (ex.CommandName.ToLower() == LOGIN_BUTTON_NAME.ToLower())
                {
                    OnLoginRequest(new EventArgs());
                }

                else if (ex.CommandName.ToLower() == LOGOUT_BUTTON_NAME.ToLower())
                {
                    OnLogoutRequest(new EventArgs());
                }

                else
                {
                    OnItemCommand(ex);
                }
                
                return true;
            }
            
            return false;
        }

        /// <summary>
        /// Metoda odpovědná za vyvolání události loginRequest
        /// </summary>
        /// <param name="e">Parametry události</param>
        protected virtual void OnLoginRequest(EventArgs e)
        {
            EventHandler eh = (EventHandler) Events[LoginRequestKey];
            if (eh != null)
            {
                eh(this, e);
            }
        }

        /// <summary>
        /// Metoda odpovědná za vyvolání události LogoutRequest
        /// </summary>
        /// <param name="e">Parametry události</param>
        protected virtual void OnLogoutRequest(EventArgs e)
        {
            EventHandler eh = (EventHandler) Events[LogoutRequestKey];

            if (eh != null)
            {
                eh(this, e);
            }
        }

        /// <summary>
        /// Metoda odpovědná za vyvolání události ItemCommand
        /// </summary>
        /// <param name="e">Parametry události</param>
        protected virtual void OnItemCommand(CommandEventArgs e)
        {
            LoginItemCommandEventHandler eh = (LoginItemCommandEventHandler) Events[ItemCommandKey];
           
            if (eh != null)
            {
                eh(this, e);
            }
        }

        #endregion Protected methods
    }
}

LoginControl použijeme na testovací stránce a zadáme anonymní šablonu.

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="WebControlLibrary1" Namespace="RStein.Web.UI.WebControls"
    TagPrefix="cc2" %>

<!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>Testovací stránka</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <cc2:LoginControl ID="LoginControl1" runat="server" AnonymousUserMessage="Přihlašte se prosím" OnLoginRequest="LoginControl1_LoginRequest">
            <AnonymousTemplate>
                <asp:label runat="server" ID="lblMessage"><%#Container.AnonymousMessage%> </asp:label> <br />
                <asp:LinkButton Id="Login" runat="server" Text="Přihlásit se" />                          
            </AnonymousTemplate>
        </cc2:LoginControl>
        <
    </div>
    </form>
</body>
</html>

Tlačítko Login i popisek lblMessage deklarované v šabloně jsou přímo dostupné ze stránky, jak si můžeme ověřit, když v události PreRender stránky změníme barvu pozadí popisku lblMessage. Nemusíme používat metodu FindControl, ani přetypovávat, což jsou docela příjemná vylepšení kódu ;)

 private void Page_PreRender(object sender, EventArgs e)
    {
        LoginControl1.DataBind();

        if (lblMessage != null)
        {
            lblMessage.BackColor = Color.Cyan;
        }
    }

 

Abyste mohli ovládací prvek LoginControl zkompilovat, následuje kód používaných pomocných tříd.

using System;
using System.Web.UI.WebControls;
using System.Web.UI;
using System.ComponentModel;

namespace RStein.Web.UI.WebControls
{
    /// <summary>
    /// Kontejner pro šablony
    /// </summary>
    [ToolboxItem(false)]
    public class LoginControlContent : Control, INamingContainer
    {
        #region Private variables
        private string m_userName;
        private string m_anonymousMessage;
        private string m_loggedInMessage;
        #endregion Private variables
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="userName">Jméno uživatele</param>
        /// <param name="anonymousMessage">Zpráva pro nepřihlášeného uživatele</param>
        /// <param name="loggedInMessage">Zpráva pro přihlášeného uživatele</param>
        internal LoginControlContent(string userName, string anonymousMessage, string loggedInMessage)
        {
            m_userName = userName;
            m_anonymousMessage = anonymousMessage;
            m_loggedInMessage = loggedInMessage;
        }
        
        /// <summary>
        /// Jméno uživatele
        /// </summary>
        public string UserName
        {
            get
            {
                return m_userName;
            }
        }

        /// <summary>
        ///Zpráva pro nepřihlášeného uživatele
        /// </summary>
        public string AnonymousMessage
        {
            get
            {
                return m_anonymousMessage;
            }
        }

        /// <summary>
        ///Zpráva pro přihlášeného uživatele
        /// </summary>
        public string LoggedInMessage
        {
            get
            {
                return m_loggedInMessage;
            }
        }
    }
}

using System;
using System.Web.UI.WebControls;
using System.Web.UI;

namespace RStein.Web.UI.WebControls
{
    /// <summary>
    /// Standardní šablona pro nepřihlášeného uživatele
    /// </summary>
    public class DefaultAnonymousTemplate : ITemplate
    {
        #region Constructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        internal DefaultAnonymousTemplate()
        {
        }
        #endregion Constructors

        #region public methods
        #region ITemplate Members
        /// <summary>
        /// Implementace InstantiateIn - vytvoření ovládacích prvků anonymní šablony
        /// </summary>
        /// <param name="container">Ovládací prvek, do jehož kolekce Controls bzdou prvky šablony přidány</param>
        public void InstantiateIn(Control container)
        {
            Label lblMessage = new Label();
            lblMessage.ID = "lblMessage";
            lblMessage.DataBinding += new EventHandler(lblMesssage_DataBind);
            
            LiteralControl separator = new LiteralControl("&nbsp;");
            
            LinkButton btnLogin = new LinkButton();
            btnLogin.ID = "btnLogin";
            btnLogin.Text = "Přihlásit";
            btnLogin.CommandName = LoginControl.LOGIN_BUTTON_NAME;

            container.Controls.Add(lblMessage);
            container.Controls.Add(separator);
            container.Controls.Add(btnLogin);


        }

        #endregion ITemplate Members
        #endregion public methods
        
        #region private methods
        //Handler události DataBind prvku lblMessage
        private void lblMesssage_DataBind(object sender, EventArgs e)
        {
            Label lblMessage = (Label) sender;
            LoginControlContent currContent = lblMessage.NamingContainer as LoginControlContent;
            lblMessage.Text = currContent.AnonymousMessage;
        }
        #endregion private methods
    }
}

using System;
using System.Web.UI.WebControls;
using System.Web.UI;

namespace RStein.Web.UI.WebControls
{
    /// <summary>
    /// Standardní šablona pro přihlášeného uživatele
    /// </summary>
    public class DefaultLoggedInTemplate : ITemplate
    {
        #region constructors
        /// <summary>
        /// Konstruktor
        /// </summary>
        internal DefaultLoggedInTemplate()
        {
        }
        #endregion constructors

        #region public methods
        #region ITemplate Members
        /// <summary>
        /// Implementace InstantiateIn - vytvoření ovládacích prvků anonymní šablony
        /// </summary>
        /// <param name="container">Ovládací prvek, do jehož kolekce Controls bzdou prvky šablony přidány</param>
        public void InstantiateIn(Control container)
        {
            Label lblMessage = new Label();
            lblMessage.ID = "lblMessage";
            lblMessage.DataBinding += new EventHandler(lblMesssage_DataBind);
            
            LiteralControl separator = new LiteralControl("&nbsp;");
            
            LinkButton btnLogout = new LinkButton();
            btnLogout.ID = "btnLogout";
            btnLogout.Text = "Odhlásit";
            btnLogout.CommandName = LoginControl.LOGOUT_BUTTON_NAME;

            container.Controls.Add(lblMessage);
            container.Controls.Add(separator);
            container.Controls.Add(btnLogout);


        }

        #endregion ITemplate Members
        #endregion public methods
        
        #region private methods
        //Handler události DataBind prvku lblMessage
        private void lblMesssage_DataBind(object sender, EventArgs e)
        {
            Label lblMessage = (Label) sender;
            LoginControlContent currContent = lblMessage.NamingContainer as LoginControlContent;
            lblMessage.Text = currContent.LoggedInMessage + " " + currContent.UserName;
        }
        #endregion private methods
    }
}

 



Monday, 09 October 2006 15:42:53 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  .NET Framework | ASP.NET