Is there a way, when binding to a Dependency Object class, to hide class properties?
I mean those "Dispatcher", "DependencyObjectType" and "IsSealed"?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows;
namespace WPGDemoApp
{
public class SampleObject : DependencyObject
{
readonly static DependencyProperty IdProperty = DependencyProperty.Register("ID", typeof(int), typeof(SampleObject));
public int ID
{
get { return (int)GetValue(IdProperty); }
set
{
SetValue(IdProperty, value);
}
}
public string Name
{
get { return "Leeroy Jenkins"; }
}
}
}
Jesse, your question not very clear but if I understand it correctly you just want to show the properties present in your SampleObject i.e. those added by you and not the ones inherited from its base class.
1. check how your PropertyGrid is getting the properties of the selected object -
it should fetch the properties of selected objects type and not for object itself -
Type type = this.SelectedItem.GetType();
properties =TypeDescriptor.GetProperties(type);
instead of -
properties = TypeDescriptor.GetProperties(this.SelectedItem);
2. If this doesn't solve the problem or if you want more control over which properties to show in your PG then you can create a custom attribute. To achieve this I have created a custom attribute(similar to IsBrowsable), you just need to decorate your property with that attribute and modify your Property Grid implementation to honor it.
Here is the attribute class -
/// <summary>
/// Attribute to identify the Custom Proeprties.
/// Only Proeprties marked with this attribute(true) will be displayed in property grid.
/// </summary>
[global::System.AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class IsCustomPropertyAttribute : Attribute
{
// See the attribute guidelines at
// http://go.microsoft.com/fwlink/?LinkId=85236
private bool isCustomProperty;
public static readonly IsCustomPropertyAttribute Default = new IsCustomPropertyAttribute(false);
public static readonly IsCustomPropertyAttribute No = new IsCustomPropertyAttribute(false);
public static readonly IsCustomPropertyAttribute Yes = new IsCustomPropertyAttribute(true);
/// <summary>
/// Initializes a new instance of the <see cref="IsCustomPropertyAttribute"/> class.
/// </summary>
/// <param name="isCustomProperty">if set to <c>true</c> [is RT display property].</param>
public IsCustomPropertyAttribute(bool isCustomProperty)
{
this.isCustomProperty = isCustomProperty;
}
/// <summary>
/// Gets a value indicating whether this instance is RT display property.
/// </summary>
/// <value>
/// <c>true</c> if this instance is RT display property; otherwise, <c>false</c>.
/// The default is false.
/// </value>
public bool IsCustomProperty
{
get { return isCustomProperty; }
set { isCustomProperty = value; }
}
/// <summary>
/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
/// </summary>
/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
/// <returns>
/// <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
/// </returns>
public override bool Equals(object obj)
{
IsCustomPropertyAttribute attribute = obj as IsCustomPropertyAttribute;
if (obj == null)
return false;
if (obj == this)
return true;
return attribute.isCustomProperty == isCustomProperty;
}
public override int GetHashCode()
{
return isCustomProperty.GetHashCode();
}
public override bool IsDefaultAttribute()
{
return isCustomProperty == IsCustomPropertyAttribute.Default.isCustomProperty;
}
}
In your property grid, add a check before adding the property to grid. something like this -
// Gets the attributes for the property.
AttributeCollection attributes = propertyDescriptor.Attributes;
//Checks to see if the value of the IsCustomPropertyAttribute is Yes.
IsCustomPropertyAttribute myAttribute =
(IsCustomPropertyAttribute)attributes[typeof(IsCustomPropertyAttribute)];
//Check if current property is CustomProperty or not
if (myAttribute.IsCustomProperty == true)
{
AddProperty(propertyDescriptor);
}
This worked for expandable objects which were dependency objects. I just created a simple marker Attribute called ViewablePropertyAttribute. I didn't want all the dependency objects to be available in the grid.
public class EllevateExpandableObjectConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
var propertyDescriptors =
base.GetProperties(context, value, attributes.OfType<ViewablePropertyAttribute>().Cast<Attribute>().ToArray());
var result = propertyDescriptors.Cast<PropertyDescriptor>()
.Where(pd => pd.Attributes.OfType<ViewablePropertyAttribute>().Any())
.ToArray();
return new PropertyDescriptorCollection(result);
}
}
[ViewablePropertyAttribute]
[TypeConverter(typeof(EllevateExpandableObjectConverter)]
public MyComplexType MyInstance {get;set; }
Related
At the moment I'm working on funcionality that involves exporting and importing data to Xlsx file. Here's what I want to do: I want to have an attribute I can put above a property like this.
public class MyClass
{
[XlsxColumn("Column 1")]
public string myProperty1 { get; set; }
public int myProperty2 { get; set; }
}
So far I don't have problems, but then I want to "store references" to properties marked with the XlsxColumn attribute. I'm using reflection
to store properties data in List
var propsList = MyClass.GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(XlsxColumn)));
I have a list with all properties marked with XlsxColumn (only myProperty1 in this example).
EDIT: The problem is I don't know how to loop over properties in MyClass, but only properties with XlsxColumn attribute (so all PropertyInfo objects stored in propsList variable), without resorting to reflection with each object saved to Xlsx file.
I'm restricted to .NET 4.0.
Thanks for your time.
MyClass.GetProperties() does not work because you have to get the type of the class to invoke the GetProperties method. Otherwise you are invoking a static method called GetProperties defined in the MyClass class.
var propsList = typeof(MyClass).GetProperties().Where(
prop => prop.IsDefined(typeof(XlsxColumnAttribute), false)).ToList();
If you just want the names (IList<string>):
var propsList = typeof(Excel).GetProperties().Where(
prop => prop.IsDefined(typeof(XlsxColumnAttribute), false))
.Select(prop=> prop.Name)
.ToList();
to use .Where you have to include System.Linq
I must say that I am not sure if this is the solution you are looking for. Because I could not quite make out what your question is. Well I have tried to provide an answer as much as I could figure out.
I went for a static class for CachingPropetyProvider but you can go for an instance class and use a dependency injection library and use it as a Singleton too. Moreover, I have written extensive comments so It is as self explanatory as possible.
Let us define MyClass. I also deliberately changed it a little bit.
public class MyClass
{
[XlsxColumn("Column 1")]
public string MyProperty1 { get; set; }
[XlsxColumn("Column 2")]
public int MyProperty2 { get; set; }
}
I also defined a MetaInfo class to hold the cached information.
public class MetaInfo {
/// <summary>
/// Immutable class for holding PropertyInfo and XlsxColumn info.
/// </summary>
/// <param name="info">PropertyInfo</param>
/// <param name="attr">XlsxColumn</param>
public MetaInfo(PropertyInfo info, XlsxColumn attr) {
PropertyInfo = info;
Attribute = attr;
}
/// <summary>
/// PropertyInfo. You may want to access the value inside the property.
/// </summary>
public PropertyInfo PropertyInfo { get; }
/// <summary>
/// Attribute. You may want to access information hold inside the attribute.
/// </summary>
public XlsxColumn Attribute { get; }
}
And lastly the main guy. This guy is responsible for providing all the data about classes
public class CachingPropProvider {
/// <summary>
/// Holds the meta information for each type.
/// </summary>
private static readonly ConcurrentDictionary<Type, List<MetaInfo>> TypeCache;
/// <summary>
/// Static constructor is guaranteed to run only once.
/// </summary>
static CachingPropProvider() {
//Initialize the cache.
TypeCache = new ConcurrentDictionary<Type, List<MetaInfo>>();
}
/// <summary>
/// Gets the MetaInfo for the given type. Since We use ConcurrentDictionary it is thread safe.
/// </summary>
/// <typeparam name="T">Type parameter</typeparam>
public static IEnumerable<MetaInfo> GetCachedStuff<T>() {
//If Type exists in the TypeCache, return the cached value
return TypeCache.GetOrAdd(typeof(T),Factory);
}
/// <summary>
/// Factory method to use to extract MetaInfo when Cache is not hit.
/// </summary>
/// <param name="type">Type to extract info from</param>
/// <returns>A list of MetaInfo. An empty List, if no property has XlsxColumn attrbiute</returns>
private static List<MetaInfo> Factory(Type #type) {
//If Type does not exist in the TypeCahce runs Extractor
//Method to extract metainfo for the given type
return #type.GetProperties().Aggregate(new List<MetaInfo>(), Extractor);
}
/// <summary>
/// Extracts MetaInfo from the given property info then saves it into the list.
/// </summary>
/// <param name="seedList">List to save metainfo into</param>
/// <param name="propertyInfo">PropertyInfo to try to extract info from</param>
/// <returns>List of MetaInfo</returns>
private static List<MetaInfo> Extractor(List<MetaInfo> seedList,PropertyInfo propertyInfo) {
//Gets Attribute
var customattribute = propertyInfo.GetCustomAttribute<XlsxColumn>();
//If custom attribute is not null, it means it is defined
if (customattribute != null)
{
//Extract then add it into seed list
seedList.Add(new MetaInfo(propertyInfo, customattribute));
}
//Return :)
return seedList;
}
}
Finally let us see how to use the solution. It is pretty straightforward actually.
//Has 2 values inside
var info = CachingPropProvider.GetCachedStuff<MyClass>();
First of all, I have been looking online for a Settings Design Pattern but I haven't been able to find a solution that works in my case or only cryptic answers like Use Dependency Injection.
The Problem
I have a large ASP .NET solution which is deployed to multiple production environments. Many decisions in code are taken based on settings which have been implemented in the Web.config file (a few hundreds). At the moment they are all accessed using the NameValueCollection ConfigurationManager.AppSettings. I want to move these settings in the database such that I can create a user interface and modify them more easily afterwards.
I would also prefer for the settings to remain accessible from the Web.config such that if something happens with the database, the application won't break.
Solution Architecture
Basically, the solution consists of the following projects:
UI (this is were the Web.config file resides)
BLL
DAL
Integration Project 1 (the application interracts with other applications. This project uses configuration settings like web addresses, authentication information etc. which at the moment are passed as parameters in the methods - which I personally find very ugly)
Integration Project 2
...
The Code
The Setting class (the database table is practically the same). SettingType is an enum which defines the type of the Setting: Int, String, Bool and I defined for the UI.
public class Setting
{
/// <summary>
/// Gets or sets the identifier.
/// </summary>
/// <value>
/// The identifier.
/// </value>
public int Id { get; set; }
/// <summary>
/// Gets or sets the name of the setting.
/// </summary>
/// <value>
/// The name of the setting.
/// </value>
public string Name { get; set; }
/// <summary>
/// Gets or sets the type of the setting.
/// </summary>
/// <value>
/// The type of the setting.
/// </value>
public SettingType Type { get; set; }
/// <summary>
/// Gets or sets the setting value.
/// </summary>
/// <value>
/// The setting value.
/// </value>
public object Value { get; set; }
/// <summary>
/// Gets or sets the setting description.
/// </summary>
/// <value>
/// The setting description.
/// </value>
public string Description { get; set; }
/// <summary>
/// Gets or sets the setting key.
/// </summary>
/// <value>
/// The setting key.
/// </value>
public string Key { get; set; }
/// <summary>
/// Gets or sets the default value
/// </summary>
/// <value>
/// The default value
/// </value>
public string Default { get; set; }
}
The SettingHelper class (which does everything at the moment, will split functionality in the future):
public static class SettingHelper
{
// Settings collection
private static List<Setting> _settings;
/// <summary>
/// Reloads the settings.
/// </summary>
/// <exception cref="System.Exception"></exception>
public static void LoadSettings()
{
_settings = new List<Setting>();
// Code which loads the settings from the database
}
private static Setting GetSetting(string key)
{
try
{
// if settings are not loaded, we reload them from the database
if (_settings == null || _settings.Count == 0)
LoadSettings();
var value = from Setting setting in _settings
where setting != null && setting.Key == key
select setting;
return value.FirstOrDefault();
}
catch (Exception)
{
return null;
}
}
public static void SetSetting(string key, object newValue, int userID)
{
var currentSetting = GetSetting(key);
if (currentSetting != null && !Convert.ToString(currentSetting.Value).ToUpper().Equals(Convert.ToString(newValue).ToUpper()))
{
// Code which updates the setting value
}
}
// For the UI
public static IEnumerable<Setting> GetAllSettings()
{
if (_settings == null)
LoadSettings();
return _settings;
}
// To change back swiftly to the Web.config in case there are errors in production - will remove in the future
public static bool SettingsFromDataBase
{
get
{
if (HttpContext.Current.Application["SettingsFromDataBase"] == null)
HttpContext.Current.Application["SettingsFromDataBase"] = ConfigurationManager.AppSettings["SettingsFromDataBase"];
bool settingsFromDataBase;
if (bool.TryParse(HttpContext.Current.Application["SettingsFromDataBase"] as string, out settingsFromDataBase))
return settingsFromDataBase;
else return false;
}
}
public static T ObjectToGenericType<T>(object obj)
{
if (obj is T)
{
return (T)obj;
}
else
{
try
{
return (T)Convert.ChangeType(obj, typeof(T));
}
catch (InvalidCastException)
{
return default(T);
}
}
}
public static T GetSetting<T>(string key)
{
if (SettingsFromDataBase)
{
var setting = GetSetting(key);
return setting != null
? ObjectToGenericType<T>(setting.Value)
: ObjectToGenericType<T>(ConfigurationManager.AppSettings[key]);
}
if (HttpContext.Current.Application[key] == null)
HttpContext.Current.Application[key] = ConfigurationManager.AppSettings[key];
return (T)HttpContext.Current.Application[key];
}
// The actual settings which will be used in the other projects
public static string StringSetting
{
get { return GetSetting<string>("StringSetting"); }
}
public static bool BoolSetting
{
get { return GetSetting<bool>("BoolSetting"); }
}
Note: Likely there are improvements to this class using Reflection, but I would prefer to avoid it (apart from that conversion).
Questions
At the moment the SettingHelper resides in the UI project (in order to be able to access the settings from the Web.config). How can I transmit these settings in the BLL or Integration projects?
Also, do you think this implementation with all the settings stored in a static list is the way to go? It feels unoptimal.
Any help or discussion on improvements is appreciated. Thank you.
I have a structure:
public struct ServiceDescription
{
string serviceDescriptionText;
string serviceLogoRef;
/// <summary>
/// The position to print the service on the label.
/// </summary>
int servicePosition;
/// <summary>
/// Constructor
/// </summary>
/// <param name="serviceDescriptionText"></param>
/// <param name="serviceLogoRef"></param>
/// <param name="servicePosition"></param>
public ServiceDescription(string serviceDescriptionText, string serviceLogoRef,
int servicePosition)
{
this.serviceDescriptionText = serviceDescriptionText;
this.serviceLogoRef = serviceLogoRef;
this.servicePosition = servicePosition;
}
}
and a property:
public string pServiceDescription
{
get
{
return p_sServiceDescription;
}
// set private structure field 1
// set private structure field 2
// etc...
}
How do I set each of the private fields of the structure in the setters of my property?
It is usually a very bad idea to have mutable structs; mutable value-type semantics is not what people usually expect. You can, however, simply add a set that works like any other regular setter. It just isn't a very good idea:
public string Text
{
get { return text; }
set { text = value; } // this is a really bad idea on a struct
}
If it was me, that would be an immutable struct with private readonly fields and a constructor that sets all of them (and get-only properties), or a class - probably with automatically implemented properties, i.e.
public class ServiceDescription {
public string Text {get;set;}
//...
}
I'm using the data annotations attributes for validation in my app, and I want to have a RequiredAsSet attribute, which will require either all properties decorated with the attribute to be populated, or none of them. The set cannot be part populated.
I thought a clever way to do it would be like this:
public class RequiredAsSetAttribute : RequiredAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="RequiredAsSetAttribute"/> class.
/// </summary>
/// <param name="viewModel">The view model.</param>
public RequiredAsSetAttribute(IViewModel viewModel)
{
this.ViewModel = viewModel;
}
/// <summary>
/// Gets or sets the view model.
/// </summary>
/// <value>The view model.</value>
private IViewModel ViewModel { get; set; }
/// <summary>
/// Determines whether the specified value is valid.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>
/// <c>true</c> if the specified value is valid; otherwise, <c>false</c>.
/// </returns>
public override bool IsValid(object value)
{
IEnumerable<PropertyInfo> properties = GetPropertiesWihRequiredAsSetAttribute();
bool aValueHasBeenEnteredInTheRequiredFieldSet = properties.Any(property => !string.IsNullOrEmpty(property.GetValue(this.ViewModel, null).ToString()));
if (aValueHasBeenEnteredInTheRequiredFieldSet)
{
return base.IsValid(value);
}
return true;
}
/// <summary>
/// Gets the properties with required as set attribute.
/// </summary>
/// <returns></returns>
private IEnumerable<PropertyInfo> GetPropertiesWithRequiredAsSetAttribute()
{
return this.ViewModel.GetType()
.GetProperties()
.Where(p => GetValidatorsFromProperty(p).Length != 0 && !GetValidatorsFromProperty(p).Any(x => x == this));
}
/// <summary>
/// Gets the validators from property.
/// </summary>
/// <param name="property">The property.</param>
/// <returns></returns>
private static RequiredAsSetAttribute[] GetValidatorsFromProperty(PropertyInfo property)
{
return (RequiredAsSetAttribute[])property.GetCustomAttributes(typeof(RequiredAsSetAttribute), true);
}
}
It basically takes my view model as a constructor argument and uses reflection to find the other properties decorated with the RequiredAsSet attribute to check if anything has been entered.
Turns out this isn't such a clever idea though, because you can't pass instances into the constructor of an attribute..only constant expresssions, typeof expressions or array creation expressions, as the compiler helpfully pointed out.
So is there another way to do this?
If I understand the problem correcty, the way to do this is with a class level validation attribute. You then have access to the entire object and can use reflection to access whatever properties you wish. The instance is passed in during validation.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class RequiredAsSetAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
var properties = TypeDescriptor.GetProperties(value);
...
}
}
I'm currently working on a Windows Forms GUI and I have a Combo that I need to display a list of string values as the DisplayMembers and use a list of user defined enum values as the ValueMember. I'm currently returning a List> from my database access function and I would like to bind this to my Combo box. I've tried assigning the list to the .DataSource property, assigning "Key" to .DataMember and "Value" to .DisplayMember. This is clearly not a valid approach as it is not working.
Can someone please give me another approach that is in good form and actually works?
Thanks
I do use my own class EnumPair<> in combination with two extension methods to bind comboboxes to Properties with enum types.
See if this can help you, that you can work directly with the enums.
Use it like this after implementation:
comboBox.BindToEnumValue<MyEnumType>(myBindingSourceInstance, "PropertyNameOfBindingSource");
That assumes you have a ComboBox named "comboBox" on your form, an Enum called "MyEnumType" and an instance of a BindingSource. The PropertyNameOfBindingSource should be the name of the Property of the type that your BindingSource has a list of, that has the PropertyType of MyEnumType.
Implementation for the background work is found below, the extension methods are not needed, i just do not like write nearly identical lines of code ;-)
public static class ComboBoxExtensions
{
public static void BindToEnumValue<TEnum>(this ComboBox cbo, BindingSource bs, string propertyName)
{
cbo.DataSource = EnumPair<TEnum>.GetValuePairList();
cbo.ValueMember = EnumPair<TEnum>.ValueMember;
cbo.DisplayMember = EnumPair<TEnum>.DisplayMember;
cbo.DataBindings.Add(new Binding("SelectedValue", bs, propertyName));
}
public static void BindClear(this ComboBox cbo)
{
cbo.DataSource = null;
cbo.DataBindings.Clear();
}
}
/// <summary>
/// Represents a <see cref="EnumPair"/> consisting of an value
/// of an enum T and a string represantion of the value.
/// </summary>
/// <remarks>
/// With this generic class every <see cref="Enum"/> can be
/// dynamically enhanced by additional values, such as an empty
/// entry, which is usefull in beeing used with
/// <see cref="ComboBox"/>es.
/// </remarks>
/// <typeparam name="T">The type of the <see cref="Enum"/> to represent.</typeparam>
public partial class EnumPair<T>
{
#region Constants
public const string ValueMember = "EnumValue";
public const string DisplayMember = "EnumStringValue";
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="EnumPair"/> class.
/// </summary>
public EnumPair()
{
Type t = typeof(T);
if (!t.IsEnum)
{
throw new ArgumentException("Class EnumPair<T> can only be instantiated with Enum-Types!");
}
}
/// <summary>
/// Initializes a new instance of the <see cref="EnumPair"/> class.
/// </summary>
/// <param name="value">The value of the enum.</param>
/// <param name="stringValue">The <see cref="string"/> value of the enum.</param>
public EnumPair(T value, string stringValue)
{
Type t = typeof(T);
if (!t.IsEnum)
{
throw new ArgumentException("Class EnumPair<T> can only be instantiated with Enum-Types!");
}
this.EnumValue = value;
this.EnumStringValue = stringValue;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the value part of the <see cref="EnumPair"/>.
/// </summary>
public T EnumValue { get; set; }
/// <summary>
/// Gets or sets the string value of the <see cref="EnumPair"/>.
/// </summary>
public string EnumStringValue { get; set; }
#endregion
#region Methods
/// <summary>
/// Returns a <see cref="string"/> that represents the current <see cref="EnumPair"/>.
/// </summary>
public override string ToString()
{
return this.EnumStringValue;
}
/// <summary>
/// Generates a <see cref="List<T>"/> of the values
/// of the <see cref="Enum"/> T.
/// </summary>
public static List<EnumPair<T>> GetValuePairList()
{
List<EnumPair<T>> list = new List<EnumPair<T>>();
EnumPair<T> pair = new EnumPair<T>();
foreach (var item in Enum.GetValues(typeof(T)))
{
pair = new EnumPair<T>();
pair.EnumValue = (T)item;
pair.EnumStringValue = ((T)item).ToString();
list.Add(pair);
}
return list;
}
/// <summary>
/// Implicit conversion from enum value to <see cref="EnumPair<>"/> from that enum.
/// </summary>
/// <param name="e">The enum value to convert to.</param>
/// <returns>A <see cref="EnumPair<>"/> to the enum value.</returns>
public static implicit operator EnumPair<T>(T e)
{
Type t = typeof(EnumPair<>).MakeGenericType(e.GetType());
return new EnumPair<T>((T)e, ((T)e).ToString());
}
#endregion
}
You can try something like this:
ddTemplates.DataSource =
Enum.GetValues(typeof(EmailTemplateType))
.Cast<EmailTemplateType>().ToList()
.Select(v => new KeyValuePair<int, string>((int)v, v.ToString())).ToList();