Silverlight Quick Tip: Dynamically Displaying Class Fields/Properties - DevCorner

Silverlight Quick Tip: Dynamically Displaying Class Fields/Properties

In one of my projects I had to create something like Visual Studio property window, for data set of different controls/data classes. Those controls/classes are still under development and I needed the way to display/change values of those properties dynamically without even knowing what is inside.

For the simple case, let’s assume the following class which holds the data:

public class SampleData
{
  public SampleData()
  {
    BooleanProperty = false;
  }

  public string StringProperty { get; set; }
  public bool BooleanProperty { get; set; }
  public Button ButtonProperty { get; set; }
  public static double StaticDoubleFiled = 123;
  public static string StaticStringFiled = "This is static field";
  public int IntFiled = 345;
  public string StringFiled = "This is non static field";
  public float FloatProperty = 456.0f;
}

The class fields/properties should be displayed dynamically. I decided to create an attribute to decorate the relevant fields/properties and then get the values/metadata from attributes on-the-fly via reflection.

So here is a sample attribute I’ve create for this:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
internal sealed class PropertyFieldDescriptionAttribute : Attribute
{

  #region .ctor
  public PropertyFieldDescriptionAttribute(string DisplayName)
  {
    this.displayName = DisplayName;
    this.IsReadOnly = false;
  }
  #endregion

  #region Public proerties
  public string DisplayName
  {
    get { return displayName; }
  }

  public string Description { get; set; }
  public string DefaultValue { get; set; }
  public bool IsReadOnly { get; set; }
  #endregion

  #region Private variables
  readonly string displayName;
  #endregion
}

And decorated fields/properties with the attribute (partial code):

[PropertyFieldDescription("Sample String Property", Description="This is sample string property")]
public string StringProperty { get; set; }

[PropertyFieldDescription("Sample Boolean Property", Description = "This is sample boolean property", DefaultValue="false")]
public bool BooleanProperty { get; set; }

[PropertyFieldDescription("Sample Button Property", Description = "This is sample button property")]
public Button ButtonProperty { get; set; }

From here everything were pretty straight forward – sample code for getting fields information:

FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);

if (fields.Length > 0)
{
  foreach (var field in fields)
  {
    PropertyFieldDescriptionAttribute[] attribs = (PropertyFieldDescriptionAttribute[])field.GetCustomAttributes(
typeof(PropertyFieldDescriptionAttribute), false); if (attribs.Length == 1) //Each valid property in this sample case should have only 1(!) such attribute { //Define data class to hold it FieldsPropertiesData constant = new FieldsPropertiesData(field.Name, field.FieldType); if (field.IsStatic) constant.FiledValue = field.GetValue(null); else constant.FiledValue = field.GetValue(instance); constant.DefaultValue = attribs[0].DefaultValue; constant.Description = attribs[0].Description; constant.DisplayName = attribs[0].DisplayName; constant.ReadOnly = attribs[0].IsReadOnly; //add to collection, which will be databoud to the screen constants.Add(constant); } } }

and almost the same function for the properties.

The results I stored in “ObservableCollection<FieldsPropertiesData>” for easy data binding.

The FieldsPropertiesData of mine is like follows:

public class FieldsPropertiesData
{
  #region .ctor
  public FieldsPropertiesData(string FiledName, Type FieldType)
  {
    this.FieldName = FieldName;
    this.FieldType = FieldType;
  }

  #endregion

  #region Public properties
  public object FiledValue { get; set; }
  public Type FieldType { get; private set; }
  public string FieldName { get; private set; }
  public string DisplayName { get; set; }
  public string Description { get; set; }
  public string DefaultValue { get; set; }
  public bool ReadOnly { get; set; }
  #endregion
}

I’ve created 2 simple custom controls – one to serve as a grid for my properties grid, and another to hold data item

The Grid:

[TemplatePart(Name="lst", Type=typeof(ListBox))]
public class FieldsPropertiesGrid : Control
{
  public FieldsPropertiesGrid()
  {
    this.DefaultStyleKey = typeof(FieldsPropertiesGrid);
  }

  public ObservableCollection<FieldsPropertiesData> Data
  {
    get { return (ObservableCollection<FieldsPropertiesData>)GetValue(DataProperty); }
    set { SetValue(DataProperty, value); }
  }

  // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
  public static readonly DependencyProperty DataProperty =
      DependencyProperty.Register("Data", typeof(ObservableCollection<FieldsPropertiesData>), typeof(FieldsPropertiesGrid), null);

  public override void  OnApplyTemplate()
  {
       base.OnApplyTemplate();

    ListBox lst = GetTemplateChild("lst") as ListBox;
    lst.DataContext = this;
  }
}

The DataItem (partial):

public class FiledPropertyData : Control
{
  public FiledPropertyData()
  {
    this.DefaultStyleKey = typeof(FiledPropertyData);
  }


  public bool IsReadOnly
  {
    get { return (bool)GetValue(IsReadOnlyProperty); }
    set { SetValue(IsReadOnlyProperty, value); }
  }

  // Using a DependencyProperty as the backing store for IsReadOnly.  This enables animation, styling, binding, etc...
  public static readonly DependencyProperty IsReadOnlyProperty =
      DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(FiledPropertyData), null);

  public string DefaultValue
  {
    get { return (string)GetValue(DefaultValueProperty); }
    set { SetValue(DefaultValueProperty, value); }
  }

  // And following depenedency properties: DefaultValue, Description, DisplayName, etc.
  // ... 
}

The default template for the controls:

 

<Style TargetType="local:FieldsPropertiesGrid">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="local:FieldsPropertiesGrid">
        <Border Background="{TemplateBinding Background}"
                          BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}">
          <ListBox x:Name="lst" ItemsSource="{Binding Data}">
            <ListBox.ItemTemplate>
              <DataTemplate>
                <local:FiledPropertyData Margin="0,2.5"/>
              </DataTemplate>
            </ListBox.ItemTemplate>
          </ListBox>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<Style TargetType="local:FiledPropertyData">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="local:FiledPropertyData">
        <Grid ToolTipService.Placement="Mouse" Width="{TemplateBinding Width}">
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <TextBlock Text="{Binding DisplayName}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="0,0,10,0"/>
          <TextBox Text="{Binding DefaultValue}" VerticalAlignment="Center" HorizontalAlignment="Right" IsEnabled="{Binding IsReadOnly, Converter={StaticResource boolToEnabledConverter}}" Grid.Column="1"/>
          <ToolTipService.ToolTip>
            <ToolTip Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Padding="0" Margin="5">
              <ToolTip.Content>
                <Border Background="PaleGoldenrod" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Padding="5" Margin="0">
                  <TextBlock Text="{Binding Description}"/>
                </Border>
              </ToolTip.Content>
            </ToolTip>
          </ToolTipService.ToolTip>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

