WebMasterSam

.Net, SEO, Dynamics CRM, AdSense/AdWords, Dating sites, Silverlight, Web hosting and more
PPC management, Online Marketing, Search Marketing, SEO, Website development - dotmedias.com

About me

I'm an IT consultant working primarily with the .Net Framework as a developper and architect. I also work on my own on my personnal dating websites. I've been developping websites since 2000.

If you like what I do, feel free to support me

PayPal - The safer, easier way to pay online!

Bookmark

Bookmark and Share

Sponsored links

Amazon hot deals

Computer releases

Last comments

None

Complete .Net COM+ interop class (COMAdmin wrapper)

If you already worked with the Interop.COMAdmin, you certainly said a lot of bad bad words ! Don't worry, you're not alone. Recently a friend of mine has to create an application that can export security from COM+ and then reimport it on another server. Because I love to do very technical code I decided to write a complete COM+ interop class that wraps the Interop.COMAdmin one. By doing this, my friend had a clean interop do deal with and so the tool to export security has been realized very quickly.

I decided to write a really clean interop so I spent about 3 days of work just to write this interop because no one on the internet has took the time to create it ! I want to share it with you because it can make you save a looooooooooot of time and frustration. You can download the code right here : ComPlusInterop.zip (31.45 kb).

The architecture behind that interop is as follow; there is a base class, COMElementBase, that wraps the access to the COMObject and there is derived classes matching the different things in COM+ such a applications, components, roles, methods and interfaces. Here is a list of the different classes the interop has :

  • ComponentServices
    • Represents the COM+ instance (can be from a remote server)
  • COMElementBase
    • The base class for the following classes
  • COMApplication
    • Represents an application
  • COMComponent
    • Represents a component (DLL)
  • COMInterface
    • Represents an interface of a component
  • COMMethod
    • Represents a method of an interface
  • COMRole
    • Represents a role definition
Because everything in COM+ is retrieved by string, I have a lot of constant values in each different class; each constant matches the internal property name. To retrieve a property value or to set one, the derived class (such as COMApplication) calls an internal method of the COMElementBase class. To simplify the process of casting property values, I created several methods in the base class to retrieve property values like GetPropertyValueBoolean, GetPropertyValueInteger and so on...

To show you in code what it looks like, here's the COMComponent class :


using Interop.COMAdmin;

using System.Runtime.InteropServices;

public class COMComponent : COMElementBase, IComparable
{
    #region "Members"

        private ReadOnlyCollection<COMInterface> _lstInterfaces;
        private ReadOnlyCollection<string> _lstRoles;
        private COMApplication _parent = null;

        private COMAdminCatalogCollection _colInterfaces = null;
        private COMAdminCatalogCollection _colRoles = null;

        private bool _dispose = false;

    #endregion

    #region "Constantss"

        internal const string NUMBER_ERROR_VERSION_INVALID = "0x8011042C";

        internal const string KEY_COLLECTION_INTERFACE = "InterfacesForComponent";
        internal const string KEY_COLLECTION_ROLES = "RolesForComponent";

