ListBox,其中每个项目包含一个List

时间:2012-08-07 06:35:12

标签: c# wpf listbox grouping

我正在尝试实现一个包含特定类型项目的列表,一个会话。每个会话都包含一个包含Note类型的列表。我想在列表中的各个Session标题下显示这些Notes。

目前我尝试了两种不同的方法。第一种方法是将ItemsControls用作ControlTemplate ListBoxItems。这是我在下面的图片中使用的,它是我希望列表看起来像。每个红色矩形显示一个Session,标题下面的项目是Notes。问题是,ListBox的选择会选择ItemsControls而不是每个单独的注释。

我尝试实现列表的另一种方法是为每个Note提供一个属性,告诉它属于哪个Session,以便在GroupStyle上使用ListBox。如果我然后将ListBox的ItemsSource设置为Notes而不是Sessions的列表,我将获得一个看起来像图片并且有选择注释的列表。现在的问题是我希望列表显示不包含任何Notes的Sessions。

有谁知道我应该用什么来实现一个带有选择的列表,并且按照我描述的方式工作?

2 个答案:

答案 0 :(得分:3)

MainWindow.xaml:

    <TreeView ItemsSource="{Binding}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type local:Session}" ItemsSource="{Binding Path=Notes}">
                <TextBlock Text="{Binding Path=Name}" />
            </HierarchicalDataTemplate>
            <DataTemplate DataType="{x:Type local:Note}">
                <Expander Header="{Binding Path=Notek}">
                    <TextBlock Foreground="Red" Text="{Binding Path=Details}" />
                </Expander>
            </DataTemplate>
        </TreeView.Resources>
    </TreeView>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        List<Session> sessions = new List<Session>();
        for (int i = 0; i < 5; i++)
        {
            List<Note> notes = new List<Note>();
            for (int j = i * 5; j < (i + 1) * 5; j++)
            {
                Note note = new Note()
                {
                    Notek = string.Format("Note {0}", j),
                    Details = string.Format("Note j = {0}{1}j*j = {2}", j, System.Environment.NewLine, j*j)
                };

                notes.Add(note);
            }
            Session session = new Session()
            {
                Name = string.Format("Session # {0}", i),
                Notes = notes
            };
            sessions.Add(session);
        }
        DataContext = sessions;
    }
}

public class Session
{
    public string Name { get; set; }
    public List<Note> Notes { get; set; }
}
public class Note
{
    public string Notek { get; set; }
    public string Details { get; set; }
}

我认为您可以根据需要设置HierarchicalDataTemplate的样式。我只是告诉你这个例子。我认为它更容易,而不是ItemsControl与事件处理程序。

答案 1 :(得分:1)

要创建答案,我将假设以下数据模型:

class Session 
{
    public IEnumerable<Note> Notes { get; }
}

class Note { }

这需要一些编码来同步列表框。我创建了一个名为'ListBoxGroup'的附加属性。具有相同组名的所有列表框只能有一个共享的选定项。这是相当多的代码,所以它在底部。

需要注意的重要事项:列表框的列表框组在最初设置后无法更改,并且不支持删除项目,不检查空值等。因此,如果您需要更改运行时的会话应该从组中删除项目,检查是否从可视树中删除了列表框等等。

首先是页面的XAML:

     xmlns:local="clr-namespace:YourApplication.YourNamespace"
    <!-- ItemsControl does not have selection -->
    <ItemsControl ItemsSource="{Binding SessionList}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <!-- Header for the session -->
                    <Border Background="Gray">
                        <TextBlock Text="{Binding Name}" />
                    </Border>
                    <!-- listbox for notes -->
                    <ListBox ItemsSource="{Binding Notes}" local:ListBoxGroup.GroupName="Group1">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <!-- Template for a single note -->
                                <TextBlock Text="{Binding Description}" />
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

下面是ListBoxGroup属性的C#代码:

public static class ListBoxGroup
{
    public static string GetGroupName(DependencyObject obj)
    {
        return (string)obj.GetValue(GroupNameProperty);
    }

    public static void SetGroupName(DependencyObject obj, string value)
    {
        obj.SetValue(GroupNameProperty, value);
    }

    // Using a DependencyProperty as the backing store for GroupName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty GroupNameProperty =
        DependencyProperty.RegisterAttached("GroupName", typeof(string), typeof(ListBoxGroup), new UIPropertyMetadata(null, ListBoxGroupChanged));

    private static Dictionary<string, List<ListBox>> _listBoxes = new Dictionary<string, List<ListBox>>();

    private static void ListBoxGroupChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        string newValue = e.NewValue as string;
        ListBox listBox = obj as ListBox;
        if (newValue == null || listBox == null) return;


        if (_listBoxes.ContainsKey(newValue))
        {
            _listBoxes[newValue].Add(listBox);
        }
        else
        {
            _listBoxes.Add(newValue, new List<ListBox>() { listBox });
        }

        listBox.SelectionChanged += new SelectionChangedEventHandler(listBox_SelectionChanged);
        listBox.PreviewKeyUp += new System.Windows.Input.KeyEventHandler(listBox_KeyUp);
    }

    static void listBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        ListBox listBox = sender as ListBox;

        if (e.Key == System.Windows.Input.Key.Up && listBox.SelectedIndex == 0)
        {
            //move to previous
            string groupName = GetGroupName(listBox);
            List<ListBox> group = _listBoxes[groupName];

            int senderIndex = group.IndexOf(listBox);
            if (senderIndex != 0)
            {
                listBox.SelectedItem = null;

                ListBox beforeSender = group[senderIndex - 1];

                int index = beforeSender.Items.Count - 1;
                beforeSender.SelectedIndex = index;

                var container = beforeSender.ItemContainerGenerator.ContainerFromIndex(index);

                (container as FrameworkElement).Focus();


            }
        }
        else if (e.Key == System.Windows.Input.Key.Down 
                    && listBox.SelectedIndex == listBox.Items.Count - 1)
        {
            //move to next
            string groupName = GetGroupName(listBox);
            List<ListBox> group = _listBoxes[groupName];

            int senderIndex = group.IndexOf(listBox);
            if (senderIndex != group.Count - 1)
            {
                listBox.SelectedItem = null;

                ListBox afterSender = group[senderIndex + 1];

                afterSender.SelectedIndex = 0;
                var container = afterSender.ItemContainerGenerator.ContainerFromIndex(0);

                (container as FrameworkElement).Focus();
            }
        }



    }

    static void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count > 0)
        {
            ListBox listBox = sender as ListBox;
            string groupName = GetGroupName(listBox);

            foreach (var item in _listBoxes[groupName])
            {
                if (item != listBox)
                {
                    item.SelectedItem = null;
                }
            }

        }


    }
}