/// <summary>
        /// Sets the properties.
        /// </summary>
        /// <param name="pi">The property item.</param>
        /// <param name="instance">The instance.</param>
        protected virtual void SetProperties(PropertyItem pi, object instance)
        {
            var tabName      = this.DefaultTabName ?? instance.GetType().Name;
            var categoryName = this.DefaultCategoryName;

            // find the declaring type
            var declaringType = pi.Descriptor.ComponentType;
            var propertyInfo  = instance.GetType().GetProperty(pi.Descriptor.Name);

            if (propertyInfo != null)
            {
                declaringType = propertyInfo.DeclaringType;
            }

            if (declaringType != this.CurrentDeclaringType)
            {
                this.CurrentCategory = null;
            }

            this.CurrentDeclaringType = declaringType;

            if (!this.InheritCategories)
            {
                this.CurrentCategory = null;
            }

            var ca = pi.GetAttribute <System.ComponentModel.CategoryAttribute>();

            if (ca != null)
            {
                this.CurrentCategory = ca.Category;
                this.CurrentCategoryDeclaringType = declaringType;
            }

            var ca2 = pi.GetAttribute <DataAnnotations.CategoryAttribute>();

            if (ca2 != null)
            {
                this.CurrentCategory = ca2.Category;
                this.CurrentCategoryDeclaringType = declaringType;
            }

            var category = this.CurrentCategory ?? (this.DefaultCategoryName ?? this.GetCategory(pi.Descriptor, declaringType));

            if (category != null)
            {
                var items = category.Split('|');
                if (items.Length == 2)
                {
                    tabName      = items[0];
                    categoryName = items[1];
                }

                if (items.Length == 1)
                {
                    categoryName = items[0];
                }
            }

            var displayName = this.GetDisplayName(pi.Descriptor, declaringType);
            var description = this.GetDescription(pi.Descriptor, declaringType);

            // Localize the strings
            pi.DisplayName = this.GetLocalizedString(displayName, declaringType);
            pi.Description = this.GetLocalizedDescription(description, declaringType);
            pi.Category    = this.GetLocalizedString(categoryName, this.CurrentCategoryDeclaringType);
            pi.Tab         = this.GetLocalizedString(tabName, this.CurrentCategoryDeclaringType);

            pi.IsReadOnly = pi.Descriptor.IsReadOnly();

            // Find descriptors by convention
            pi.IsEnabledDescriptor = pi.GetDescriptor(string.Format(this.EnabledPattern, pi.PropertyName));
            pi.IsVisibleDescriptor = pi.GetDescriptor(string.Format(this.VisiblePattern, pi.PropertyName));
            pi.OptionalDescriptor  = pi.GetDescriptor(string.Format(this.OptionalPattern, pi.PropertyName));

            foreach (Attribute attribute in pi.Descriptor.Attributes)
            {
                this.SetAttribute(attribute, pi, instance);
            }

            pi.IsOptional = pi.IsOptional || pi.OptionalDescriptor != null;

            if (pi.Descriptor.PropertyType == typeof(TimeSpan) && pi.Converter == null)
            {
                pi.Converter          = new TimeSpanToStringConverter();
                pi.ConverterParameter = pi.FormatString;
            }
        }
        /// <summary>
        /// Sets the properties.
        /// </summary>
        /// <param name="pi">
        /// The property item.
        /// </param>
        protected virtual void SetProperties(PropertyItem pi)
        {
            var pd         = pi.Descriptor;
            var properties = pi.Properties;

            var tabName      = this.DefaultTabName ?? pi.Instance.GetType().Name;
            var categoryName = this.DefaultCategoryName;

            // find the declaring type
            Type declaringType = pi.Descriptor.ComponentType;
            var  propertyInfo  = pi.Instance.GetType().GetProperty(pi.Descriptor.Name);

            if (propertyInfo != null)
            {
                declaringType = propertyInfo.DeclaringType;
            }

            if (declaringType != this.CurrentDeclaringType)
            {
                this.CurrentCategory = null;
            }

            this.CurrentDeclaringType = declaringType;

            if (!this.InheritCategories)
            {
                this.CurrentCategory = null;
            }

            var ca = AttributeHelper.GetFirstAttribute <CategoryAttribute>(pd);

            if (ca != null)
            {
                this.CurrentCategory = ca.Category;
                this.CurrentCategoryDeclaringType = declaringType;
            }

            var category = this.CurrentCategory ?? (this.DefaultCategoryName ?? pd.Category);

            if (category != null)
            {
                var items = category.Split('|');
                if (items.Length == 2)
                {
                    tabName      = items[0];
                    categoryName = items[1];
                }

                if (items.Length == 1)
                {
                    categoryName = items[0];
                }
            }

            var displayName = this.GetDisplayName(pd, declaringType);
            var description = this.GetDescription(pd, declaringType);

            pi.DisplayName = this.GetLocalizedString(displayName, declaringType);
            pi.Description = this.GetLocalizedDescription(description, declaringType);

            pi.Category            = this.GetLocalizedString(categoryName, this.CurrentCategoryDeclaringType);
            pi.CategoryDescription = this.GetLocalizedDescription(categoryName, this.CurrentCategoryDeclaringType);
            pi.Tab            = this.GetLocalizedString(tabName, this.CurrentCategoryDeclaringType);
            pi.TabDescription = this.GetLocalizedDescription(tabName, this.CurrentCategoryDeclaringType);

            // Find descriptors by convention
            pi.IsEnabledDescriptor = pi.GetDescriptor(string.Format(this.EnabledPattern, pd.Name));
            pi.IsVisibleDescriptor = pi.GetDescriptor(string.Format(this.VisiblePattern, pd.Name));
            pi.OptionalDescriptor  = pi.GetDescriptor(string.Format(this.OptionalPattern, pd.Name));

            pi.UseRadioButtons = pi.GetAttribute <RadioButtonsAttribute>() != null;
            var fp = pi.GetAttribute <FontPreviewAttribute>();

            pi.PreviewFonts = fp != null;
            if (fp != null)
            {
                pi.FontSize   = fp.Size;
                pi.FontWeight = fp.Weight;
                pi.FontFamilyPropertyDescriptor = pi.GetDescriptor(fp.FontFamilyPropertyName);
            }

            var fpa = pi.GetAttribute <FilePathAttribute>();

            pi.IsFilePath = fpa != null;
            if (fpa != null)
            {
                pi.FilePathFilter           = fpa.Filter;
                pi.FilePathDefaultExtension = fpa.DefaultExtension;
                pi.IsFileOpenDialog         = fpa.UseOpenDialog;
            }

            var dpa = pi.GetAttribute <DirectoryPathAttribute>();

            pi.IsDirectoryPath = dpa != null;

            foreach (var da in pi.GetAttributes <DataTypeAttribute>())
            {
                pi.DataTypes.Add(da.DataType);
                switch (da.DataType)
                {
                case DataType.MultilineText:
                    pi.AcceptsReturn = true;
                    break;

                case DataType.Password:
                    pi.IsPassword = true;
                    break;
                }
            }

            foreach (var da in pi.GetAttributes <ColumnAttribute>())
            {
                pi.Columns.Add(da);
            }

            var la = pi.GetAttribute <ListAttribute>();

            if (la != null)
            {
                pi.ListCanAdd               = la.CanAdd;
                pi.ListCanRemove            = la.CanRemove;
                pi.ListMaximumNumberOfItems = la.MaximumNumberOfItems;
            }

            var sia = pi.GetAttribute <SortIndexAttribute>();

            if (sia != null)
            {
                pi.SortIndex = sia.SortIndex;
            }

            var eba = pi.GetAttribute <EnableByAttribute>();

            if (eba != null)
            {
                pi.IsEnabledDescriptor = properties.Find(eba.PropertyName, false);
            }

            var vba = pi.GetAttribute <VisibleByAttribute>();

            if (vba != null)
            {
                pi.IsVisibleDescriptor = properties.Find(vba.PropertyName, false);
            }

            if (pi.Is(typeof(IList)))
            {
                pi.MinimumHeight   = 100;
                pi.MaximumHeight   = 240;
                pi.HeaderPlacement = HeaderPlacement.Above;
            }

            pi.IsOptional = pi.OptionalDescriptor != null;

            var oa = pi.GetAttribute <OptionalAttribute>();

            if (oa != null)
            {
                if (oa.PropertyName != null)
                {
                    pi.OptionalDescriptor = properties.Find(oa.PropertyName, false);
                }

                pi.IsOptional = true;
            }

            pi.IsComment = pi.GetAttribute <CommentAttribute>() != null;
            if (pi.IsComment)
            {
                pi.HeaderPlacement = HeaderPlacement.Hidden;
            }

            pi.IsEditable = pi.GetAttribute <IsEditableAttribute>() != null;

            pi.AutoUpdateText = pi.GetAttribute <AutoUpdateTextAttribute>() != null;

            var ispa = pi.GetAttribute <ItemsSourcePropertyAttribute>();

            if (ispa != null)
            {
                pi.ItemsSourceDescriptor = properties.Find(ispa.PropertyName, false);
            }

            var rpa = pi.GetAttribute <BasePathPropertyAttribute>();

            if (rpa != null)
            {
                pi.RelativePathDescriptor = properties.Find(rpa.BasePathPropertyName, false);
            }

            var fa = pi.GetAttribute <FilterPropertyAttribute>();

            if (fa != null)
            {
                pi.FilterDescriptor = properties.Find(fa.PropertyName, false);
            }

            var fsa = pi.GetAttribute <FormatStringAttribute>();

            if (fsa != null)
            {
                pi.FormatString = fsa.FormatString;
            }

            var coa = pi.GetAttribute <ConverterAttribute>();

            if (coa != null)
            {
                pi.Converter = Activator.CreateInstance(coa.ConverterType) as IValueConverter;
            }

            var sa = pi.GetAttribute <SlidableAttribute>();

            if (sa != null)
            {
                pi.IsSlidable          = true;
                pi.SliderMinimum       = sa.Minimum;
                pi.SliderMaximum       = sa.Maximum;
                pi.SliderSmallChange   = sa.SmallChange;
                pi.SliderLargeChange   = sa.LargeChange;
                pi.SliderSnapToTicks   = sa.SnapToTicks;
                pi.SliderTickFrequency = sa.TickFrequency;
            }

            var spa = pi.GetAttribute <SpinnableAttribute>();

            if (spa != null)
            {
                pi.IsSpinnable     = true;
                pi.SpinMinimum     = spa.Minimum;
                pi.SpinMaximum     = spa.Maximum;
                pi.SpinSmallChange = spa.SmallChange;
                pi.SpinLargeChange = spa.LargeChange;
            }

            var wpa = pi.GetAttribute <WidePropertyAttribute>();

            if (wpa != null)
            {
                pi.HeaderPlacement = wpa.ShowHeader ? HeaderPlacement.Above : HeaderPlacement.Hidden;
            }

            var wia = pi.GetAttribute <WidthAttribute>();

            if (wia != null)
            {
                pi.Width = wia.Width;
            }

            var hpa = pi.GetAttribute <HeaderPlacementAttribute>();

            if (hpa != null)
            {
                pi.HeaderPlacement = hpa.HeaderPlacement;
            }

            var ha = pi.GetAttribute <HeightAttribute>();

            if (ha != null)
            {
                pi.Height        = ha.Height;
                pi.MinimumHeight = ha.MinimumHeight;
                pi.MaximumHeight = ha.MaximumHeight;
                pi.AcceptsReturn = true;
            }
        }