        internal const string PROPERTY_NAME = "ProgID";
        internal const string PROPERTY_GUID = "CLSID";
        internal const string PROPERTY_DESCRIPTION = "Description";
        internal const string PROPERTY_DLL = "DLL";
        internal const string PROPERTY_MAJOR_VERSION = "VersionMajor";
        internal const string PROPERTY_MINOR_VERSION = "VersionMinor";
        internal const string PROPERTY_BUILD_VERSION = "VersionBuild";
        internal const string PROPERTY_SUBBUILD_VERSION = "VersionSubBuild";
        internal const string PROPERTY_TRANSACTION = "Transaction";
        internal const string PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED = "ComponentTransactionTimeoutEnabled";
        internal const string PROPERTY_COMPONENT_TRANSACTION_TIMEOUT = "ComponentTransactionTimeout";
        internal const string PROPERTY_ACCESS_CHECKS_ENABLED = "ComponentAccessChecksEnabled";
        internal const string PROPERTY_POOLING_ENABLED = "ObjectPoolingEnabled";
        internal const string PROPERTY_MAX_POOL_SIZE = "MaxPoolSize";
        internal const string PROPERTY_MIN_POOL_SIZE = "MinPoolSize";
        internal const string PROPERTY_CREATION_TIMEOUT = "CreationTimeout";
        internal const string PROPERTY_CONSTRUCTION_ENABLED = "ConstructionEnabled";
        internal const string PROPERTY_CONSTRUCTION_STRING = "ConstructorString";
        internal const string PROPERTY_JIT_ACTIVATION = "JustInTimeActivation";
        internal const string PROPERTY_EVENT_TRACKING_ENABLED = "EventTrackingEnabled";
        internal const string PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT = "MustRunInClientContext";
        internal const string PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT = "MustRunInDefaultContext";
        internal const string PROPERTY_IS_PRIVATE = "IsPrivateComponent";
        internal const string PROPERTY_SYNCHRONIZATION = "Synchronization";
        internal const string PROPERTY_THREADING_MODEL = "ThreadingModel";
        internal const string PROPERTY_EXCEPTION_CLASS = "ExceptionClass";

    #endregion

    #region "CTOR"

        internal COMComponent(COMAdminCatalogObject COMobject, COMAdminCatalogCollection collection, COMApplication parent)
            : base(COMobject, collection)
        {


            _parent = parent;
        }

        protected override void Dispose(bool disposing)
        {

            base.Dispose(disposing);

            if (!_dispose)
            {

                if (_colInterfaces != null)
                {
                    Marshal.ReleaseComObject(_colInterfaces);
                }

                if (_colRoles != null)
                {
                    Marshal.ReleaseComObject(_colRoles);

                }
            }


            _dispose = true;
        }

    #endregion

    #region "Properties"

        public ReadOnlyCollection<COMInterface> Interfaces
        {
            get
            {

                if (_lstInterfaces == null)
                {
                    RetrieveInterfaces();
                }


                return _lstInterfaces;
            }
        }

        public ReadOnlyCollection<string> RolesCoches
        {
            get
            {

                if (_lstRoles == null)
                {
                    RetrieveRoles();
                }


                return _lstRoles;
            }
        }

        public string Name
        {
            get { return base.GetPropertyValueString(PROPERTY_NAME); }
        }

        public Guid ID
        {
            get { return base.GetPropertyValueGUID(PROPERTY_GUID); }
        }

        public string DLLPath
        {
            get { return base.GetPropertyValueString(PROPERTY_DLL); }
        }

        public string Version
        {
            get { return string.Format("{0}.{1}.{2}.{3}", base.GetPropertyValueInteger(PROPERTY_MAJOR_VERSION), base.GetPropertyValueInteger(PROPERTY_MINOR_VERSION), base.GetPropertyValueInteger(PROPERTY_BUILD_VERSION), base.GetPropertyValueInteger(PROPERTY_SUBBUILD_VERSION)); }
        }