The sample page creates an instance of SampleData class, and displays attributed the properties/fields in the fields grid:

<Grid x:Name="LayoutRoot" Background="White">
  <Grid.Resources>
    <local:PropetiesConverter x:Key="propertiesConverter"/>
    <Button x:Key="theSampleButton" Content="Sample Button" Width="100" Height="25"/>
    <local:SampleData x:Key="theSampleData" BooleanProperty="True" StringProperty="Test String" 
ButtonProperty="{StaticResource theSampleButton}"/> </Grid.Resources> <local:FieldsPropertiesGrid x:Name="propGrid" Data="{Binding Source={StaticResource theSampleData},
Converter={StaticResource propertiesConverter}}"/> </Grid>

The last thing here is the converter which get’s the instance and returns the ObservableCollection with properties/fields info:

public class PropetiesConverter : IValueConverter
{
  #region IValueConverter Members

  public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    Type destType = value.GetType();
    ObservableCollection<FieldsPropertiesData> props = ReflectionHelper<SampleData>.GetProperties((SampleData)value);

    return props;
  }

  public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
  {
    throw new NotImplementedException();
  }

  #endregion
}

That’s it – not any type with known attributes will be displayed in the Properties Grid:

image

If adding new Field/Property with the attribute it will be shown next time the Properties grid displayed:

[PropertyFieldDescription("Additional String Field", Description = "This is additional sample string field", 
DefaultValue = "Test Value")] public string AdditionaStringProperty = "New Value";

image

 

The source code here.

Running sample here.

 

Enjoy,

Alex

Published Thursday, August 13, 2009 2:21 PM by Alex Golesh

Comments

# Silverlight Quick Tip: Dynamically Displaying Class Fields/Properties - DevCorner

Thank you for submitting this cool story - Trackback from DotNetShoutout

Wednesday, August 19, 2009 7:59 PM by DotNetShoutout

# Silverlight Quick Tip: Dynamically Updating Class Fields/Properties

In previous post ( here ) I blogged about displaying values of class/control in runtime and displaying

Thursday, August 27, 2009 11:03 AM by DevCorner

# Silverlight Quick Tip: Dynamically Updating Class Fields/Properties

In previous post ( here ) I blogged about displaying values of class/control in runtime and displaying

Thursday, August 27, 2009 11:26 AM by Community Blogs

# credit repair company

I have been looking through these pages and thought this site was interesting!

Sunday, November 08, 2009 6:09 AM by credit repair company

Leave a Comment

(required) 
(required) 
(optional)
(required) 

Enter the numbers above: