\


 Sunday, 16 July 2006
Po delší době literární hádanka...

Poznáte autora veršů? Pokud ano, smekám, protože o žádného "profláknutého" a v čítánkách "přišpendleného" autora nejde. 

A jen tak na okraj  - tahle báseň je pro mě důkazem toho, kolik (sebe)lítosti můžete stočit do slov a přitom nemít nic společného s citovým kriplem dnešního věku, takzvaným homo sentimentalis, kterému z huby odkapává při každé větě težkopádný rozněžnělý patos nad sebou samým. Patos, jehož nejbohatším zřídlem jsou histrionské citové výrony v soap operách a jiných televizních veledílech. :)

Kolik jí bude až ji zas potkám

Kolik jí bude až ji zas potkám?
A co já?
Řeka už nekouzlí
neschová.

Jen zrcadlo
hladina
na portrét tiše
zhasíná.

Kolik jí bude až ji zas potkám?
Stromy se skloní v temnou spleť?
Půjdeme spolu podél řeky?
Úlomky větví tenký led?
Tenký led.



Sunday, 16 July 2006 19:35:29 (Central Europe Standard Time, UTC+01:00)       
Comments [7]  Literární a jiné humanitní úlety


 Sunday, 09 July 2006
Chyba ve VS.NET 2005?
Chyba vulgo Bug

Včera a dneska jsem narazil na jedno "omezení-chybu" VS.NET 2005. Vždy, když jsem se snažil zkompilovat projekt, jsem dostal ve VS.NET pouze hlášku.

"An error has occured which the C# compiler is unable to report due to low memory or possible heap corruption. It is recommended that you save all your files, close and restart Visual Studio"

Restart VS.NET ale nezabral a dokonce ani nepomohl jindy všemocný restart počítače - měl jsem podezření, že po opakované hibernaci  notebooku, která ve Windows XP na počítačích s více než jedním GB paměti není ani zdaleka dokonalá, má problémy VS.NET se správou paměti přesně tak, jak se snaží reportovat v chybové zprávě.

Zkusil jsem projekt zkompilovat v příkazovém řádku:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Csc.exe /noconfig /nowarn:1701,1702 /errorreport:prompt
 /warn:4 /define:DEBUG;TRACE /reference:"C:\Documents and Settings\Rene\My Documents\Visual Studio 2005\Projects\RStein.RS\RStein.RS\RStein.RS.DataStorage\bin\Debug\RStein.RS.DataStorage.dll"
 /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
\System.configuration.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /debug+ /debug:full /optimize- /out:obj\Debug\RStein.RS.BusinessEntities.dll /target:library Articles\Article.cs
Articles\ArticleCategory.cs Articles\ArticleChapter.cs Articles\ArticleKeyword.cs
Articles\ArticlePhotoCategory.cs Articles\ArticleSerial.cs Articles\ArticleState.cs Task.cs Articles\ArticleTheme.cs Attachment.cs AttachmenType.cs Collections\ArticleCategoryCollection.cs
Collections\ArticleChapterCollection.cs Collections\TaskCollection.cs Collections\ArticleThemeCollection.cs Collections\BusinessCollectionBase.cs
Collections\CollectionChangeEventArgs.cs Collections\HWInformationTypeCollection.cs
 CommonInterfaces\IUIDataProvider.cs Helpers\BasicValidations.cs BusinessObjectBase.cs Category.cs
Collections\AttachmentCollection.cs Collections\CategoryCollection.cs Collections\DeviceInfoCollection.cs Collections\ObservationCollection.cs Helpers\CentralConfigData.cs
Helpers\GlobalConstants.cs Helpers\IdentityMap.cs HWInformationDataType.cs DeviceInfo.cs
Helpers\DataCacheHelper.cs Observation.cs CodeTableBase.cs
Collections\HWCategoryCollection.cs HWCategory.cs
HWInformationType.cs Language.cs Measurement.cs
Properties\AssemblyInfo.cs
CategoryObservation.cs Quantity.cs Unit.cs UnitType.cs UrlAttachment.cs

A zjistil jsem, že VS.NET svádí na kompilátor C# chybu, se kterou si nedokáže poradit samo. Kompilátor totiž bez reptání nareportoval chybu ve zdrojovém kódu.

Collections\TaskCollection.cs(10,18): error CS0309: The type
        'RStein.RS.BusinessEntities.Task' must be convertible to
        'RStein.RS.BusinessEntities.BusinessObjectBase' in order to use it as
        parameter 'T' in the generic type or method
        'RStein.RS.BusinessEntities.BusinessCollectionBase<T>'
Collections\BusinessCollectionBase.cs(8,18): (Location of symbol related to
        previous error)
Task.cs(7,18): (Location of symbol related to previous error)

 

VS.NET si tedy evidentně neumí vypořádat s touto kombinací:

Máte bázovou třídu pro typové kolekce s generickým typem T, u kterého vyžaduji, že musí být potomkem třídy BussinessObjectBase.

 