        public string Description
        {
            get { return base.GetPropertyValueString(PROPERTY_DESCRIPTION); }
            set { base.SetPropertyValue(PROPERTY_DESCRIPTION, value); }
        }
        public bool ComponentTransactionTimeoutEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED, value); }
        }

        public int ComponentTransactionTimeout
        {
            get { return base.GetPropertyValueInteger(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT); }
            set
            {
                if (value < 0 || value > 3600)
                {
                    throw new OverflowException("La valeur doit être comprise entre 0 et 3600.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT, value);
                }
            }
        }

        public bool AccessChecksEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_ACCESS_CHECKS_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_ACCESS_CHECKS_ENABLED, value); }
        }

        public bool PoolingEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_POOLING_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_POOLING_ENABLED, value); }
        }

        public int MaxPoolSize
        {
            get { return base.GetPropertyValueInteger(PROPERTY_MAX_POOL_SIZE); }
            set
            {
                if (value < 1 || value > 1048576)
                {
                    throw new OverflowException("The value must be between 1 and 1048576.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_MAX_POOL_SIZE, value);
                }
            }
        }

        public int MinPoolSize
        {
            get { return base.GetPropertyValueInteger(PROPERTY_MIN_POOL_SIZE); }
            set
            {
                if (value < 0 || value > 1048576)
                {
                    throw new OverflowException("The value must be between 0 and 1048576.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_MIN_POOL_SIZE, value);
                }
            }
        }

        public int CreationTimeout
        {
            get { return base.GetPropertyValueInteger(PROPERTY_CREATION_TIMEOUT); }
            set
            {
                if (value < 0 || value > 2147483647)
                {
                    throw new OverflowException("The value must be between 0 and 2147483647.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_CREATION_TIMEOUT, value);
                }
            }
        }

        public bool ConstructionEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_CONSTRUCTION_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_CONSTRUCTION_ENABLED, value); }
        }

        public string ConstructionString
        {
            get { return base.GetPropertyValueString(PROPERTY_CONSTRUCTION_STRING); }
            set { base.SetPropertyValue(PROPERTY_CONSTRUCTION_STRING, value); }
        }

        public bool JITActivation
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_JIT_ACTIVATION); }
            set { base.SetPropertyValue(PROPERTY_JIT_ACTIVATION, value); }
        }

        public bool EventTrackingEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_EVENT_TRACKING_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_EVENT_TRACKING_ENABLED, value); }
        }

        public bool MustRunInClientContext
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT); }
            set { base.SetPropertyValue(PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT, value); }
        }

        public bool MustRunInDefaultContext
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT); }
            set { base.SetPropertyValue(PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT, value); }
        }

        public bool IsPrivate
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_IS_PRIVATE); }
            set { base.SetPropertyValue(PROPERTY_IS_PRIVATE, value); }
        }

    #endregion

    #region "Public methods"

        public void AssociateRole(string name)
        {

            if (_colRoles == null)
            {
                RetrieveRoles();
            }

            if (!_colRoles.AddEnabled)
            {
                throw new NotSupportedException("Associating role is not supported.");
            }

            if (this.Parent.IsSystem)
            {
                throw new NotSupportedException("Cannot associate role in a system application.");
            }

            if (!this.Parent.Changeable)
            {
                throw new NotSupportedException("The application does not support modifications.");
            }

            if (RoleAssociated(name))
            {
                throw new DuplicateNameException(string.Format("The role '{0}' is already associated with this component.", name));
            }

            COMAdminCatalogObject role = (COMAdminCatalogObject)_colRoles.Add();

            role.Value(COMRole.PROPERTY_NAME) = (object)name;

            _colRoles.SaveChanges();


            RetrieveRoles();
        }

        public void DissociateRole(string name)
        {

            if (_colRoles == null)
            {
                RetrieveRoles();
            }

            if (!_colRoles.AddEnabled)
            {
                throw new NotSupportedException("Dissociating role is not supported.");
            }

            if (this.Parent.IsSystem)
            {
                throw new NotSupportedException("Cannot dissociate role in a system application.");
            }

            if (!this.Parent.Changeable)
            {
                throw new NotSupportedException("The application does not support modifications.");
            }

            if (!RoleAssociated(name))
            {
                throw new KeyNotFoundException(string.Format("The role '{0}' is not associated with this component.", name));
            }

            for (int i = 0; i <= _colRoles.Count - 1; i++)
            {
                if (((string)((COMAdminCatalogObject)_colRoles.Item(i)).Name).ToLower() == name.ToLower())
                {
                    _colRoles.Remove(i);
                    break;
                }
            }

            _colRoles.SaveChanges();

            RetrieveRoles();
        }

        public void DissociateRoles()
        {

            for (int i = this.RolesCoches.Count - 1; i >= 0; i += -1)
            {
                DissociateRole(this.RolesCoches(i));

            }
        }

        public bool RoleAssociated(string name)
        {

            bool found = false;

            for (int i = 0; i <= this.RolesCoches.Count - 1; i++)
            {
                if (this.RolesCoches(i).ToLower() == name.ToLower())
                {
                    found = true;
                    break;
                }
            }


            return found;
        }

        public bool InterfaceExists(string name)
        {

            bool found = false;

            for (int i = 0; i <= this.Interfaces.Count - 1; i++)
            {
                if (this.Interfaces(i).Name.ToLower() == name.ToLower())
                {
                    found = true;
                    break;
                }
            }


            return found;
        }

        public COMInterface RetrieveInterface(string name)
        {

            COMInterface interfaceFound = null;

            for (int i = 0; i <= this.Interfaces.Count - 1; i++)
            {
                if (this.Interfaces(i).Name.ToLower() == name.ToLower())
                {
                    interfaceFound = this.Interfaces(i);
                    break;
                }
            }

            if (interfaceFound == null)
            {
                throw new KeyNotFoundException(string.Format("The interface '{0}' was not found.", name));
            }


            return interfaceFound;
        }

        public void Refresh()
        {

            RetrieveInterfaces();

            RetrieveRoles();
        }

        public int CompareTo(object obj)
    {


        return this.Name.CompareTo(((COMComponent)obj).Name);
    }

    #endregion

    #region "Private methods"

        private void RetrieveInterfaces()
        {

            _lstInterfaces = new ReadOnlyCollection<COMInterface>();

            _colInterfaces = base.GetCollection(KEY_COLLECTION_INTERFACE);

            for (int i = 0; i <= _colInterfaces.Count - 1; i++)
            {
                _lstInterfaces.Add(new COMInterface((COMAdminCatalogObject)_colInterfaces.Item(i), _colInterfaces, this));
            }


            _lstInterfaces.Sort();
        }

        private void RetrieveRoles()
    {

        _lstRoles = new ReadOnlyCollection<string>();

        _colRoles = base.GetCollection(KEY_COLLECTION_ROLES);

        for (int i = 0; i <= _colRoles.Count - 1; i++)
        {
            _lstRoles.Add((string)((COMAdminCatalogObject)_colRoles.Item(i)).Name);
        }


        _lstRoles.Sort();
    }

    #endregion
}

