views:

627

answers:

3

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

I have Contact.Phones (EntityCollection) 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?

+1  A: 

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.

Ray Burns
A: 

Hi I'm using mvvm and have a viewmodel:

 public class GridViewModel<T> {...}

I've a datetemplate

<DataTemplate DataType="{x:Type vm:GridViewModel}">
    <vw:GridView />
</DataTemplate>

Off course, this gives an error since it should be generic. I've put your class in the same namespace (vm). I tried to use

<DataTemplate DataType="{vm:Type vm:GridViewModel{Entities.EntityBase}}">
    <vw:GridView />
</DataTemplate>

but it still states it cannot find the object. How should I write this?

I also tried with

 <DataTemplate DataType="{vm:Type  TypeName=om.EntityBase}">
    <vw:GridView />
</DataTemplate>

Then I get the error "A key for a dictionary cannot be of type 'ViewModel.TypeExtension'. Only String, TypeExtension, and StaticExtension are supported" tx Geoffrey

geoffrey
HI! Sorry to answer late, your DataTemplate needs a key.
Nick Daniels
A: 

Yep, I agree with geoffrey. Your solution doesn't compile. Microsoft says the only solution for this problem is to use ControlTemplateSelectors. Also DataTemplate doesn't have property named TargetType.

Ivan