public class BusinessCollectionBase<T> : Collection<T> 
                                        where T : BusinessObjectBase
    {
    }
 

Dále máte odvozenou typovou kolekci pro business objekt - např. pro třídu Task.

 

public class TaskCollection : BusinessCollectionBase<Task>

{

}

A Třída Task není potomkem třídy BusinessObjectBase.

public class Task
    {
    }

 

Poté dostanete ve VS.NET výše uvedenou docela zmatečnou hlášku.

Stačí doplnit bázovou třídu BusinessObjectBase k třídě Task a kompilace i ve VS.NET proběhne tak jak má.

public class Task : BusinessObjectBase
    {
    }

Můžete prosím někdo toto chování ověřit na svém počítači, abych mohl zaměstnat Lady Bug Microsoftu? :) Díky.



Sunday, 09 July 2006 16:43:43 (Central Europe Standard Time, UTC+01:00)       
Comments [10]  .NET Framework


 Sunday, 02 July 2006
Řešení programátorské hádanky z 24.6.2006 - ThreadStartDelegate

Jak bylo patrné ze zadání "hádanky", snažil jsem se, abyste mi napsali, jaké postupy používáte pro předání argumentů a získání návratové hodnoty z metody, když jste omezeni signaturou delegátu ThreadStartDelegate. Třída Thread dokáže pracovat pouze s metodami bez návratových hodnot (void) a také:

a) Metoda nesmí mít žádné argumenty  - delegát ThreadStartDelegate

b) Metody přijímající jeden argument typu object - delegát ParameterizedThreadStart (pouze v  .Net Frameworku 2.0).

Jak jste se zmínili v komentářích - když chceme předat argumenty kódu vykonávanému v samostatném threadu, můžeme použít třídy s vlastním stavem a návratovou hodnotu získat třeba tak, že si zaregistrujeme "callback" funkci.

Mně by se ale líbilo, kdybych měl  jen jednu "stavovou" třídu s argumenty pro metody s jakoukoli signaturou a tedy společnou pro všechny rozdílné typy delegátů a hlavně chci objekt z této třídy přímo předat do konstruktoru třídy Thread, aby se můj kód nijak nelišíl od přímého použití delegátů ThreadStart a ParameterizedThreadStart.  Proto jsem v komentářích mluvil o eleganci zvoleného řešení :)

    class ThreadStartDelegateWrapper
    {
        #region Private members
        private Delegate m_innerDelegate;
        private object[] m_methodParameters;
        private object m_returnValue;
        #endregion Private members

        #region Construtors
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="del">Delegát ukazující na metodu, která poběží v jiném threadu</param>
        public ThreadStartDelegateWrapper(Delegate del)
        {
            if (del == null)
            {
                throw new ArgumentNullException("del");
            }
            m_innerDelegate= del;
        }

        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="del">Delegát ukazující na metodu, která poběží v jiném threadu</param>
        ///<param name="methodParameters">Argumenty metody</param>
        public ThreadStartDelegateWrapper(Delegate del, params object[] methodParameters) : this(del)
        {
            m_methodParameters = methodParameters;
        }

        #endregion Construtors

        #region Public properties
        /// <summary>
        /// Argumenty metody
        /// </summary>
        public object[] MethodParameters
        {
            get
            {
                return m_methodParameters;
            }
            set
            {
                m_methodParameters = value;
            }
        }

        /// <summary>
        /// Návratová hodnota metody
        /// </summary>
        public object ReturnValue
        {
            get
            {
                return m_returnValue;
            }
        }
        #endregion Public properties

        #region Operators
        /// <summary>
        /// Implicitní konverzní operátor, který instanci třídy <see cref="ThreadStartDelegateWrapper"/> převede na delegáta <see cref="ThreadStartDelegate"/>
        /// </summary>
        /// <param name="originalWrapper">Instance třídy <see cref="ThreadStartDelegateWrapper"/></param>
        /// <returns>Instanci delegáta <see cref="ThreadStartDelegate"/>, který ukazuje na metodu zapouzdřující zavolání jakéhokoli delegáta</returns>
        public static implicit operator ThreadStart(ThreadStartDelegateWrapper originalWrapper)
        {
            if (originalWrapper == null)
            {
                throw new ArgumentNullException("originalWrapper");
            }

            return new ThreadStart(originalWrapper.InvokeDelegate);
        }
        #endregion Operators

        #region private methods
        //Metoda, jejíž signatura odpovídá signatuře deklarované delegátem ThreadStartDelegate
        private void InvokeDelegate()
        {
            m_returnValue = m_innerDelegate.DynamicInvoke(m_methodParameters);
        }
        #endregion private methods
    }
 

Třídě ThreadStartDelegateWrapper můžete do konstruktoru předat odkaz na jakéhokoli delegáta a také můžete předat všechny potřebné argumenty metodě, na kterou delegát ukazuje (argument methodParameters v konstruktoru). Předané argumenty můžete kdykoli upravit, protože jsou zveřejněny ve vlastnosti MethodParameters.

