使用 Caliburn 将 SL4 TreeView 绑定到 IGrouping

发布于 2024-08-31 04:40:40 字数 883 浏览 13 评论 0原文

我刚刚开始接触 SL 世界,并尝试使用 Caliburn NavigationShell 作为我的起点。我将解决方案转换为 SL4 并使用后备箱中的 Caliburn。

为了创建基本导航,我有点不确定(嗯,相当确定),如何将按钮的原始 StackPanel 显示为可折叠的 Treeview。

我将 ITaskBarItem 更改为拥有一个简单的 GroupName 属性

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

,然后将其在 ShellViewModel 中公开给视图:

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

如何执行 xaml 标记以便获得简单的层次结构?

如何绑定操作不使用按钮?

> GroupName
    DisplayName
    DisplayName
    DisplayName

> GroupName
    DisplayName
    DisplayName
    DisplayName
    ...

请注意,这是 MVVM,所以我不会使用代码隐藏或事件来做到这一点......

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

江心雾 2024-09-07 04:40:40

这里有一些困难。首先,这是我的标记:

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

我的 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) 将按照约定绑定项目控件、TaskBarItems。然而,由于几个原因,其余部分将无法按照惯例工作。它位于 DataTemplate 中,因此我们通常会使用 Bind.Model。但是,它在这里不起作用,因为 itemscontrol 中每个项目的类型都是通用的(IGrouping)。默认约定无法找到相应的视图。因此我们提供了一个内联数据模板。

其次,Key 属性似乎是作为显式接口实现的。这意味着 Silverlight 无法绑定到它。我制作了一个简单的转换器,它绑定到组并提取密钥:

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();
    }
}

接下来,由于分组本身就是 IEnumerable,我们必须直接绑定嵌套 itemscontrol 的 ItemsSource。我们无法使用约定,因为 IGrouping 上没有返回项目的属性。 (尽管如果有的话,我们可能仍然会遇到显式接口问题。)

关于你的第二个问题。您可以将操作绑定到任何事件。请参阅此处的文档:
http://caliburnmicro.codeplex.com/wikipage?title= All%20About%20Actions&referringTitle=文档

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

寄居者 2024-09-07 04:40:40

Christopher 的答案中的值转换器可以概括如下:


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();
    }
}

用法如下:


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

该转换器支持任何接口的任何属性。更多信息请参见我的博客

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文