2
votes

I'd like to use Caliburn.Micro conventions for the Accordion control from Silverlight and WPF Toolkits:

View:

<Grid Background="White">
    <Controls:Accordion x:Name="Items"/>
</Grid>

ViewModel:

public class ShellViewModel : IShell
{
    public ShellViewModel()
    {
        Items = new List<AccItemViewModel>
                    {
                        new AccItemViewModel
                            {
                                DisplayName = "header one", 
                                Content = "content one"
                            },
                        new AccItemViewModel
                            {
                                DisplayName = "header two", 
                                Content = "content two"
                            },
                    };
    }

    public IEnumerable<IScreen> Items { get; set; }


    public class AccItemViewModel : Screen
    {
        public string Content { get; set; }
    }

By default, Caliburn binds elements in Accordion's ItemsSource into AccordionItem headers:


I have added the convention for the Accordion's ContentTemplate:

    private void AddCustomConventions()
    {
        ConventionManager.AddElementConvention<Accordion>
        (Accordion.ItemsSourceProperty, "SelectedItem", "SelectionChanged")
        .ApplyBinding =
        (viewModelType, path, property, element, convention) =>
        {
            if (ConventionManager
            .GetElementConvention(typeof(ItemsControl))
            .ApplyBinding(viewModelType, path, property,
            element, convention))
            {
                element.SetValue(Accordion.ContentTemplateProperty,
                ConventionManager.DefaultItemTemplate);
                return true;
            }
            return false;
        };
    }

which achieved the following:

but I'd like to either bind the AccordionItem's header to DisplayName of the AccItemViewModel(IScreen) or have a Header view model property on AccItemViewModel. AccordionItem has HeaderProperty and HeaderTemplateProperty, but I can't figure out how to apply conventions to these.

2

2 Answers

2
votes

Have a look at the WPF TabControl convention under source. That should put you on the right path.

2
votes

As it appears, the Accordion is indeed extremely similar to TabControl, and the missing piece for the snippet in my question was

ConventionManager
  .ApplyHeaderTemplate(accordion, 
     ItemsControl.ItemTemplateProperty, viewModelType);

Here is the complete convention for your convenience (BTW this code can be generalized for multiple descendants of the ItemsControl):

        ConventionManager.AddElementConvention<Accordion>(
            ItemsControl.ItemsSourceProperty, 
            "ItemsSource", "SelectionChanged")
            .ApplyBinding = (
                viewModelType, path, property, element, convention) =>
            {
                if (!ConventionManager.SetBinding(
                    viewModelType, path, property, element, convention))
                    return false;

                var accordion = (Accordion)element;
                if (accordion.ContentTemplate == null
                    && property.PropertyType.IsGenericType)
                {
                    var itemType = property
                        .PropertyType
                        .GetGenericArguments()
                        .First();
                    if (!itemType.IsValueType &&
                        !typeof(string).IsAssignableFrom(itemType))
                    {
                        accordion.ContentTemplate = 
                            ConventionManager.DefaultItemTemplate;
                    }
                }

                ConventionManager.ConfigureSelectedItem(
                    element, Selector.SelectedItemProperty, 
                    viewModelType, path);

                if (string.IsNullOrEmpty(accordion.DisplayMemberPath))
                    ConventionManager.ApplyHeaderTemplate(
                        accordion, ItemsControl.ItemTemplateProperty, 
                        viewModelType);

                return true;
            };
        ConventionManager.AddElementConvention<AccordionItem>(
            ContentControl.ContentProperty, 
            "DataContext", "DataContextChanged");