2013-11-20 2 views
0

Я пытаюсь создать шаблон TemplateSelector, который распознает, реализует ли интерфейс интерфейс и применяет для него DataTemplate.Как добавить товар в Словарь

Я хотел бы использовать этот селектор в следующим образом:

<ListView Grid.Column="0" 
    ItemsSource="{Binding Media}" 
    SelectionMode="None"> 
    <ListView.ItemTemplateSelector> 
     <selectors:InterfaceAwareTemplateSelector> 
      <DataTemplate x:Key="IMedia"> 
       <Image Source="{Binding PreviewImage}" /> 
      </DataTemplate> 
      <DataTemplate x:Key="IDocument"> 
       <TextBlock Text="test" /> 
      </DataTemplate> 
     </selectors:InterfaceAwareTemplateSelector> 
    </ListView.ItemTemplateSelector> 
</ListView> 

Я в конечном итоге с следующей реализацией:

[ContentProperty(Name = "Items")] 
public class InterfaceAwareTemplateSelector: DataTemplateSelector { 
    public DataTemplate DefaultTemplate { get; set; } 
    public Dictionary<Type, DataTemplate> Items { get; set; } 

    public InterfaceAwareTemplateSelector() { 
     Items = new Dictionary<Type, DataTemplate>(); 
    } 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) 
    { 
     var result = (
      from t in Items 
      where t.Key.GetTypeInfo().IsAssignableFrom(item.GetType().GetTypeInfo()) 
      select t.Value).FirstOrDefault(); 


     return result ?? DefaultTemplate; 
    } 
} 

Это, конечно, не работает, в противном случае я бы не стал написать этот вопрос :) Применение расправляется с сообщением XAML не может быть разобрано:

A first chance exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in Hicron.ProductCatalog.MainUI.exe 
WinRT information: E_UNKNOWN_ERROR [Line: 47 Position: 39] 
An exception of type 'Windows.UI.Xaml.Markup.XamlParseException' occurred in Hicron.ProductCatalog.MainUI.exe but was not handled in user code 
WinRT information: E_UNKNOWN_ERROR [Line: 47 Position: 39] 
Additional information: Unspecified error 

