Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DataTemplate.DataType = Collection<Entity>?

Is there a way to create a data template that handles a list of items?

I have Contact.Phones (EntityCollection<Phone>) and I want the data template to handle the list - add remove edit etc.

Is there a way to set the DataType property of the DataTemplate to generic EntityCollection<Phone>?

like image 323
Shimmy Weitzhandler Avatar asked Nov 03 '09 04:11

Shimmy Weitzhandler


2 Answers

In my Emerald Data Foundation (EDF) tool I solved this by creating a more powerful MarkupExtension than x:Type that could specify generic types too. That way I could write:

 <DataTemplate TargetType="{edf:Type generic:ICollection{local:Entity}}" />

Here's what I used:

  [MarkupExtensionReturnType(typeof(Type))]
  public class TypeExtension : MarkupExtension
  {
    public TypeExtension() { }
    public TypeExtension(string typeName) { TypeName = typeName; }
    public TypeExtension(Type type) { Type = type; }

    public string TypeName { get; set; }
    public Type Type { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
      if(Type==null)
      {
        IXamlTypeResolver typeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
        if(typeResolver==null) throw new InvalidOperationException("EDF Type markup extension used without XAML context");
        if(TypeName==null) throw new InvalidOperationException("EDF Type markup extension used without Type or TypeName");
        Type = ResolveGenericTypeName(TypeName, (name) =>
        {
          Type result = typeResolver.Resolve(name);
          if(result==null) throw new Exception("EDF Type markup extension could not resolve type " + name);
          return result;
        });
      }
      return Type;
    }

    public static Type ResolveGenericTypeName(string name, Func<string, Type> resolveSimpleName)
    {
      if(name.Contains('{'))
        name = name.Replace('{', '<').Replace('}', '>');  // Note:  For convenience working with XAML, we allow {} instead of <> for generic type parameters

      if(name.Contains('<'))
      {
        var match = _genericTypeRegex.Match(name);
        if(match.Success)
        {
          Type[] typeArgs = (
            from arg in match.Groups["typeArgs"].Value.SplitOutsideParenthesis(',')
            select ResolveGenericTypeName(arg, resolveSimpleName)
            ).ToArray();
          string genericTypeName = match.Groups["genericTypeName"].Value + "`" + typeArgs.Length;
          Type genericType = resolveSimpleName(genericTypeName);
          if(genericType!=null && !typeArgs.Contains(null))
            return genericType.MakeGenericType(typeArgs);
        }
      }
      return resolveSimpleName(name);
    }
    static Regex _genericTypeRegex = new Regex(@"^(?<genericTypeName>\w+)<(?<typeArgs>\w+(,\w+)*)>$");

  }

The generic type name parsing code is in a separate method because it is also used by some other code in EDF. You could combine it all into one method.

like image 132
Ray Burns Avatar answered Nov 04 '22 20:11

Ray Burns


Wrap your generic list in a new class, which subclasses your generic list without adding anything. This makes it possible to bind to the list from XAML. This is described here: Can I specify a generic type in XAML (pre .NET 4 Framework)?

like image 3
T.J.Kjaer Avatar answered Nov 04 '22 18:11

T.J.Kjaer