简体   繁体   中英

Select ItemTemplate in value converter

I'm trying to have multiple ItemTemplates in a listview based on each items property. But I keep getting {"Error HRESULT E_FAIL has been returned from a call to a COM component."} in my value converter:

public class EquipmentTemplateConverter : IValueConverter
{
    public object Convert(object value, Type type, object parameter, string language)
    {
        switch ((EquipmentType) (int) value)
        {
            case EquipmentType.Normal:
                return Application.Current.Resources.FirstOrDefault(r => r.Key.ToString() == "EquipmentNormalTemplate");
            case EquipmentType.Upgrade:
                return Application.Current.Resources.FirstOrDefault(r => r.Key.ToString() == "EquipmentUpgradeTemplate");
            default:
                throw new ArgumentOutOfRangeException(nameof(value), value, null);
        }
    }

    public object ConvertBack(object value, Type type, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

XAML:

    <DataTemplate x:Key="EquipmentTemplate" >
        <Grid>
            <ContentControl DataContext="{Binding}" Content="{Binding}" x:Name="TheContentControl" ContentTemplate="{Binding Equipment.Type, Converter={StaticResource EquipmentTemplateConverter } }" />
        </Grid>
    </DataTemplate>

Any ideas how I can solve this?

The usual way to do this is by writing a DataTemplateSelector and assigning an instance of it to ContentControl.ContentTemplateSelector .

<DataTemplate x:Key="EquipmentTemplate" >
    <DataTemplate.Resources>
        <local:EquipmentTemplateSelector x:Key="EquipmentTemplateSelector" />
    </DataTemplate.Resources>
    <Grid>
        <ContentControl 
            DataContext="{Binding}" 
            Content="{Binding}" 
            x:Name="TheContentControl" 
            ContentTemplateSelector="{StaticResource EquipmentTemplateSelector}" 
            />
    </Grid>
</DataTemplate>

C#:

public class EquipmentTemplateSelector : DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        //  container is the container. Cast it to something you can call
        //  FindResource() on. Put in a breakpoint and use the watch window. 
        //  I'm at work with Windows 7. Shouldn't be too hard.
        var whatever = container as SomethingOrOther;

        Object resKey = null;

        //  ************************************
        //  Do stuff here to pick a resource key
        //  ************************************

        //  Application.Current.Resources is ONE resource dictionary.
        //  Use FindResource to find any resource in scope. 
        return whatever.FindResource(resKey) as DataTemplate;
    }
}

But I keep getting {"Error HRESULT E_FAIL has been returned from a call to a COM component."} in my value converter.

This error usually happens when a reference to a style or an event handler that dose not exist or is not within the context of the XAML.

You posted only the code of your converter and part of your xaml code, I can't 100% reproduce your data model and your xaml, but from your code, I think in your converter, you would like to return the specific DataTemplate , but you actually return a KeyValuePair<object, object> , resources are defined in ResourceDictionary follow the "key-value" parttern, for more information, you can refer to ResourceDictionary and XAML resource references .

Here I wrote a sample, again I didn't 100% reproduce your xaml and data model:

MainPage.xaml:

<Page.Resources>
    <local:EquipmentTemplateConverter x:Key="EquipmentTemplateConverter" />
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView ItemsSource="{x:Bind list}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ContentControl DataContext="{Binding}" Content="{Binding}" ContentTemplate="{Binding Count, Converter={StaticResource EquipmentTemplateConverter}}" />
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

code behind:

private ObservableCollection<EquipmentType> list = new ObservableCollection<EquipmentType>();

public MainPage()
{
    this.InitializeComponent();
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    list.Add(new EquipmentType { Count = 0 });
    list.Add(new EquipmentType { Count = 1 });
    list.Add(new EquipmentType { Count = 0 });
    list.Add(new EquipmentType { Count = 0 });
    list.Add(new EquipmentType { Count = 1 });
    list.Add(new EquipmentType { Count = 1 });
}

My EquipmentType class is quite simple:

public class EquipmentType
{
    public int Count { get; set; }
}

and EquipmentTemplateConverter is like this:

public class EquipmentTemplateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        switch ((int)value)
        {
            case 0:
                var a = Application.Current.Resources.FirstOrDefault(r => r.Key.ToString() == "EquipmentNormalTemplate");
                return a.Value;

            case 1:
                var b = Application.Current.Resources.FirstOrDefault(r => r.Key.ToString() == "EquipmentUpgradeTemplate");
                return b.Value;

            default:
                throw new ArgumentOutOfRangeException(nameof(value), value, null);
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Since you are using Application.Resources property in your converter, I just put the DataTemplate in the App.xaml for test:

<Application.Resources>
    <DataTemplate x:Key="EquipmentNormalTemplate">
        <Grid>
            <TextBlock Text="This is EquipmentNormalTemplate." />
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="EquipmentUpgradeTemplate">
        <Grid>
            <TextBlock Text="This is EquipmentUpgradeTemplate." />
        </Grid>
    </DataTemplate>
</Application.Resources>

But I agree with @Ed Plunkett, using DataTemplateSelector is a more common way to do this work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM