如何将项添加到Dictionary

时间:2013-11-20 22:49:54

标签: xaml c#-4.0 windows-runtime winrt-xaml

我正在尝试创建一个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中缺少此类:(

修改
在修复字典问题方面,我已将字典更改为自定义类型列表。仍然无法使用XAML中的Type设置创建自定义类型。除非我指定完全限定的类型名称,否则我可以使用字符串,但我无法在代码中管理它。

[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>

4 个答案:

答案 0 :(得分: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。应该很容易解析那里的形式。

祝你好运!

答案 1 :(得分:0)

如果将Dictionary<Type, DataTemplate>替换为ResourceDictionary,请查看它是否有效。我打赌至少有一个问题是x:Key="IMedia"中的密钥无法隐式转换为Type。您也可以尝试使用string作为密钥类型。

答案 2 :(得分:0)

我终于设法解决了这个问题。不幸的是我无法在XAML中从字符串转换为Type,所以我不得不坚持使用字符串:/不是很方便,但至少可以工作。这就是我最终的结果:

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>

选择器本身:

[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;
    }
}

我赞成Filip&amp;杰瑞,因为我发现你的建议很有帮助。谢谢你们。

答案 3 :(得分: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

优先级是控制应检查数据存储的顺序。这样我们就可以控制多个键与正在转换的对象匹配时会发生什么。

实施:

[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
}

希望有人会发现这个实现很有帮助。再一次,非常感谢Filip和Jerry的帮助。