Analytická hádanka - žhnoucí vztahy mezi třídami
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 , 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.
A 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)
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)
Compact .Net Framework
ASP.NET - jednoduchý přístup z kódu na webovém formuláři k prvkům deklarovaným v šabloně
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(" ");
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(" ");
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)
.NET Framework | ASP.NET