Once again, if you want to download the code, you can get it here : ComPlusInterop.zip (31.45 kb).
If you want a complete COM+ programming reference, you can check this MSDN article. This is where I got all the string needed to retrieve property values from COMAdmin.
Enjoy !

Microsoft Dynamics CRM 4.0 tabSet list

As I explained in another article, when you want to retrieve to name of the entity displayed in a CRM page it's easy to check for the etc parameter and then translate the number to a name by calling the MetaData service. But, as I also said, when you check for the page "areas.aspx", you don't have the entity number of the related entity... instead you get a tabSet parameter. The tabSet contains either the relationship name or a tabSet name...

Because the same related entity can have multiple tabSet names, I decided to list everything I found to simplify the job for you (I all got those using Fiddler and clicking on every link of related entities in CRM...).

Area name Related entity name
areaActivities ActivityPointer
areaActivityHistory ActivityPointer
areaAsyncOperations WFProcess
areaRelationships CustomerRelationship
areaRelationship CustomerRelationship
areaContacts Contact
areaSubConts Contact
areaSubAccts Account
areaAddresses CustomerAddress
areaOpps Opportunity
areaOpportunities Opportunity
areaQuotes Quote
areaOrders SalesOrder
areaInvoices Invoice
areaService Incident
areaCases Incident
areaContract Contract
areaListsInSFA List
areaCampaignsInSFA Campaign
areaProducts Product
areaSubs Product
areaComp Competitor
areaComps Competitor
areaSalesLit SalesLiterature
areaItems ProductPriceLevel
areaContractLines ContractDetail
areaRoles Role
areaTeams Team
areaServices Service
areaResourceGroup ResourceGroup
areaResourceGroups ResourceGroup
areaUsers SystemUser
areaBiz BusinessUnit
areaResource Resource
areaUnits UoM
areaPrices ProductPriceLevel
areaMembers SystemUser
areaExistingProducts QuoteDetail
SalesOrder
Invoice
areaWriteInProducts QuoteDetail
SalesOrder
Invoice

