3

我正在尝试实现一个包含某种类型的项目的列表,一个会话。每个 Session 都包含一个包含 Note 类型的列表。我想在它们各自的会话标题下的列表中显示这些注释。

目前我已经尝试了两种不同的方法。第一种方法是使用ItemsControlsas ControlTemplateListBoxItems这是我在下图中使用的,也是我希望列表的样子。每个红色矩形显示一个会话,标题下方的项目是注释。那么问题是从选择中ListBox选择ItemsControls而不是每个单独的注释。

我尝试实现列表的另一种方法是为每个 Note 赋予它所属的 Session 的属性,以便GroupStyleListBox. 如果我随后将 ListBox 的 ItemsSource 设置为 Notes 列表而不是 Sessions,我将得到一个看起来像图片的列表,并且其中包含注释选择。现在的问题是我希望列表也显示不包含任何注释的会话。

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

4

2 回答 2

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>

主窗口.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使用事件处理程序更容易。

于 2012-08-07T07:36:08.567 回答
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;
                }
            }

        }


    }
}
于 2012-08-07T07:18:26.953 回答