Что случилось с т словарь? Обычно я хотел бы использовать CompositeCollection и объединить несколько источников, но этот класс отсутствует в WinRT :(

EDIT
С точки зрения фиксации проблемы словаря я изменил словарь в список пользовательских типов. Тем не менее, не может создать . пользовательский тип с типом набора из XAML я мог бы использовать строку, но чем я не могу управлять этим в коде, если я не указать полное имя типа

[ContentProperty(Name = "Items")] 
public class InterfaceAwareTemplateSelector: DataTemplateSelector { 
    public DataTemplate DefaultTemplate { get; set; } 
    public List<InterfaceAwareTemplateSelectorItem> Items { get; set; } 

    public InterfaceAwareTemplateSelector() { 
     Items = new List<InterfaceAwareTemplateSelectorItem>(); 
    } 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { 
     if (item == null) { 
      return DefaultTemplate; 
     } 

     var result = (
      from t in Items 
      where t.Type.GetTypeInfo().IsAssignableFrom(item.GetType().GetTypeInfo()) 
      select t.Template).FirstOrDefault(); 


     return result ?? DefaultTemplate; 
    } 
} 

public class InterfaceAwareTemplateSelectorItem 
{ 
    public Type Type { get; set; } 
    public DataTemplate Template { get; set; } 
} 

Соответствующую XAML:.

 // somewhere in page tag 
     xmlns:bo="using:/*long long namespace*/.BusinessObjects" 

     // somewhere in XAML file 
     <ListView Grid.Column="0" 
      ItemsSource="{Binding Media}" 
      SelectionMode="None"> 
      <ListView.ItemTemplateSelector> 
       <selectors:InterfaceAwareTemplateSelector> 
        <selectors:InterfaceAwareTemplateSelectorItem Type="bo:IMedia"> 
         <selectors:InterfaceAwareTemplateSelectorItem.Template> 
          <DataTemplate> 
           <Image Source="{Binding PreviewImage}" 
             Tapped="ImageTapped" /> 
          </DataTemplate> 
         </selectors:InterfaceAwareTemplateSelectorItem.Template> 
        </selectors:InterfaceAwareTemplateSelectorItem> 
        <selectors:InterfaceAwareTemplateSelectorItem Type="bo:IDocument"> 
         <selectors:InterfaceAwareTemplateSelectorItem.Template> 
          <DataTemplate> 
           <TextBlock Text="pa8u4mrapwu" /> 
          </DataTemplate> 
         </selectors:InterfaceAwareTemplateSelectorItem.Template> 
        </selectors:InterfaceAwareTemplateSelectorItem> 
       </selectors:InterfaceAwareTemplateSelector> 
      </ListView.ItemTemplateSelector> 
     </ListView> 
+0

Довольно умная идея. –

ответ

0

Мне, наконец, удалось это исправить. К сожалению, я не смог получить преобразование из строки в Type в XAML, поэтому мне пришлось придерживаться строк:/Не очень удобно, но, по крайней мере, работает. Это то, что я закончил с:

XAML

<ListView Grid.Column="0" 
    ItemsSource="{Binding Media}" 
    SelectionMode="None"> 
    <ListView.ItemTemplateSelector> 
     <selectors:InterfaceAwareTemplateSelector> 
      <DataTemplate x:Key="IMedia"> 
       <Image Source="{Binding PreviewImage}" Tapped="ImageTapped"/> 
      </DataTemplate> 
      <DataTemplate x:Key="IDocument"> 
       <commonItems:DocumentItemPresenter TappedCommand="{Binding DataContext.OpenDocument, ElementName=PageRoot}"/> 
      </DataTemplate> 
     </selectors:InterfaceAwareTemplateSelector> 
    </ListView.ItemTemplateSelector> 
</ListView> 

Selector сам:

[ContentProperty(Name = "Items")] 
public class InterfaceAwareTemplateSelector: DataTemplateSelector { 
    public DataTemplate DefaultTemplate { get; set; } 
    public Dictionary<string, DataTemplate> Items { get; set; } 

    public InterfaceAwareTemplateSelector() { 
     Items = new Dictionary<string, DataTemplate>(); 
    } 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { 
     if (item == null) { 
      return DefaultTemplate; 
     } 

     var result = (
      from ii in item.GetType().GetTypeInfo().ImplementedInterfaces 
      from dt in Items 
      where ii.Name == dt.Key 
      select dt.Value).FirstOrDefault(); 

     return result ?? DefaultTemplate; 
    } 
} 

Я upvoted Filip & Джерри, потому что я нашел ваши советы полезными. Спасибо вам, ребята.

0

Посмотрите, работает ли он, если вы замените Dictionary<Type, DataTemplate> на ResourceDictionary. Я уверен, что хотя бы одна из проблем заключается в том, что ключ в x:Key="IMedia" не может быть неявно преобразован в Type. Вы также можете просто использовать string в качестве типа ключа.

+0

Да, переход к типу - одна точка истории. Я не могу использовать строки вместо типов, потому что я не смогу построить тип на основе строки, если у меня нет полностью соответствующей строки. Проверьте мои изменения. – SOReader

+1

Я не думаю, что вы сможете использовать типы непосредственно в XAML. Используйте имена типов в XAML и используйте блок переключателя в коде позади и список принятых типов или используйте отражение, чтобы найти тип, который соответствует строковому значению. –

1

Хорошо, так что с помощью этого:

public interface IFake1 { } 
public interface IFake2 { } 
public class TemplateItem 
{ 
    public DataTemplate Template { get; set; } 
    public string Interface { get; set; } 
} 
public class MySelector : DataTemplateSelector 
{ 
    public List<TemplateItem> Templates { get; set; } 
} 

Я мог бы сделать это:

<GridView> 
    <GridView.ItemTemplateSelector> 
     <local:MySelector> 
      <local:MySelector.Templates> 
       <local:TemplateItem Interface="IFake1"> 
        <local:TemplateItem.Template> 
         <DataTemplate> 
          <!-- TODO --> 
         </DataTemplate> 
        </local:TemplateItem.Template> 
       </local:TemplateItem> 
       <local:TemplateItem Interface="IFake2"> 
        <local:TemplateItem.Template> 
         <DataTemplate> 
          <!-- TODO --> 
         </DataTemplate> 
        </local:TemplateItem.Template> 
       </local:TemplateItem> 
      </local:MySelector.Templates> 
     </local:MySelector> 
    </GridView.ItemTemplateSelector> 
</GridView> 

появляется ошибка в Type вы используете. Я не мог заставить это работать. Пришлось использовать String. Должно быть просто разобрать форму.

Удачи!

+0

Да, это то, что я сделал в качестве промежуточного шага к моему решению. Я также не мог получить преобразования типа string-> типа в XAML:/Спасибо за советы в любом случае – SOReader

+0

Честно говоря, я думаю, что это может быть вашим решением. –

0

Если кому-то интересно, как эта проблема была решена ниже, это окончательная реализация и пример использования.

Использование:

<GridView ....> 
     <GridView.ItemTemplateSelector> 
      <selectors:InterfaceAwareTemplateSelector> 
       <!-- ReSharper disable once Xaml.RedundantResource --> 
       <DataTemplate x:Key="INewsContainer" selectors:InterfaceAwareTemplateSelector.Priority="1"> 
        <ctrls:ItemsContainerTile Width="350" Height="350" 
         ItemTappedCommand="{Binding DataContext.OpenNewsDetails, ElementName=PageRoot}"/> 
       </DataTemplate> 
       <!-- ReSharper disable once Xaml.RedundantResource --> 
       <DataTemplate x:Key="ISimpleMaterial" selectors:InterfaceAwareTemplateSelector.Priority="0"> 
        <ctrls:GenericTile Width="350" Height="350" 
         TappedCommand="{Binding DataContext.OpenDetails, ElementName=PageRoot}" /> 
       </DataTemplate> 
      </selectors:InterfaceAwareTemplateSelector> 
     </GridView.ItemTemplateSelector> 
    .... the rest of XAML 

Приоритет должен контролировать порядок, в котором должны быть проверены datatamples. Таким образом, мы можем контролировать, что произойдет, если несколько ключей соответствуют преобразованному объекту.

Реализация:

[ContentProperty(Name = "Items")] 
public class InterfaceAwareTemplateSelector: DataTemplateSelector { 
    public DataTemplate DefaultTemplate { get; set; } 
    public Dictionary<string, DataTemplate> Items { get; set; } 

    public InterfaceAwareTemplateSelector() { 
     Items = new Dictionary<string, DataTemplate>(); 
    } 

    protected override DataTemplate SelectTemplateCore(object item, DependencyObject container) { 
     if (item == null) { 
      return DefaultTemplate; 
     } 

     var results = (
      from ii in item.GetType().GetTypeInfo().ImplementedInterfaces 
      from dt in Items 
      where ii.Name == dt.Key 
      select dt) 
      .ToArray(); 

     if (results.Length > 1) { 
      var orderedResults = 
       from r in results 
       where IsPrioritySet(r.Value) 
       orderby GetPriority(r.Value) descending 
       select r; 

      if (orderedResults.Any()) { 
       return orderedResults.First().Value; 
      } 

      throw new AmbigiousResolveTemplateFound(item.GetType(), results.Select(x => x.Key)); 
     } 
     else if (results.Length == 1) { 
      return results[0].Value; 
     } 

     return DefaultTemplate; 
    } 

    #region PriorityProperty 
    public static readonly DependencyProperty PriorityProperty = 
     DependencyProperty.RegisterAttached(
      "Priority", 
      typeof(int), 
      typeof(InterfaceAwareTemplateSelector), 
      new PropertyMetadata(0)); 

    public static int GetPriority(DependencyObject item) { 
     if (item == null) { throw new ArgumentNullException("item"); } 

     return (int)item.GetValue(PriorityProperty); 
    } 

    public static void SetPriority(DependencyObject item, int value) { 
     if (item == null) { throw new ArgumentNullException("item"); } 

     item.SetValue(PriorityProperty, value); 
    } 

    public static bool IsPrioritySet(DependencyObject item) { 
     if (item == null) { throw new ArgumentNullException("item"); } 

     var result = item.ReadLocalValue(PriorityProperty); 
     return result != DependencyProperty.UnsetValue; 
    } 
    #endregion 
} 

Надежда кто-то найдет это реализация полезным. Еще раз большое спасибо Филиппу и Джерри за вашу помощь.

Смежные вопросы