All this may save you some hours of clicking !

Posted: Apr 27 2009, 13:46 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | CRM 4.0 | Programming
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Retrieving the entity name or number from a CRM page URL

Recently I had to create an HttpModule that catch every call to a CRM page and inject some JavaScript code in it. The first the HttpModule does is to retrieve to entity name (or number) and instanciates a class that contains some information about the page (organization, page url, entity name, entity number, etc...). Normally it is easy, you can get the etc querystring parameter to get the number and then call the MetaData service to get the entity name. Sometimes the querystring parameter gets another name... it's not always etc: it can be "etc", "oType" or "iObjType".

Life will be too easy if everytime when want to get the entity number we just have to check for the querystring parameter... so Microsoft though about a more challenging way to get the entity number. You will face that if you catch the "areas.aspx" page. This page is the one that is called when you click a left navigation bar item (in an entity record).

Let's check some different URLs you can get in CRM :

For the entity form...

...of CRM entities

 - http://crmserver/org/sfa/accts/edit.aspx (Account)
 - http://crmserver/org/sfa/conts/edit.aspx (Contact)
 - http://crmserver/org/activities/email/edit.aspx (Email activity)

...of user-defined entities

 - http://crmserver/org/userdefined/edit.aspx?etc=#entity_number#

These looks very easy to understand and it's easy to retrieve the entity numer or name. You will have to hard-code the entity number or name in your code when you get CRM entities because the URL (page) tells which entity it is. For the user-defined, you simply get the "etc" parameter in the QueryString.

For the entity record list...

...of the activites, campains and cases

 - http://crmserver/org/workplace/home_activities.aspx
 - http://crmserver/org/ma/home_camps.aspx
 - http://crmserver/org/cs/home_cases.aspx

...of anything else

 - http://crmserver/org/_root/homepage.aspx?etc=#entity_number#

These looks also very easy to understand but you will notice that barely everything (including user-defined entities) are shown in the same page (homepage.aspx) so you will not have to hard-code a lot of thing. And now, for the entity relationship view (the one you get on the right side of the CRM entity form when you click on a left navigation bar item of the entity form)...

...of CRM entities

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=areaContacts (Account showing related contacts)

...of user-defined entities (one-to-many relationship)

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=account_new_myentityname (Account showing related new_myentityname)

...of user-defined entities (many-to-many relationship)

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=areaaccount_new_myentityname (Account showing related new_myentityname)

This not sound very cool... the same querystring parameter (tabSet) can contains 3 different types of value... depending on whether the related entity is custom or built-in and if it is a one-to-many or many-to-many relationship... !

More horrible than this, for the same related entity (shown in different parent entity), the tabSet parameter will not be named the same way (but sometimes it is...). For example, if the related entity is a contact, the tabSet can be named "areaContacts" or "areaSubConts". Wow ! I don't have to tell you it has to be a lot hard-coded !

For the user-defined entities it is a lot more simple than this; the value in the tabSet is the relationship name. This is cool because you can query the CRM MetaData service and get both entities of the relationship. There's only a little twist with this... when you have a many-to-many relationship, the tabSet starts with "area"... I don't know why but MS decided to do it this way so we have to deal with it.

Because the tabSet parameter is a pain, I decided to write an article listing all the different tabSet values I've found in hour of clicking in CRM (because I didn't find it on the web).

Posted: Apr 27 2009, 12:55 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | CRM 4.0 | Programming
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

How to pass Culture and UI-Culture to Silverlight

Globalization is a common thing when you create websites that targets many different cultures (country and languages), so localizing your interfaces is a must.

