5

I'm creating my own UserControl and I have two different DataTemplates under the UserControl.Resources section in my XAML. I want to choose between these two datatemplates depending on the value of a property on objects displayed in a listview. I do this by creating a custom DataTemplateSelector class and overriding the SelectTemplate method which is supposed to return the DataTemplate I wish to use. However, I have no idea how to "find" my datatemplates that are located in the UserControls resource section, all the examples I've seen only fetches datatemplates from Window.Resources. In this example they fetch the current MainWindow and then use FindResource to find the DataTemplate, how do I fetch my UserControl in a similar manner?:


public override DataTemplate 
            SelectTemplate(object item, DependencyObject container)
        {
            if (item != null && item is AuctionItem)
            {
                AuctionItem auctionItem = item as AuctionItem;
                Window window = Application.Current.MainWindow;

                switch (auctionItem.SpecialFeatures)
                {
                    case SpecialFeatures.None:
                        return 
                            window.FindResource("AuctionItem_None") 
                            as DataTemplate;
                    case SpecialFeatures.Color:
                        return 
                            window.FindResource("AuctionItem_Color") 
                            as DataTemplate;
                }
            }

            return null;
        }

The example above is from here: ItemsControl.ItemTemplateSelector Property

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Joakim
  • 11,468
  • 9
  • 44
  • 50

4 Answers4

8

Try this:

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item != null && item is AuctionItem)
        {
            AuctionItem auctionItem = item as AuctionItem;

            switch (auctionItem.SpecialFeatures)
            {
                case SpecialFeatures.None:
                    return 
                        ((FrameworkElement)container).FindResource("AuctionItem_None") 
                        as DataTemplate;
                case SpecialFeatures.Color:
                    return 
                        ((FrameworkElement)container).FindResource("AuctionItem_Color") 
                        as DataTemplate;
            }
        }

        return null;
    }
scrat789
  • 2,961
  • 3
  • 30
  • 26
8

I usually instantiate my DataTemplateSelector from code behind with the UserControl as parameter in the constructor of the DataTemplateSelector, like so:

public class MyUserControl : UserControl
{
    public MyUserControl()
    {
        Resources["MyDataTemplateSelector"] = new MyDataTemplateSelector(this);
        InitializeComponent();
    }
}

public class MyDataTemplateSelector : DataTemplateSelector
{
    private MyUserControl parent;
    public MyDataTemplateSelector(MyUserControl parent)
    {
        this.parent = parent;
    }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        parent.DoStuff();
    }
}

Not the most prettiest girl in town, but it get the job done ;)

Hope this helps!

Arcturus
  • 26,677
  • 10
  • 92
  • 107
  • 1
    Is there no way to do the same thing in XAML? – Joakim May 27 '09 at 14:39
  • Ok this works fine... But it would be nice to know what the recommended way of doing this is, preferably doing it all in XAML. – Joakim May 27 '09 at 14:56
  • So true.. I too would like to know a prettier way.. but I don't think this problem can be solved with XAML actually. You will need some kind of reference in your code behind, so the easiest way is to just give directly ;) – Arcturus May 28 '09 at 07:42
  • This seems like the only way to accomplish this task in Metro as well. None of the other options work due to limitations in the Metro implementation. – SergioL Jul 20 '12 at 15:27
  • Does this create a retain cycle? – Vasiliy Kulakov Aug 20 '14 at 13:39
2
       <DataTemplate x:Key="addTemplate">
        <Button Command="{Binding Path=AddCommand}">Add</Button>
    </DataTemplate>

    <DataTemplate x:Key="editTemplate">
        <Button Command="{Binding Path=UpdateCommand}">Update</Button>
    </DataTemplate>

    <TemplateSelectors:AddEditTemplateSelector
        AddTemplate="{StaticResource addTemplate}"
        EditTemplate="{StaticResource editTemplate}"
        x:Key="addEditTemplateSelector" />

XAML only!

msfanboy
  • 5,273
  • 13
  • 69
  • 120
0

A WinRT & Windows Phone solution involves moving up the visual tree until the parent control is found:

    protected override Windows.UI.Xaml.DataTemplate SelectTemplateCore(object item, Windows.UI.Xaml.DependencyObject container)
    {
        var parent = FindParent<MyParentControlType>(container as FrameworkElement);

        if(parent != null)
        {
            if (item is Something)
                return parent.Resources["TemplateForSomething"] as DataTemplate;
            else if(item is SomethingElse)
                return parent.Resources["TemplateForSomethingElse"] as DataTemplate;
            else 
                // etc
        }
        else
        {
            return App.Current.Resources["SomeFallbackResource"] as DataTemplate;
        }
    }

    public static T FindParent<T>(FrameworkElement element) where T : FrameworkElement
    {
        FrameworkElement parent = Windows.UI.Xaml.Media.VisualTreeHelper.GetParent(element) as FrameworkElement;

        while (parent != null)
        {
            T correctlyTyped = parent as T;

            if (correctlyTyped != null)
                return correctlyTyped;
            else
                return FindParent<T>(parent);
        }

        return null;
    }

The FindParent method is based on the accepted answer here: How to get a ListView from a ListViewItem?

Community
  • 1
  • 1
Vasiliy Kulakov
  • 5,401
  • 1
  • 20
  • 15