开发者

Bind a SL4 TreeView to an IGrouping using Caliburn

I am just starting in the SL world and am trying to use the Caliburn NavigationShell as my starting point. I converted the solution to SL4 and use Caliburn from the trunk .

To create the basic navigation, I am a bit unsure (well, quite), how 开发者_JAVA技巧I can display the original StackPanel of Buttons as a collapsible Treeview.

I changed ITaskBarItem to own a simple GroupName property

public interface ITaskBarItem : IEntryPoint
{
    BitmapImage Icon { get; }
    string DisplayName { get; }
    string GroupName { get;}
}

then, I expose this in ShellViewModel to the View:

    public IEnumerable<IGrouping<string, ITaskBarItem>> TaskBarItems
    {
        get { return _taskBarItems.GroupBy(t => t.GroupName); }
    }

How can I do the xaml markup so that I get a simple hierarchy?

How can I bind Actions without the use of buttons?

> GroupName
    DisplayName
    DisplayName
    DisplayName

> GroupName
    DisplayName
    DisplayName
    DisplayName
    ...

Mind, this is MVVM, so I am not going to use code behind or events to do that...


There are a couple of difficulties here. First, here is my markup:

<ItemsControl x:Name="TaskBarItems">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Converter={StaticResource groupName}}"
                            FontWeight="Bold" />
                <ItemsControl ItemsSource="{Binding}"
                                Margin="12 0 0 0">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel>
                                <TextBlock Text="{Binding DisplayName}" />
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

and my Shell:

public class ShellViewModel : IShell
{
    readonly TaskBarItemViewModel[] taskBarItems;

    public ShellViewModel()
    {
        taskBarItems = new[]
            {
                new TaskBarItemViewModel {GroupName = "Animal", DisplayName = "Monkey"},
                new TaskBarItemViewModel {GroupName = "Animal", DisplayName = "Cat"},
                new TaskBarItemViewModel {GroupName = "Animal", DisplayName = "Dog"},
                new TaskBarItemViewModel {GroupName = "Mineral", DisplayName = "Biotite"},
                new TaskBarItemViewModel {GroupName = "Mineral", DisplayName = "Phlogopite"},
                new TaskBarItemViewModel {GroupName = "Mineral", DisplayName = "Lepidolite"},
            };
    }

    public IEnumerable<IGrouping<string, TaskBarItemViewModel>> TaskBarItems
    {
        get
        {
            return taskBarItems.GroupBy(t => t.GroupName).ToList();
        }
    }
}

Calibrun Micro (cm) will bind the itemscontrol, TaskBarItems, by convention. However, the rest won't work by convention for a couple of reasons. It's in a DataTemplate, so we would usually use Bind.Model. However, it won't work here because the type of each item in the itemscontrol is generic (IGrouping). The default conventions can't handle finding a view for that. So we provide a data template inline.

Secondly, the Key property appears to be implemented as an explicit interface. That means Silverlight can't bind to it. I made a simple converter that binds to the group and extracts the key:

public class GroupNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((IGrouping<string,TaskBarItemViewModel>)value).Key;
    }

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

Next, since the grouping is itself the IEnumerable we have to bind the ItemsSource of the nested itemscontrol directly. We can't use conventions because there is not property on IGrouping that returns the items. (Though if there was, we'd probably still have the explicit interface issue.)

Regarding your second question. You can bind an action to any event. See the documentation here: http://caliburnmicro.codeplex.com/wikipage?title=All%20About%20Actions&referringTitle=Documentation


The value converter from Christopher's answer can be generalized like this:


public class ExplicitPropertyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value == null ? null : GetPropertyValue(value, (string)parameter);
    }

    private static object GetPropertyValue(object target, string name)
    {
        return (
                from type in target.GetType().GetInterfaces()
                from prop in type.GetProperties()
                where prop.Name == name && prop.CanRead
                select prop.GetValue(target, new object[0])
            ).FirstOrDefault();
    }

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

And the usage is here:


<TextBlock
    Text="{
        Binding
        Converter={StaticResource ExplicitPropertyConverter},
        ConverterParameter=Key
    }"
    />

This converter supports any property of any interface. More info in my blog.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