Babylon Toolkit : a toolbox for developing 3D applications with Silverlight 5

[05/10/2011 : Adding StatesManager]

I just created a new Codeplex project for adding some high level features to Silverlight 5:

https://babylontoolkit.codeplex.com/

You can also grab it using NuGet :

https://www.nuget.org/List/Packages/Babylon.Toolkit

Effect class

With Babylon.Toolkit you will be able to create effects around your shaders.

Shaders must be compiled with a custom build task you can install with the ($Project)LibsShaderBuildTaskSetup.msi. Once the task is installed, you can set PixelShader or VertexShader build action to your shaders:

To use your shader, just create a class inheriting from Effect class (or encapsulating an effect):





public class BasicEffect : Effect
{
public BasicEffect(GraphicsDevice device)
:
base(device, Babylon.Toolbox, BasicEffect/BasicEffect)
{

}

}




<p>
  Effect instances are optimized by using a shaders and registers cache. So you can have many effects on the same shader as the cache will only instantiate one shader and will reference it on all effects.
</p>

<p>
  You can affect effect’s parameters by using direct affectation or by using EffectParameter class:
</p>

<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
  <pre style="background-color: white; width: 789px; height: 366px; overflow: auto"><div>

public class BasicEffect : Effect
{
readonly EffectParameter worldViewProjectionParameter;
readonly EffectParameter worldParameter;

</span><span style="color: #0000ff">public</span><span style="color: #000000"> Matrix World { </span><span style="color: #0000ff">get</span><span style="color: #000000">; </span><span style="color: #0000ff">set</span><span style="color: #000000">; }
</span><span style="color: #0000ff">public</span><span style="color: #000000"> Matrix View { </span><span style="color: #0000ff">get</span><span style="color: #000000">; </span><span style="color: #0000ff">set</span><span style="color: #000000">; }
</span><span style="color: #0000ff">public</span><span style="color: #000000"> Matrix Projection { </span><span style="color: #0000ff">get</span><span style="color: #000000">; </span><span style="color: #0000ff">set</span><span style="color: #000000">; }

</span><span style="color: #0000ff">public</span><span style="color: #000000"> BasicEffect(GraphicsDevice device)
    : </span><span style="color: #0000ff">base</span><span style="color: #000000">(device, </span><span style="color: #800000">"</span><span style="color: #800000">Babylon.Toolbox</span><span style="color: #800000">"</span><span style="color: #000000">, </span><span style="color: #800000">"</span><span style="color: #800000">BasicEffect/BasicEffect</span><span style="color: #800000">"</span><span style="color: #000000">)
{
    worldViewProjectionParameter </span><span style="color: #000000">=</span><span style="color: #000000"> GetParameter(</span><span style="color: #800000">"</span><span style="color: #800000">WorldViewProjection</span><span style="color: #800000">"</span><span style="color: #000000">);
    worldParameter </span><span style="color: #000000">=</span><span style="color: #000000"> GetParameter(</span><span style="color: #800000">"</span><span style="color: #800000">World</span><span style="color: #800000">"</span><span style="color: #000000">);
}

</span><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">override</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> Apply()
{         
    worldViewProjectionParameter.SetValue(World </span><span style="color: #000000">*</span><span style="color: #000000"> View </span><span style="color: #000000">*</span><span style="color: #000000"> Projection);
    worldParameter.SetValue(World);

    </span><span style="color: #0000ff">base</span><span style="color: #000000">.Apply();
}

}

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <p>
    You can find a complete shaders sample in the project Babylon.Toolkit. The class called BasicEffect provides a simple way to do basic rendering with the following features:
  </p>

  <ul>
    <li>
      Ambient color
    </li>
    <li>
      Emissive color
    </li>
    <li>
      Diffuse color and texture
    </li>
    <li>
      Alpha transparency
    </li>
    <li>
      One point light
    </li>
  </ul>

  <h1>
    Camera classes
  </h1>

  <p>
    Babylon.Toolkit provides 2 cameras classes:
  </p>

  <ul>
    <li>
      Camera : a standard camera defined by a position and a target
    </li>
    <li>
      OrbitCamera : an orbital camera defined by a target and two angles representing rotation angles around the target
    </li>
  </ul>

  <p>
    Camera classes provides View and Projection matrices you can use with your shaders/effects.
  </p>

  <p>
    For instance, here is a sample code of an application using OrbitCamera:
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
    <pre style="background-color: white; width: 789px; height: 1060px; overflow: auto"><div>

public partial class MainPage
{
bool mouseLeftDown;
Point startPosition;
readonly OrbitCamera camera = new OrbitCamera{Radius = 15};

</span><span style="color: #0000ff">public</span><span style="color: #000000"> MainPage()
{
    InitializeComponent();
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> DrawingSurface_Draw(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, DrawEventArgs e)
{
    camera.ApplyInertia();

    </span><span style="color: #008000">//</span><span style="color: #008000"> Draw

    </span><span style="color: #008000">//</span><span style="color: #008000"> Invalidate</span><span style="color: #008000">

e.InvalidateSurface();
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> drawingSurface_SizeChanged(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, SizeChangedEventArgs e)
{
    ComputeAspectRatio();
}

</span><span style="color: #0000ff">void</span><span style="color: #000000"> ComputeAspectRatio()
{
    camera.AspectRatio </span><span style="color: #000000">=</span><span style="color: #000000"> (</span><span style="color: #0000ff">float</span><span style="color: #000000">)(drawingSurface.ActualWidth </span><span style="color: #000000">/</span><span style="color: #000000"> drawingSurface.ActualHeight);
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> UserControl_Loaded(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, RoutedEventArgs e)
{
    ComputeAspectRatio();
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> drawingSurface_MouseMove(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, System.Windows.Input.MouseEventArgs e)
{
    </span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">!</span><span style="color: #000000">mouseLeftDown)
        </span><span style="color: #0000ff">return</span><span style="color: #000000">;

    Point currentPosition </span><span style="color: #000000">=</span><span style="color: #000000"> e.GetPosition(drawingSurface);
    camera.InertialAlpha </span><span style="color: #000000">+=</span><span style="color: #000000"> (</span><span style="color: #0000ff">float</span><span style="color: #000000">)(currentPosition.X </span><span style="color: #000000">-</span><span style="color: #000000"> startPosition.X) </span><span style="color: #000000">*</span><span style="color: #000000"> camera.AngularSpeed;
    camera.InertialBeta </span><span style="color: #000000">-=</span><span style="color: #000000"> (</span><span style="color: #0000ff">float</span><span style="color: #000000">)(currentPosition.Y </span><span style="color: #000000">-</span><span style="color: #000000"> startPosition.Y) </span><span style="color: #000000">*</span><span style="color: #000000"> camera.AngularSpeed;


    startPosition </span><span style="color: #000000">=</span><span style="color: #000000"> currentPosition;
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> LayoutRoot_MouseLeftButtonDown(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, System.Windows.Input.MouseButtonEventArgs e)
{
    mouseLeftDown </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">true</span><span style="color: #000000">;
    startPosition </span><span style="color: #000000">=</span><span style="color: #000000"> e.GetPosition(drawingSurface);
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> LayoutRoot_MouseLeftButtonUp(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, System.Windows.Input.MouseButtonEventArgs e)
{
    mouseLeftDown </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">false</span><span style="color: #000000">;
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> LayoutRoot_MouseEnter(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, System.Windows.Input.MouseEventArgs e)
{
    mouseLeftDown </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">false</span><span style="color: #000000">;
}

</span><span style="color: #0000ff">private</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span><span style="color: #000000"> LayoutRoot_MouseWheel(</span><span style="color: #0000ff">object</span><span style="color: #000000"> sender, System.Windows.Input.MouseWheelEventArgs e)
{
    camera.Radius </span><span style="color: #000000">-=</span><span style="color: #000000"> e.Delta </span><span style="color: #000000">*</span><span style="color: #000000"> camera.Radius </span><span style="color: #000000">/</span><span style="color: #000000"> </span><span style="color: #800080">1000.0f</span><span style="color: #000000">;
}

}

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <h1>
    Model/ModelMesh/ModelMeshPart classes
  </h1>

  <p>
    The Model class represents a set of meshes defined by ModelMesh classes. A ModelMesh is a set of ModelMeshPart where each part has its own Effect instance.
  </p>

  <p>
    The ModelMesh contains the vertex and index buffer. Each part will be drawn by using a segment of the ModelMesh index buffer. So for rendering a Model, you just have to call the following code:
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
    <pre style="background-color: white; width: 789px; height: 304px; overflow: auto"><div>

e.GraphicsDevice.BlendState = BlendState.AlphaBlend;
e.GraphicsDevice.DepthStencilState
= DepthStencilState.Default;
e.GraphicsDevice.RasterizerState
= RasterizerState.CullNone;

e.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, new Color(, , , ), 1.0f, );

foreach (ModelMesh mesh in model.Meshes)
{
foreach (BasicEffect basicEffect in mesh.Effects)
{
basicEffect.World
= Matrix.Identity;
basicEffect.View
= camera.View;
basicEffect.Projection
= camera.Projection;
basicEffect.LightPosition
= camera.Position;
basicEffect.CameraPosition
= camera.Position;
}
}

model.Draw();

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <p>
    To create a complete Model, you have to fill the object model with data such as vertices, indices and definitions of each part. When an instance is ready, you have to call Freeze() method to close it. Only frozen objects can be drawn :
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
    <pre style="background-color: white; width: 789px; height: 436px; overflow: auto"><div>

// Model
Model result = new Model();

// ModelMesh(s)
currentMesh = new ModelMesh<VertexPositionNormalTexture>(device,
(importationOptions
& ImportationOptions.Optimize) == ImportationOptions.Optimize);
result.AddMesh(currentMesh);

result.AddVertex(..); // Many times
result.AddIndex(…); // Many times too

// ModelMeshPart(s)
currentPart = new ModelMeshPart(device);
currentPart.Effect
= new BasicEffect(device);

currentMesh.AddPart(currentPart);

currentPart.PrimitivesCount = …;
currentPart.NumVertices
= …;
currentPart.StartIndex
= …;
currentPart.MinVertexIndex
= …;

currentPart.Freeze();

currentMesh.Freeze();

result.Freeze();


<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <h1>
    Importation system
  </h1>

  <p>
    The importation system define IModelImporter:
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
    <pre style="background-color: white; width: 789px; height: 131px; overflow: auto"><div>

public interface IModelImporter
{
event Action<Model> OnImportCompleted;
event Action<int> OnImportProgressChanged;

</span><span style="color: #0000ff">void</span><span style="color: #000000"> ImportAsync(Stream source, Func</span><span style="color: #000000">&lt;</span><span style="color: #0000ff">string</span><span style="color: #000000">, Stream</span><span style="color: #000000">&gt;</span><span style="color: #000000"> resourceLocator, GraphicsDevice device, 
     ImportationOptions importationOptions </span><span style="color: #000000">=</span><span style="color: #000000"> ImportationOptions.None);

}

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <p>
    An importer must implement the previous interface.
  </p>

  <p>
    Babylon.Toolkit provides a sample Wavefront OBJ importer :
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" class="wlWriterSmartContent">
    <pre style="background-color: white; width: 789px; height: 255px; overflow: auto"><div>

Stream objStream = Application.GetResourceStream(new Uri(/Babylon.Toolbox.Sample;component/Models/temp.obj,
UriKind.Relative)).Stream;
ObjImporter importer
= new ObjImporter();

importer.OnImportCompleted += m =>
{

};

// Report loading progress
importer.OnImportProgressChanged += p =>
{

};

importer.ImportAsync(objStream, GetMaterialStream, device, ImportationOptions.Optimize);

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <h1>
    States Manager
  </h1>

  <p>
    Babylon.Toolkit includes a a class called StatesManager which allows developers to affect GraphicsDevice’s states without creating individual objects. A cache system handles duplicates and provides great performances when setting states :
  </p>

  <div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:f9179a28-64a3-4238-aab9-b9d5ad6a4b5c" class="wlWriterEditableSmartContent">
    <pre style=" width: 789px; height: 116px;background-color:White;overflow: auto;"><div>

statesManager = new StatesManager(e.GraphicsDevice);

// States
statesManager.DepthBufferEnable = true;
statesManager.CullMode
= CullMode.None;
statesManager.ApplyDepthStencilState();
statesManager.ApplyRasterizerState();

<p>
  <!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  https://dunnhq.com --></div> 

  <p>
    States are built on demand when you call ApplyXXXX().
  </p>

  <h1>
    Conclusion
  </h1>

  <p>
    Feel free to use Babylon.Toolkit in your own project. And do not hesitate to contact me if you have any questions <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Sourire" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/44/73/metablogapi/0410.wlEmoticon-smile_72ABC529.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-44-73-metablogapi/0410.wlEmoticon_2D00_smile_5F00_72ABC529.png" />
  </p>

Markup extensions in Silverlight 5

(This post is a translation of a part of my original article about the new features of Sivlerlight 5 beta 1).

In XAML, the content of the attributes is by default considered as a constant to assign to the target property.

Thus the following code will put the value “hello” in the Text property of the TextBlock:

<TextBlock Text="hello" />


The content of an attribute might pass through an IValueConverter to convert it in the correct format. For instance, here, the string “red” will be converted to a SolidColorBrush:

<Rectangle Fill="Red"/>


However, the notion of “markup extension” has been added to make the content of attributes dynamic (much like the difference between a property and a data member). So for instance, we can define bindings with the “binding” markup extension:

<TextBlock Text="{Binding ElementName=lst, Path=SelectedItem.Text}"/>


In this case, the extension will pick an element of the context called “lst” and retrieve the “SelectedItem.Text” property to assign the value of field”Text”.


With markup extensions, it is also possible to use StaticResource for instance.


Therefore, Silverlight 5 introduce (inspired by WPF) capacity for the developer to make its own extensions by implementing the IMarkupExtension interface.


 


Implementation


To provide a sample, we will create the x:Static extension of WPF (which allows to get the value of a static property in a static class).


So we will develop a class that will implement IMarkupExtension by providing a [public object ProvideValue(IServiceProvider_serviceProvider)] method. In this method we need to use the IServiceProvider services to return the final value. In our case, a little bit of reflection will enable us to find what we want:

public class MyStaticExtension : IMarkupExtension<object>



{



    public string Source { get; set; }



    public string PropertyName { get; set; }






    public object ProvideValue(IServiceProvider serviceProvider)



    {



        Type type = Type.GetType(Source);






        PropertyInfo propertyInfo = type.GetProperty(PropertyName, BindingFlags.Public | BindingFlags.Static);






        return propertyInfo.GetValue(null, null);



    }



}


Here we do not use the IServiceProvider. However, it allows us to access different services such as host control, the common property or the current object.


For instance, on the following code:

<TextBlock Text="{HotSilverlight5:MyStaticExtension Source=HotSilverlight5.Tools, PropertyName=Property}"/>


It is possible in our extension to retrieve the following information:

IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;



IRootObjectProvider rootObject = serviceProvider.GetService(typeof(IRootObjectProvider)) as IRootObjectProvider;




targetProvider.TargetObject: Instance of my TextBlock.


targetProvider.TargetProperty: Text.


rootObject.RootObject: Instance of my root UserControl.


This can enable us to make rich contextual extensions.


Note that in XAML, extensions can be referenced either in summary as above or in an exploded form as here:

<TextBlock Margin="10" HorizontalAlignment="Center" FontSize="20">



     <TextBlock.Text>



          <HotSilverlight5:MyStatic Source="HotSilverlight5.Tools" PropertyName="Property"/>



     </TextBlock.Text>



</TextBlock>


In addition, it is not mandatory to use the word “Extension”. Thus we can reference our extension as a “mystaticextension” or “mystatic”.


 


Extensions for events


Extensions can also appear at the level of the events in order to choose during runtime the handler that you wish to use:

<Grid Loaded="{HotSilverlight5:MyDynamicHandlerExtension}">


And our class will then look like this:

public class MyDynamicHandlerExtension : IMarkupExtension<Delegate>



{



    void MyHandler(object sender, RoutedEventArgs e)



    {



        // Mon handler



    }






    public Delegate ProvideValue(IServiceProvider serviceProvider)



    {



        IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;



        EventInfo targetEvent = targetProvider.TargetProperty as EventInfo;






        ParameterInfo[] pars = targetEvent.GetAddMethod().GetParameters();



        Type delegateType = pars[0].ParameterType;



        MethodInfo methodInfo = GetType().GetMethod("MyHandler", BindingFlags.NonPublic | BindingFlags.Instance);






        return Delegate.CreateDelegate(delegateType, this, methodInfo);



    }



}


Use cases



  • Creation of data on the fly (like for instance a UriMaker) as a kind of IValueConverter but more powerful




  • Provide automatic translations from a key




  • Search data (such as our MyStaticExtension) to retrieve non-directly accessible informations in XAML


Dynamic properties in Silverlight 5

_(This post is a translation of a part of my original article about the new features of Sivlerlight 5 beta 1)._

This is an important feature in my point of view for Silverlight because it gives access to the opportunity to add properties at runtime to an object and above all it allows  data bindings on these properties.

Consider, for instance that you must deserialize data from a stream (XML or other). It will be possible to make a class whose properties can be added at runtime. And moreover nothing will prevent you from also have standard properties (design-time) on your class.

The only problem I see with this new technology (actually not new since it was present in.NET via ICustomTypeDescriptor and even via IDynamicMetaObjectProvider for DLR) is the complexity of implementation.

We will detail this for your convenience if you want to use it (because you worth it!).

Generally speaking, the operation is as follows: our class must implement ICustomTypeProvider which involves the creation of the GetCustomType method. This method is called by Silverlight to retrieve the actual type of the class (in place of GetType). This type when asked for the list of the properties he managed will return the actual list AND our dynamic list. This dynamic list will be stored in a dictionary in our class.

What, in terms of code will give us:

The DynamicObject class:

public class DynamicObject : ICustomTypeProvider, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        readonly Dictionary<string, object> dynamicProperties = new Dictionary<string, object>();
        MyTypeDelegator<DynamicObject> myTypeDelegator;
        public int TrueProperty { get; set; }
        public object GetPropertyValue(string key)
        {
            object value;
            return !dynamicProperties.TryGetValue(key, out value) ? null : value;
        }
        public void SetPropertyValue(string key, object value)
        {
            dynamicProperties[key] = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(key));
        }
        public Type GetCustomType()
        {
            return myTypeDelegator ?? (myTypeDelegator = new MyTypeDelegator<DynamicObject>());
        }
    }


You can see the access methods to the properties (and a real property for our tests) and especially  the implementation of GetCustomType.


This method is used to return our own implementation of the DynamicObject type (in order to add our dynamic properties):

public class MyTypeDelegator<T> : Type
    {
        public static List<MyPropertyInfo> _classProperties = new List<MyPropertyInfo>();
        public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
        {
            var properties = typeof(T).GetProperties(bindingAttr);
            return properties.Concat(_classProperties).ToArray();
        }
        protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers)
        {
            PropertyInfo propertyInfo = typeof(T).GetProperty(name, bindingAttr);
            if (propertyInfo != null)
                return propertyInfo;
            return new MyPropertyInfo(name);
        }
        // NotImplemented
        public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
        {
            throw new NotImplementedException();
        }
...


This class is responsible for redefining GetProperties and GetPropertyImpl. For the rest, in our sample, we can throw a simple NotImplementedException. It would have been more elegant to use the TypeDelegator class that allows precisely to do a delegation but for some darks reasons I can’t make it work with the beta.


Finally, we only have to provide the class MyPropertyInfo to wrap access to a dynamic property:

public class MyPropertyInfo : PropertyInfo
    {
        private readonly string name;
        public MyPropertyInfo(string name)
        {
            this.name = name;
        }
        public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
        {
            DynamicObject myObject = (DynamicObject)obj;
            return myObject.GetPropertyValue(name);
        }
        public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, CultureInfo culture)
        {
            DynamicObject myObject = (DynamicObject)obj;
            myObject.SetPropertyValue(name, value);
        }
        // NotImplemented 🙂
        public override PropertyAttributes Attributes
        {
            get { throw new NotImplementedException(); }
        }
...


This class is just a bridge to the good methods in our DynamicObject.


As you can see the implementation is a little complex but on the final version of Silverlight Microsoft should provide helpers to simplify it a little bit.


In what concerns the use in XAML (and this is where the magic operates), just use our dynamic properties as standard properties:

<Grid x:Name="LayoutRoot" Background="White">
    <StackPanel>
        <TextBlock Text="Incredible...there is a dynamic property here?"/>
        <TextBlock Text="{Binding Path=MyProperty}" />
        <TextBlock Text="{Binding Path=TrueProperty}" />
    </StackPanel>
</Grid>


The code behind:

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    DynamicObject myDynamicObject = new DynamicObject {TrueProperty = 10};
    myDynamicObject.SetPropertyValue("MyProperty", "Here I am!");
    DataContext = myDynamicObject;
}


Our DynamicObject object is therefore constructed both in the design-time (on its property TrueProperty) and in the runtime (on his property MyProperty). The binding in XAML retrieves the two without any problems!

Babylon engine on code.msdn.microsoft.com

For my first post on my new blog, I’m proud to annonce the availability of a new sample on code.msdn.microsoft.com:

https://code.msdn.microsoft.com/Babylon-3D-engine-f0404ace

You can now get the full source of Babylon, the 3D engine I wrote to demonstrate the power of Silverlight 3D.

Live example can be found here:

https://david.blob.core.windows.net/babylon/Babylon.html

In a near future, I will create a new Codeplex entry for Babylon to allow the community to develop on it.