DCSIMG
August 2009 - Posts - Alex Golesh's Blog About Silverlight Development

August 2009 - Posts

Silverlight Quick Tip: Dynamically Updating Class Fields/Properties

In previous post (here) I blogged about displaying values of class/control in runtime and displaying them in Visual Studio - like property window. Today I’ll show how to push them back to the class instance.

In previous post I’ve stored values in “ObservableCollection<FieldsPropertiesData>” for easy databinding and connected this ListBox control. In order to get user input I’ve created TwoWay databinding in DataTemplate – here is updated data template:

<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 FieldValue, Mode=TwoWay}" 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>

Now at the setter of the “FieldValue” property all I need is to push it back to the instance.

I’ve create function, which accepts FieldName, FieldType (from data already exits in the file from the loading) and new value:

private static void SetFieldValue(string FieldName, Type FieldType, object TheValue)
{
  Assembly asm = Assembly.GetExecutingAssembly();
  Type type = asm.GetType("SAMPLE_TYPE_TO_UPDATE");
  FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);

  var fieldInfo = from field in fields
                  where field.FieldType == FieldType && field.Name == FieldName
                  select field;

  FieldInfo theField = fieldInfo.FirstOrDefault();
  if (null != theField)
    if (value.GetType() == FieldType)
      theField.SetValue(if (theField.IsStatic) ? null : TheInstance, TheValue);
    else //In case of value changed by databinding in most cases ToString() result will be here, 
         //hence need to convert
      theField.SetValue(if (theField.IsStatic) ? null : TheInstance, Convert.ChangeType(TheValue, FieldType, null));

  fieldInfo = null;
  fields = null;
  type = null;
  asm = null;
}

That’s it… Now the data dynamically displayed, updated by user and pushed back to the instance of the object.

 

Enjoy,

Alex

Israeli Developers Community Conference 2009 – Vote For My Sessions

The Israeli Developers Community Conference (IDCC) is all about developers learning, sharing and interacting with each other in an informal and relaxed atmosphere. It will take a place at

Microsoft ILDC,
13 Shenkar st., Herzeliya, Israel

Monday, 14th September 2009
08:30 - 17:30

The community will decide upon the conference’s agenda – voting is open for everyone.

 

I’ve submitted two cool sessions:

5 Most Wanted Features in Silverlight 3

Many tips and tricks, best practices and as always cool samples are promised :)

XNA Game Studio 3: Develop Once, Play Everywhere

In this session we will see how to use XNA Framework for writing cool games, which features available and how same game could be played on number of different hardware platforms (PC, XBox 360, Zune). This session will be totally FUN – cool samples, demos, “crazy” setup

 

Please vote for my sessions!!!

 

See you there,

Alex

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

Silverlight Quick Tip: How to Perform a Hit Test

In some cases, especially when developing rich UI application developer need to identify which control were clicked or under mouse pointer at some point of time or just under some coordinate at the UI. For those purposes Silverlight provides “FindElementsInHostCoordinates” function in VisualTreeHelper class.

The function gets the Point (coordinate on the screen) or Rect (rectangular area) and UIElement which will be checked recursively to have any visual child's in desired coordinate/area. The function returns IEnumerable<UIElement>.

In most cases, especially when using custom controls the returned list will have many UIElements. They need to be filtered out.

For the sample I’ve created the Custom Control with default style/template:

<Style TargetType="local:SampleControl">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="local:SampleControl">
          <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Width="{TemplateBinding Width}"
                            Height="{TemplateBinding Height}">
            <StackPanel Orientation="Vertical">
              <Path Fill="{TemplateBinding Background}" Stretch="Uniform" StrokeThickness="3" 
Stroke="{TemplateBinding BorderBrush}" Height="43.5" UseLayoutRounding="False"
Data="M21,36 C55, ..., 75.500374z"/> <TextBlock Text="{TemplateBinding SmapleStringProperty}" TextWrapping="Wrap"/> </StackPanel> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style>

image

The control is pretty empty – created 3 Dependency Properties, for store some Boolean, Integer and String data (see in sample here).

For the test page I’ve created function to initialize the number of such controls randomly and subscribed to “MouseLeftButtonDown” event on main canvas

image

In sample case, I wanted to show the list of controls, with Boolean value == true (in initialization each control got True in case its number was odd, and False if it vas even) and show them in report.

First, to get the list of controls under my mouse pointer at the time of event:

List<UIElement> list = VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(null), 
LayoutRoot as UIElement) as List<UIElement>;

Even in case of single control click my list returns with 5 elements (in case of click on overlapped controls it is even more)

image

To filter out only the SampleControl, which also has Boolean property with desired value I used LINQ:

var selectedControls = from control in list
                       where control is SampleControl && (control as SampleControl).SampleBoolProperty
                       select control as SampleControl;

In case I have controls I’m looking for I could get them and process according to my business loginc

if (selectedControls.Count() > 0)
        foreach (var selectedControl in selectedControls)
...

 image

That’s it – got the list of the controls under the desired point.

Sample source here.

Running sample here.

 

Enjoy,

Alex