Abych mohl svoji třídu použít kdekoli je očekáván delegát ThreadStart, napsal jsem implicitní konverzní operátor třídy ThreadStartDelegateWrapper na delegát ThreadStartDelegate.

Návratovou hodnotu metody vykonané v jiném threadu nalezneme ve vlastnosti ReturnValue - nic nám také nebrání přidat událost, ve které získanou návratovou hodnotu budeme sami aktivně distribuovat všem zájemcům.

A tady je jednoduchý ukázkový kód.

    class Test
    {
        public delegate string TestDelegate(string message);

        public string WriteMessage(string message)
        {
            Console.WriteLine(message);
            return "OK";
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test myTestClass = new Test();
            Test.TestDelegate myDelegate = myTestClass.WriteMessage;

            ThreadStartDelegateWrapper wrapper = new ThreadStartDelegateWrapper(myDelegate, new object[] { "Hello world" });
            Thread myThread = new Thread(wrapper);
            myThread.Start();
            myThread.Join();

            Console.WriteLine(wrapper.ReturnValue);
            Console.Read();
            
        }
    }

Je jednoduché rozšířit třídu ThreadStartDelegateWrapper o podporu dalších delegátů v .NET Frameworku (např. WaitCallback), takže na metody s různými signaturami mohou ukazovat typičtí delegáti v .Net Frameworku  s využitím jedné jediné třídy fungující jako obecný adaptér mezi naším kódem a kódem v .Net Frameworku.



Sunday, 02 July 2006 14:40:08 (Central Europe Standard Time, UTC+01:00)       
Comments [3]  .NET Framework | Programátorské hádanky


 Friday, 30 June 2006
Update 24.7. 2006 : Kurz objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací - 6.9. - 8.9. 2006

Update 24.7. 2006  - 1 volné místa v termínu 6.9. - 8.9. 2006

Update 18.7. 2006  - 2 zájemci se přehlásili na říjnový termín - zbývají tedy 2 volná místa v termínu 6.9. - 8.9. 2006

 Pokud byste měli zájem o stejný kurz v
termínu 25.-27.10. 2006, přihlašte se prosím předběžně na adrese petra@renestein.net

Rád bych Vás pozval na další běh kurzu "Objektovými principy a návrhovými vzory řízený design a vývoj kvalitních aplikací", který  proběhne 6.9 - 8.9. 2006.

Podrobné informace o kurzu a přihlášení na kurz.



Friday, 30 June 2006 10:12:22 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Kurzy UML a OOP


 Saturday, 24 June 2006
Vyřazení blogu ze služby Weblogy.cz - aktualizujte prosím své RSS čtečky

Protože můj blog již pravděpodobně nesplňoval všechny podmínky pro zařazení do seznamu zdrojů Weblogy (hlavně tedy určitě nemám blog o webdesignu a navíc z  html kódu generovaného DasBlogem se chce občas zvracet i mě, který na svatou inkvizici všech náctiletých webdesignérů, zřízenou za účelem šikanování ostatních pomocí W3C tortury a zaštítěnou magickou mocí svatých slov CSS, SEO, Pixy apod.:-),  nebere zřetel), prosím všechny ty, kdo byli zvyklí můj blog přes tuto agregační službu sledovat, aby si přihlásili odběr RSS nebo ATOM zdroje přímo na mém blogu.

Jestliže nemáte zájem o všechny spoty, což by mě tedy docela udivilo, ale lidé mají zvláštní úchylky a potřeby :-D, RSS jednotlivých tématických kategorií naleznete v pravém sloupci stránky.

Omlouvám se za toto mírné a mnou nezaviněné nepohodlí. ;-) 

 



Saturday, 24 June 2006 22:54:08 (Central Europe Standard Time, UTC+01:00)       
Comments [2]  Ostatní


Programátorská hádanka - jak metodě v delegátu ThreadStart předat argumenty?

Jak asi víte, delegát ThreadStart v .Net Frameworku může ukazovat pouze na metodu, která nemá žádné argumenty a vrací void.

Jak nejlépe metodě v delegátu ThreadStart předat argumenty? A navíc  - dokázali byste, aby metoda spuštěná v delegátu ThreadStart vrátila hodnotu, nebo si teď už opravdu jen vymýšlím po dobrém sobotním bourbonu nějaké rozkošné .Net 3.0 pohádky? ;-) Napadlo mě totiž docela elegantní řešení, tak by mě zajímalo, jestli ho někdo z vás už zná a používá? :-)

 



Saturday, 24 June 2006 22:28:13 (Central Europe Standard Time, UTC+01:00)       
Comments [12]  .NET Framework | Programátorské hádanky


 Friday, 19 May 2006
Sloupec v GridView s potvrzením vymazání záznamu (na klientovi)

Jestliže chcete mít v GridView sloupec, který uživatele požádá na klientovi (s využitím JavaScriptu) o potvrzeni smazání záznamu, je doporučeno používat TemplateField, jehož šablona ItemTemplate obsahuje ve vlastnosti OnClientClick tlačítka klientský kód.

<asp:TemplateField ShowHeader="False">

   <ItemTemplate>

      <asp:ImageButton ID="ImageButton1" runat="server" CausesValidation="False" CommandName="Delete" ImageUrl="~/Images/delete.gif" OnClientClick="javascript:return confirm('Opravdu smazat záznam?');" />

   </ItemTemplate>

</asp:TemplateField>

ButtonField by byl sice vhodnější, ale tvůrci ASP.NET zadání kódu v JavaScriptu pro ButtonField nepodporují. Třída ButtonField nenabízí ani přístup k vlastnostem prvku Button, který je vyrenderován na každém řádku GridView.

Chceme-li napsat svůj vlastní typový sloupec pro mazání záznamů, který budeme používat ve všech svých projektech, použijeme třídu ButtonField alespoň jako předka svého nového sloupce a lehce "dirty" kódem v v přepsané metodě InitializeCell přidáme tlačítku vytvořenému v "našem" sloupci pro každý řádek GridView jednoduchý kód v JavaScriptu, jenž bude vyžadovat od uživatele potvrzení smazání záznamu.

    /// <summary>
    /// Nový typ sloupce pro GridView obsahující tlačítko pro smazání záznamu s potvrzením na klientovi (JS)
    /// </summary>
    public class DeleteButtonField : ButtonField
    {
        #region Private constants
        private const string DEFAULT_CONFIRM_MESSAGE = "Opravdu smazat záznam?";
        private const string DELETE_COMMAND = "Delete";
        private const string DELETE_JS = "javascript: if (!confirm('{0}')) return false;";
        #endregion Private constants

        public DeleteButtonField() : base()
        {

        }

        #region Public properties
        /// <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

        #region Public properties

        /// <summary>
        /// Metoda volaná při incializaci buňky Gridu
        /// </summary>
        /// <param name="cell">Inicializovaná buňka</param>
        /// <param name="cellType">Typ buňky</param>
        /// <param name="rowState">Stav řádku buňky</param>
        /// <param name="rowIndex">Index řádku buňky</param>
        public override void InitializeCell(DataControlFieldCell cell, DataControlCellType cellType, DataControlRowState rowState, int rowIndex)
        {
            base.InitializeCell(cell, cellType, rowState, rowIndex);
            if (cell.Controls.Count > 0)
            {
                IButtonControl control = cell.Controls[0] as IButtonControl;

                if (control != null)
                {
                    control.CommandName = DELETE_COMMAND;
                  
                    LinkButton lb = control as LinkButton;
                    Button btn = control as Button;
                    ImageButton iBtn = control as ImageButton;

                    string fullConfirmMessage = String.Format(DELETE_JS, ConfirmMessage);
                    if (lb != null)
                    {
                        lb.OnClientClick = fullConfirmMessage;
                    }

                    else if (btn != null)
                    {
                        btn.OnClientClick = fullConfirmMessage;
                    }
                    
                    else if (iBtn != null)
                    {
                        iBtn.OnClientClick = fullConfirmMessage;
                    }
                }
            }
        }
        #endregion Public methods
    }

Kód není nijak složitý. Kromě konstant, jejichž význam je dostatečně zřejmý z jejich názvu, máme v našem novém sloupci vlastnost ConfirmMessage, která obsahuje text zobrazovaný v dialogu pro potvrzení smazání záznamu. V metodě InitializeCell nejdříve zavoláme implementaci InitializeCell třídy ButtonField. Poté zjistíme, jestli buňka gridu (cell) obsahuje nějaké ovládací prvky. Jestliže ne, byla metoda volána pro řádek reprezentující záhlaví nebo zápatí gridu, a v ní žádné tlačítko nebude. Když buňka obsahuje alespoň jeden ovládací prvek, zjistíme zda první prvek v kolekci1 je typu IButtonControl (rozhraní IButtonControl implementují třídy LinkButton, Button i ImageButton) a nastavíme jeho vlastnost CommandName na text Delete. Jak asi víte, řetězec "Delete" je jedním z příkazů, které GridView rozeznává a speciálně ošetřuje, takže při bublání událost Click tlačítka s CommandName nastaveným na "Delete" je vyvolána událost RowDeleting (a případně další) místo generické události RowCommand.

Protože rozhraní IButtonControl nenabízí vlastnost OnClientClick pro zadání potvrzujícího kódu v Javascriptu, musíme přetypovat na konkrétní třídu Button a poté teprve můžeme použít vlastnost OnClientClick. Další "dirty" kód :-) - byl bych raději, kdyby byla vlastnost OnClientClick přímo součástí rozhraní IButtonControl, anebo by mohl existovat společný abstraktní předek s vlastností OnClientClick pro třídy ImageButton, LinkButton a Button.

