Saturday, February 27, 2010

{x:Static} Replacement in Silverlight


So, there's no {x:Static} in Silverlight. That kind of sucks. I wrote this value converter to try to get around that. Unlike {x:Static}, you should put the whole path in the Source and avoid using the Binding.Path property. The converter should parse everything as needed. The code below doesn't support indexers of any kind, but it wouldn't be too hard to add.

You can use it in your XAML like this:
<local:BlockContainer Blocks="{Binding Source=Cubus.Game.CubusGame.Current.CurrentPlayer.Blocks, Converter={StaticResource static}}"/>

Here's the class. My apologies for the formatting.
 
public class StaticConverter : IValueConverter
{
#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is string))
{
throw new ArgumentException("Value must be of type string.");
}

try
{
string staticValue = (string)value;

// First we need to find the type
string typeName = null;
Type type = null;
int periodIndex = 0;
int lastPeriodIndex = staticValue.LastIndexOf(".");

while (type == null && periodIndex < lastPeriodIndex)
{
periodIndex = staticValue.IndexOf(".", periodIndex + 1);
typeName = staticValue.Substring(0, periodIndex);

// This bizarre block of code is how you enumerate through
// loaded assemblies in Silverlight. Normally you'd use
// AppDomain.Current.GetAssemblies() or something like that.
foreach (AssemblyPart ap in Deployment.Current.Parts)
{
System.Windows.Resources.StreamResourceInfo sri = Application.GetResourceStream(new Uri(ap.Source, UriKind.Relative));
System.Reflection.Assembly assembly = new AssemblyPart().Load(sri.Stream);
type = assembly.GetType(typeName);

if (type != null)
{
break;
}
}
}

if (type == null)
{
throw new NullReferenceException(string.Format("No type was found matching the name {0}.", typeName));
}

// Now let's find the value.
// After the type name, the first part of the path
// should be a static member. After that, the rest
// should be instance members.

object workingValue = null;
Type workingType = type;
string[] path = staticValue.Substring(periodIndex + 1, staticValue.Length - (periodIndex + 1))
.Split(new string[] { "." }, StringSplitOptions.None);

for (int i = 0; i < path.Length; i++)
{
BindingFlags flags;
if (i == 0)
{
flags = (BindingFlags.Static | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public);
}
else
{
flags = (BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public);
}

PropertyInfo propInfo = workingType.GetProperty(path[i], flags);
if (propInfo != null)
{
workingValue = propInfo.GetValue(workingValue, null);
}
else
{
FieldInfo fieldInfo = workingType.GetField(path[i], flags);
if (fieldInfo != null)
{
workingValue = fieldInfo.GetValue(workingValue);
}
else
{
throw new NullReferenceException(string.Format("No properties or fields were found matching the name {0}.", path[i]));
}
}

workingType = workingValue.GetType();
}

return workingValue;
}
catch
{
if (DesignerProperties.GetIsInDesignMode(new UserControl()))
{
return DependencyProperty.UnsetValue;
}
else
{
throw;
}
}
}

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

#endregion
}

No comments:

Post a Comment