If you already know how to pass parameters to a Silverlight application with the InitParams, you can think of passing your culture via those parameters and then set the culture at runtime. It will work, there's no doubt about it, but when a functionnality is supported by a specific technology, you must it the way it's used to be; not create a parallel functionnality.

So here's what you will have to add to you Silverlight HTML object code :


<param name="culture" value="fr-CA" /><param name="uiculture" value="fr-CA" />

It's easy and clean !

Posted: Apr 26 2009, 18:13 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Programming | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Multiline TextBox in Silverlight 2.0

If, like me, you create some Silverlight applications, you probably said the same thing I said today : "Where the hell is the TextMode property of the TextBox ?!". In Silverlight it does not exist... so you can't set it to "Multiline" when you want a "TextArea" so you can type the Enter key so it adds a carriage return to the content of the TextBox.

The property's not completely gone... but the new property you get only gives you the chance to accept EnterKey or not; it's name is AcceptReturn. Use it like this :


<TextBox AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" />

Posted: Apr 25 2009, 14:42 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Programming | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Easily pass parameters to Silverlight (and retrieve it everywhere in your app)

 

When you want to pass values (parameters) to a Silverlight application from within a web page, you can use the "initParams" fixed parameter. By default it does not contain anything. If you want to use it you must declare it this way :

You will notice that all the params must be passed within a single comma-separated string. Silverlight works this way. Because it works this way, you don't have to manually parse that string to get parameters and values, Silverlight parses it for you. If you want to retrieve a value of a parameter you passed, you can check on the dictionnary of parameters. The dictionnary is available on the StartupEventArgs of the Application_Startup event.

Because I wanted my parameters to be available everywhere and aasily parseable to the right type I created this simple helper class that wraps all the logic behind initParams.


    public class StartupParameters
    {
        #region Members

            private static IDictionary<string, string> _initParams = null;

        #endregion

        #region Methods

            public static void Initialize(IDictionary<string, string> initParams)
            {
                _initParams = initParams;
            }

            public static string GetParameter(string name)
            {
                if (ParameterExists(name))
                    return _initParams[name];
                else
                    return string.Empty;
            }

            public static bool GetParameterBoolean(string name)
            {
                bool blnResult = false;

                if (bool.TryParse(GetParameter(name), out blnResult))
                    return blnResult;
                else
                    return false;
            }

            public static int GetParameterInteger(string name)
            {
                int intResult = 0;

                if (int.TryParse(GetParameter(name), out intResult))
                    return intResult;
                else
                    return 0;
            }

            public static long GetParameterLong(string name)
            {
                long lngResult = 0;

                if (long.TryParse(GetParameter(name), out lngResult))
                    return lngResult;
                else
                    return 0;
            }

            public static bool ParameterExists(string nom)
            {
                return _initParams.ContainsKey(nom);
            }

        #endregion
    }

The only thing you have to do to properly use this class is to initialize it using the Initialize method receiving the initParams dictionnary, just like this :


     private void Application_Startup(object sender, StartupEventArgs e)
     {
           StartupParameters.Initialize(e.InitParams);

           this.RootVisual = new SilverlightControl1();
     }

Now you always have your parameters with you wherever you are in your Silverlight application.

Posted: Apr 25 2009, 14:03 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Silverlight trace; use this simple tracing class