Nový sloupec ve svém projektu použijete takto:

Zaregistrujete nový sloupec na stránce jako běžný serverový ovládací prvek zadáním jména jeho assembly, jmenného prostoru a rezervací prefixu značek pro své prvky v direktivě @Register.

<%@ Register Assembly="Rstein.Web.Ui" Namespace="Rstein.Web.Ui" TagPrefix="custom" %>

Sloupec přidejte do kolekce Columns prvku GridView a nastavte dle svých požadavků vlastnosti sloupce. 

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" OnRowDeleting="GridView1_RowDeleting">

   <Columns>

         <custom:DeleteButtonField Text="Smazat" ButtonType="Button" ConfirmMessage="Smazat záznam?"/>

   </Columns>

</asp:GridView>

Příště si ukážeme, jak podobné sloupce pro Grid udělat lépe s využitím šablon vytvořených v kódu a také doplníme pro své sloupce kompletní podporu pro design time.

 

  1. Zde je ten zmiňovaný "dirty" postup. I když jsem si ověřil Reflectorem, že prvním prvkem kolekce je vždy IButtonControl vytvářený třídou ButtonColumn, jde o typický případ svazování svého kódu s kódem, jehož  změny nemáte pod kontrolou a přitom se nespoléháte jen na jeho "typově bezpečné" veřejné API, takže se v dalších verzích můžete dočkat nepříjemných překvapení  :( 


Friday, 19 May 2006 17:44:04 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  .NET Framework | ASP.NET


 Thursday, 11 May 2006
První zkušenosti s navigačním programem iGO

Program iGO má nejen exotický maďarský původ, ale také jde v poslední verzi o slušně zpracovaný navigační systém, který vás bez větších problémů dovede tam, kam máte namířeno. A hlavně jako u všech dobrých navigací dorazíte do cílového místa, aniž byste předtím museli vytvářet itinerář cesty hledáním v tištěném Autoatlasu nebo internetové mapě, a k rodinné pohodě přispěje i tím, že už nemusíte za jízdy manželce v roli navigátora po několikerém špatném odbočení nervózně a zvýšeným hlasem vysvětlovat, jak se pozná na papírové mapě, jestli máte odbočit vpravo či vlevo. ;-)

I když jsem k novému programu přistupoval s despektem, musím říci, že moje obavy z práce neznámých maďarských programátorů byly pouhé předsudky a samotné iGO se může i přes drobné chybky v kvalitě navigace směle měřit s programy jako jsou Dynavix či TomTom.

Všechny možné grafické a funkční serepetičky navigace oceníte jen a pouze při práci s navigací v křesle domova, protože za jízdy těžko budete obdïvovat krásné textury objektů na dispĺeji. Plech vašeho vozu by totiž mohl být za chvíli ilustrací fyzikálního zákona pojednávajícího o následcích srážky s pohyblivými i statickými objekty v reálném světě. Proto mám na navigační programy tyto tři hlavní požadavky.

  1. Nejdůležitější součástí každého navigačního programu je intuitivní hlasová navigace - ideální hlasová navigace po celou dobu jízdy přichází s tak přesnými instrukcemi, že se uživatel nemusí ani jednou podívat na displej, aby věděl, jak má na křižovatce zabočit. To znamená, že hlasová navigace by dnes již neměla mít jen jednoduché instrukce ve stylu "zabočte vlevo", "zabočte vpravo" nebo "po 150 m zabočte doprava", ale měla by navigovat uživatele například i v křižovatkách, kde po sobě rychle následují dvě odbočky doprava a jasně mu říci, kterou odbočku má zvolit. Při jízdě po hlavní silnici, kdy dodatková tabulka 'opravdový tvar křižovatky' u značky 'Hlavní silnice' sděluje, že hlavní silnice pokračuje doleva či doprava, musí hlasová navigace v dostatečném předstihu sdělit "jeďte po hlavní silnici" či "pokračujte po hlavní silnici - mírně vpravo", abyste se opět nemuseli o dalším směru jízdy ("rovně po vedlejší" nebo "dále po hlavní") rozhodovat podle toho, co je momentálně zobrazeno na displeji. 
  2. Žádný navigační program zatím s ideální hlasovou navigací nepřišel, a proto se občas při složitějších křižovatkách kouknutí na displej PDA nevyhneme. Abychom zorientováním se v informacích na displeji netrávili zbytečně mnoho času, je nutné, aby navigační program zobrazoval všechny podstatné informace, perfektně vykreslil schéma následujího manévru a nastavil měřítko (zoom) mapy tak, abychom ihned dokázali "namapovat" reálnou křižovatku na zobrazení křížovatky na displeji a věděli, jak ji máme projet. Za standard již považuji 3D pohled. Když je 3D pohled dobře naprogramován, je orientace v mapě citelně jednodušší než při méně přirozeném "plochém" 2D pohledu. Přeplácaná grafika orientaci na mapě  stěžuje - čím více zbytečných detailů, které oceníte snad jen v hrách typu Need For Speed, tím pomalejší orientace v mapě. Od podrobností abstrahující a jen schematické zobrazení míst, kterými právě projíždíte, je pro navigaci mnohem přínosnější než všemi barvami a grafickými efekty opentlená mapa. 
  3.  I když dnes již snad každý program trochu alibisticky zobrazuje upozornění, které nás důrazně varuje před používáním programu za jízdy, občas se přeci jen ovládání programu při řízení nevyhneme. Když se rozhodneme někde naobědvat, budeme chtít rychle vyhledat v zájmových bodech (POI) nejbližší restauraci a nechat se k ní navigovat. Je proto důležité, aby menu bylo přehledné a aby ikonky jednotlivých funkcí v něm byly dostatečně velké. Ikonky musí být čitelné na displeji PDA i z větší dálky a celé menu musí být ovladatelné dotykem prstu, jehož průměr bývá u většiny normálně vyvinutých jedinců přeci jen o něco větší než průměr stylu. K bezpečnému ovládání za jízdy také velkou měrou přispívá velikost tlačítek na softwarové klávesnici pro zadávání názvů měst, ulic a filtrování seznamu zobrazených POI.

Jak si poradí iGO s těmito požadavky ?

Hlasová navigace v iGo je na slušné úrovni. Na výběr máte mužský i ženský hlas v češtině, slovenštině a dalších jazycích. Já jsem většinu času jezdil s navigací v anglickém jazyce, protože mi angličtina přišla příjemnější a méně "strojová" než čeština a slovenština.

Místo zmatených hlášek ve městě s mnoha bočními uličkami, které pouze říkají, co máte udělat za 150 m, ale již Vám nesdělí, jak máte těch 150 m v autě změřit, se dovíte, že  máte zabočit vpravo do následující (první, druhé) ulice. Všechna hlášení jsou také vydávána s dostatečným předstihem  - občas si ale říkáte, že po nájezdu na dálnici není nutné ihned vědět, co se stane za 40 Km, a i by podle mě mohl být zredukován počet hlášení v různých vzdálenostech od odbočky.

Při přímém porovnání s TomTomem byla hlasová navigace přesnější v Benešově při nájezdu na most. iGo naviguje "zabočte mírně vlevo a pak ihned vpravo", kdežto TomTom říká pouze "na konci ulice zahněte doprava", což může být pro řidiče, který se ve městě nevyzná, trochu matoucí. Jednak lze konec mostu za konec ulice považovat jen s velkou dávkou fantazie a také oznámení o prvním mírném zabočení doleva lépe vystihuje to, že při přímé jízdě před  začátkem mostu bychom vjeli do ulice označené značkou zákaz vjezdu. Také v Praze, při mírném odbočování vlevo z ulice U Zdravotního ústavu do ulice Ruská , kde TomTom sveřepě mlčí, i když odbočení doleva při jízdě z opačného směru hlásí, iGo přesně naviguje instrukcí, abychom se drželi na hlavní silnici a odbočili mírně vlevo. TomTom měl zase při hlasové navigaci navrch při nájezdu na dálnici u Mirošovic směrem na Prahu  - aby mě dostal na "správnou" stranu dálnice (tedy do směru na Prahu a ne na Brno), přikáže před nájezdem na dálnici "nyní se budete držet vlevo", což vám zabrání sjet do pravého pruhu a vydat se směrem do Brna. IGo pouze sdělí, že máte najet na dálnici. Také hlášení TomToma, že se blíží sjezd z dálnice, přichází oproti iGo mnohem dříve. Záleží na rychlosti auta - při rychlosti 130 Km/h TomTom oznamuje poprvé sjezd cca 2 km před samotným sjezdem, zatímco u iGo se musíte spokojit s prvním upozorněním ve vzdáleností cca 500 metrů před sjezdem. Přesto je iGo TomTomovi v kvalitě hlasové navigace dobrou konkurencí a v leckterých složitějších dopravních situacích ho i předčí. Za špičku hlasové navigace považuji stále Dynavix  - i v centru Prahy to byla opravdu jediná navigace, která mě dokázala spolehlivě vést jen hlasovými instrukcemi. TomToma i iGO bych dal při hodnocení kvality hlasové navigace společně na druhé místo ihned za Dynavix.

Grafika iGO je vynikající a při prvním pohledu TomToma předčí. Vlaječky označující počátek a konec trasy jsou krásně animované, názvy ulic ve městech perfektně čitelné a vždy české a ne ceske.  Přesto se neorientuji v 2D ani 3D pohledu tak dobře jako u TomToma, ale to může být dáno tím, že iGo používám zatím velmi krátkou dobu. Schéma následujícího manévru v levé části obrazovky a automatický zoom při příjezdu na křižovatku jsou zpracovány dobře a pokud nemáte s čím srovnávat, budete zcela spokojeni. 

Mapové podklady pro Českou republiku mají oba programy od společnosti TeleAtlas a jejich podrobnost je skvělá. U iGo ale nemáte takové množství POI, které existují pro TomToma. Nepříjemně mě překvapilo, že v obchodních centrech zcela absentuje třeba Hypernova Průhonice. Také zamrzí, že nemůžete z nejbližších POI vyfiltrovat jen ty, které leží přímo na trase.

Ovládání menu iGo "jedním prstem" sice možné je, ale při přímém srovnání s ergonomií ovládání TomToma jsou patrné zásadní nedostatky a nedomyšlenosti. Do dalších úrovní menu se dostáváte složitě a také mi moc nevyhovuje, že při hledání místa je nejdříve zobrazen formulář pro vyhledání ulic ve městě, které jste zadali při posledním hledání. Teprve po volbě změnit město se zobrazí města hledaná dříve a když chcete zadat město, které není v historii hledaných měst, musíte volbou 'Jiné město' přejít na obrazovku, kde zadáte několik počátečních písmen z názvu města. Program iGO na formuláři pro zadání názvu města ukazuje jen počet měst vyhovujících zadávaným písmenům, ale samotná města uvidíte až po stisknutí tlačítka Hotovo. Celý postup mi přijde nesmyslně zdlouhavý a krkolomný. Navíc mi tlačítka softwarové  klávesnice připadají příliš titěrná a to i po jejich zvětšení stisknutím tlačítka Klávesy.

Také první a graficky netradiční rozcestník hlavního menu volbou barev a písma nechtěně vizuálně potlačuje centrální a nejdůležitější položku menu s názvem "Vyhledat a jet". Co naopak u iGo chválím je plánování složitějších cest přes více míst ("lomových bodů"). 

V ergonomii ovládání TomTom jasně vítězí a iGo bude muset hodně zapracovat na odstranění zmíněných nedostatků.

Závěrečné postřehy:

  • U iGo stejně jako u TomToma bych vyzdvihl bezproblémovou konfiguraci a práci s GPS. (BT GPS Holux 230 - spárována s MDA Vario). 
  • Program s celou mapou Evropy se vejde na jednu 1GB SD kartu. Program iGo se prodává na SD kartě a k jeho instalaci dojde po zasunutí SD karty do slotu. Jak asi tušíte, na kartě je adresář s "wellknown" názvem 2577, ze kterého Windows Mobile po zasunutí karty vždy automaticky spustí  soubor nazvaný autorun.exe.
  • Zadání zeměpisných souřadnic.
  • Upozornění na překročení zadané rychlosti.
  • ""Ladění" algoritmu pro výpočet trasy - volba mezi nejrychlejší, nejkratší a ekonomickou trasou. "Profily" trasy pro vozidla, kola, chodce. Možnost zakázat dálnice nebo jen placené dálnice a také třeba nezpevněné cesty.
  • Zakázání obratů do protisměru. Tato funkce ale podle mě nefunguje správně - i když jsem obraty do protisměru zakázal, přesto po mě iGo párkrát při odchýlení se od stanovené trasy obrat do protisměru vyžadoval.
  • Možnost zakázat při výpočtu vnitrostátní trasy cestu přes hranice s okolními státy.
  • Automatické přepínání mezi denním a nočním barevným profilem.

Obrázková galerie iGo:

Rychlý přístup k některým položkám menu   Hlavní menu  Menu v mapě  Režim navigace  Seznam POI  Body zájmu (POI) - kina   Nastavení parametrů trasy   Mapa  - ulice Vinohradská, Praha

Thursday, 11 May 2006 15:41:31 (Central Europe Standard Time, UTC+01:00)       
Comments [3]  Mobilitky | Navigace


 Monday, 01 May 2006
Odstranění problému(ů) s instalací Compact .NET Frameworku 2.0 na Windows Mobile 5.0

Již několikrát jsem se setkal s tím, že na zařízení s Windows Mobile 5.0 byl problém nainstalovat Compact .Net Framework. Pár lidí si mi teď v poslední době v mailu nebo na ICQ také stěžovalo, že jim CNF nejde nainstalovat a že při pátrání po příčinách potíží dostanou nanejvýš jen hlášku, že došlo k problému a že by bylo dobré resetovat zařízení a zkusit instalaci znovu. ;-) Tomu říkám skvělé design guidelines v praxi - "nikdy neobtěžujte uživatele zbytečnými technickými podrobnostmi". :-D

Odstranění problému není složité - z důvodu známému asi jen vývojářskému týmu CNF v Microsoftu a mystérióznímu pro běžného smrtelníka ActiveSync při instalaci CNF z počítače přes Application Manager zkopíruje do zařízení soubor nazvaný NETCFv2.wce5.armv4i.cab místo správného NETCFv2.wm.armv4i.cab. NETCFv2.wm.armv4i.cab patří na všechna současná zařízení používající procesor s ARM V4 kompatibilní instrukční sadou.