For those who work with Silverlight already knows that Silverlight runs in a sandbox so your disconnected from all the local resources on the client-side, except something called IsolatedStorage. This is what I used to create my very own tracing class. It is only a small tracing class but it does all you want. Feel free to customize it if you need more functionnalities.


    public class Trace
    {
        private static IsolatedStorageFile _storageFile = null;
        private static IsolatedStorageFileStream _storageFileStream = null;
        private static StreamWriter _streamWriter = null;

        static Trace()
        {
            _storageFile = IsolatedStorageFile.GetUserStoreForApplication();
            _storageFileStream = _storageFile.OpenFile("Trace.log", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
            _streamWriter = new StreamWriter(_storageFileStream);
            _streamWriter.AutoFlush = true;
        }

        ~Trace()
        {
            _storageFileStream.Close();
        }

        public static void Write(String message)
        {
            _streamWriter.WriteLine(message);
        }
    }

Posted: Apr 21 2009, 14:46 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Asynchronous UI update in Silverlight; here comes the Dispatcher

For those who are familiar with multi-thread developpment already know that cross-thread calls are not allowed. Silverlight is no exception. When you have some code that will execute asynchronously, like an event handler or a timer tick, you will have to involve something called the "Dispatcher". The Dispatcher  is here to help you update the UI asynchronously.

You don't have to use it everytime you want to execute some asynchronous code; you only have to use this when you want to update the UI. The Dispatcher is very easy to use and understand. Because the Dispatcher can handle anonymous delegates, you can do something like this in all you event handlers :


Dispatcher.BeginInvoke(delegate()
{
    // Your UI updating code goes here
});

That's really simple, and it works well.

Posted: Apr 20 2009, 16:43 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Display a MessageBox in Silverlight

 

For people who tries to find the "MessageBox" class in Silverlight to display a message in a pop-up like we do in WinForms will have difficulty to find it because this doesn't work the same way. Because Silverlight runs in the browser, the message box is the one that is available in JavaScript: the "alert" method.


System.Windows.Browser.HtmlPage.Window.Alert("Your message goes here");

In the Window object (System.Windows.Browser.HtmlPage.Window) you will find some other things like "Prompt", "Navigate", "AttachEvent" and "Confirm". It shows how integrated with the browser and the JavaScript code Silverlight is, and this is a real good thing because you can easily make those 2 technologies talk together.

 

Posted: Apr 20 2009, 16:33 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | JavaScript | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Programming best practices; you should always follow them

As you know, I'm a .Net developper and I love to develop softwares and websites. I love when I do something all by myself because everything is done the same way and the code is clear and readable. When I have to review other's code or add something in an existing project I always have difficulties with it because each programmer has it's own way to code, and a lot of programmers don't know how to code. Sometimes I look at some code and think "How can someone thinks about the problem and solves this that way. How can he read his code to add something in it... ?". I think it's time to give you some general rules you have to follow whatever your programming language is.

Never comment you code, except methods signatures

This is a simple and clear rule. You code HAS to be self-explaining. Name you variables well, avoid abreviations, avoid generic names, etc... Let's take a look at this piece of code :

Bad code


    public WeatherForcastData Func()
    {
        /* Declare the username used to connect to the webservice */
        string name = "WebMasterSam";

        /* Declare the variable that will contain the password
        Here we put it in clear text but normaly we don't */

        string pass = "Pass@word1";

        /* Instanciate the webservice so we can call the WebMethod
        to get the weather */

        WebService service = new WebService();

        /* Assign the credentials to the service so we don't get
        an error  */

        service.Credentials = new NetwrokCredentials(name, pass);

        /* Call the WebMethod to retrieve weather forcast */
        return service.GetWeatherForcastData("CITY");

    }

Good code


    /// <summary>
    /// Get the weather forcast for a specific city by calling the WeatherForcast WebService.
    /// </summary>
    /// <remarks>We have to set the credentials to call the WebService because it's not a public free service.</remarks>
    public WeatherForcastData GetWeatherForcast()
    {
        string userName = "WebMasterSam";
        string password = "Pass@word1";
        string cityName = "CITY";
        WebService service = new WebService();

        service.Credentials = new NetwrokCredentials(userName, password);

        return service.GetWeatherForcastData(cityName);
    }

Adding a lot of comments in the code makes it really heavy and hard to read. Keep your comments in the method signature. Method signature is used by intellisense, so when you call a method you see the information in a yellow pop-up.

In functions, always have a single exit point

This is sometimes simpler to add "return" statements everywhere in the code if you, for example, only validate data and want to return False if the validation fails. When you do this, and someones passes after you to add something, he will not necessarily see all your return statements and he can miss one. It might not appear in his little tests but appear later when you send your application for approbation by the client, or even worse, in production.

When you have a function, your last line of code should be the return statement. Take a look a this piece of code :

Bad code


    public bool ValidateInput(string param1, string param2, int param3, int param4)
    {
        if (param1.Length == 0)
            return false;
        else
        {
            if (param1.Length > 100)
                return false;

            if (param2 == "NONE")
                return false;
            else
            {
                if (param3 < 0 || param3 > 100)
                    return false;
                if (param4 == 0)
                    return false;
            }
        }

        return true;
    }

Good code


    public bool ValidateInput(string param1, string param2, int param3, int param4)
    {
        bool isInputValid = true;

        isInputValid = isInputValid && (param1.Length > 0 && param1.Length < 100);
        isInputValid = isInputValid && (param2 != "NONE");
        isInputValid = isInputValid && (param3 >= 0 && param3 <= 100);
        isInputValid = isInputValid && (param4 != 0);

        return isInputValid;
    }

I rewrote the method like this but if you don't like the way I did it you can do it another way. The important thing to remember is that you need only one return statement at the end that returns a well-named variable that tells exactly what it returns.

Keep your methods significant (split them as needed)

Many programmers have the bad habit to create big methods and put all their code in it. Even worse, some of them put the code directly on the event handlers. Normaly you should keep your methods no longer than 50 lines of code.

Each method you write should have only one signification. For example, if you have a routine that will contact a webservice, add data to a database, create a text file and write to the event viewer, please don't write all your code in the same method. Split your code into many methods.

Bad code


    public void MegaMethod()
    {
        /*
       add code to contact webservice
       add code to add data to the database
       add code to create a text file
       add code to write to the event viewer
       */

    }

Good code


    public void MegaMethod()
    {
        ContactWebService();
        AddToDataBase();
        CreateTextFile();
        WriteToEventViewer();
    }

In addition to this, when people all put in the same method, they tend to add a lot of comments to help understand when the method does. As stated in the first point, this is bad. By spliting your code into different methods, you can add comments in the methods signatures, not in the code. When you split into many methods, name them significantly because you have to keep your code self-explaining.

Don't put all in member variables, pass values as method parameters

Normaly, a function should receive parameters, treat them and output a value. Keep it this way. When you have a lot of methods, some people create a lot of member variables, affect them and use them in their different methods. It's a bad practice because you don't what your functions need to do their job.

Always have the same naming convention

It's a very simple and easy thing to follow, but how many people absolutely don't care about this. When you write constants, always write them the same way. When you write methods, always write them the same way... just look at this :

Bad code


    private string member1 = "";
    private string Member_2 = "";
    private int iMember3 = 0;

    private const string Const1 = "";
    private const string CONST2 = "";

    public void methodName1() { }
    public void Method_Name_2() { }

Good code


    private string _member1 = "";
    private string _member2 = "";
    private int _member3 = 0;

    private const string CONST_1 = "";
    private const string CONST_2 = "";

    public void MethodName1() { }
    public void MethodName2() { }

It looks obvious but I see bad naming like this every day. If you don't like screaming caps for constants, fine, you can use camel casing, but always use camel casing for your constants. In fact, when you start a project, establish the naming convention and always stick to it strictly.

Think about it before coding it

Yes, think about it. You will save time and effort. How many programmers are too pressed, too stressed that they can't wait a minute to start coding ? a lot ! This is more efficient to sit and think about what you have to code for about 10 minutes and then start to code for about an hour than immediatly start to code and patch your code the rest of the day.

When you think about how you will code, you find code than can be reused and you can find possible dead-ends.

In the end...

All of this is what I see everyday when I check other's code. I don't have to tell you I check everybody's code even if it's not officialy part of my job ! If you follow those logical and simple rules, you will see a big difference when you have to read back your code to add something and you will find it easy to read other's code.

Remember... always KISS (Keep It Simple and Stupid) !

Posted: Apr 12 2009, 08:47 by WebMasterSam | Comments (0) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us
LINK BUILDING IS PROHIBITED ON THIS WEBSITE