Takže selže-li vám instalace, najděte na počítači soubor NETCFv2.wm.armv4i.cab, a zkopírujte jej "ručně" (v Exploreru nebo třeba v v Total Commanderu s pluginem pro CE zařízení)  do PDA. V PDA pak jen cab soubor spusťte a VŽDY zvolte instalaci CNF do zařízení - instalace na kartu (SD, CF) je sice možná, ale při prvním použití CNF dojde stejně k nakopírování všech knihoven do storage v zařízení.

Pokud máte stále problémy s instalací:

  1. Resetujte (soft-reset) zařízení a zkuste nainstalovat CNF znovu. Teď už má reset smysl. ;-)
  2. Nezabere-li ani to, vypněte po dobu instalace všechny Today pluginy, restartujte (soft reset) a znovu instalujte.

Přinejhorším bod 2 zabere podle mých zkušeností vždy - ještě se mi nestalo, že bych na nějaké PDA s WM 5 CNF 2 nenainstaloval ;-)

 



Monday, 01 May 2006 21:50:26 (Central Europe Standard Time, UTC+01:00)       
Comments [0]  Compact .Net Framework | Mobilitky


 Thursday, 27 April 2006
Řešení hádanky z 18-.4. - vztahy Include, Extend v případech užití

Protože kolegové v diskuzi pod původním spotem podrobně rozebrali všechny aspekty vztahu Include a Extend v případech užití,  je tento můj spot z větší části sumarizující. V čem tedy byl "chyták"? Cílem hádanky bylo upozornit na to, že vztahy Include a Extend nemusejí být tak jednoduše rozpoznatelné, jak se dočtete v různých rychlopříručkách,  a také jsem chtěl, abyste si všimli, že vztah Extend bylo bylo příjemné zpřesnit tím, co sám pro sebe nazývám "anonymní datové sloty".

Tak toto byl začátek dvoustránkového spotu, který se mi podařilo omylem smazat. :-( Protože už se mi nechce teď psát celý spot znovu, tak jen v krátkosti zrekapituluji, čeho se hádanka týkala.

Specifikace UML popisuje vztahy Include a Extend takto:

"Include is a directed relationship between two use cases, implying that the behavior of the included use case is inserted into the behavior of the including use case. The including use case may only depend on the result (value) of the included use case. This value is obtained as a result of the execution of the included use case."

"This relationship specifies that the behavior of a use case may be extended by the behavior of another (usually supplementary) use case. The extension takes place at one or more specific extension points defined in the extended use case. Note, however, that the extended use case is defined independently of the extending use case and is meaningful independently of the extending use case. On the other hand, the extending use case typically defines behavior that may not necessarily be meaningful by itself. Instead, the extending use case defines a set of modular behavior increments that augment an execution of the extended use case under specific conditions. Note that the same extending use case can extend more than one use case. Furthermore, an extending use case may itself be extended."

 

V hádance nemohl být vztah mezi případem užití 'Založení objednávky' a 'Platba kartou' Include ani Extend. Include nelze použít, protože máme i jiné typy plateb, a případ užití Platbu kartou tedy nelze vložit povinně (dalším typem platby bude třeba dobírka - žádné záludnosti ohledně plateb jsem přichystány neměl, jen jsem chtěl, abyste se jako správní analytici pídili po podrobném zadání :-) ). Nejedná se ale ani o vztah Extend, protože rozšířený případ užití nijak nezávisí na rozšiřujícím případu užití a nemůže číst žádné jeho "návratové hodnoty"  - pro nás to znamená, že nezjistíme, jestli platba prošla.

Řešením je vytvoření nového abstraktního případu užití nazvaného třeba 'Kontrola platby', který přes vazbu Include provážeme s případem užití 'Založení objednávky' . Konkrétními potomky budou "Kontrola platby kartou", "Kontrola ostatních typů plateb". Vazba Include nám dovoluje číst návratovou hodnotu vloženého případu užití. V alternativním toku událostí případu užití  'Založení objednávky' podle vybraného typu platby vložíme konkrétní případ užití a dle výsledku vkládaného případu užití dokončíme hlavní tok událostí. Když neprojde platba kartou, můžeme zákazníkovi nabídnout výběr jiného typu platby.

Hlavně ale platí - celá hádanka i její řešení je jen hříčka bez užitku pro zákazníka, která demostruje, jak lze i méně časté vztahy řešit běžnými výrazovými prostředky jazyka UML. Stále platí, že vazby Include, Extend ani gen-spec zákazníkovi raději moc neukazujte, jestliže  nechcete zbytečně trávit čas vysvětlováním vztahů, které jsou pro projekt málo podstatné a pro zákazníka samotného většinou jen iritující.



Thursday, 27 April 2006 18:25:16 (Central Europe Standard Time, UTC+01:00)       
Comments [13]  Programátorské hádanky | UML