2

我正在尝试制作一个在 WPF 上进行分组的列表框。这可以很容易地完成,如 WPF4 Unleashed 和 Web 上的任何其他教程中所述。

XAML(这里有两个列表,有和没有分组 + 按钮来更新它们的公共项目源):

<Window x:Class="GroupTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <ListBox Grid.Row="0" ItemsSource="{Binding Path=Items}" x:Name="_listBox1">
            <ListBox.GroupStyle>
                <x:Static Member="GroupStyle.Default" />
            </ListBox.GroupStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <ListBox Grid.Row="1" ItemsSource="{Binding Path=Items}" x:Name="_listBox2">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <Button Grid.Row="2" Content="Update Items" Click="Button_Click" Focusable="False"/>
    </Grid>
</Window>

代码(这里我在页面加载时设置分组并在单击按钮时更新/替换项目源):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace GroupTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public class Item
        {
            public string Name { get; set; }
            public bool Flag { get; set; }
        }

        private List<Item> _items;
        public List<Item> Items
        {
            get { return _items; }
            set
            {
                if (_items != value)
                {
                    _items = value;
                    NotifyPropertyChanged("Items");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            InitializeComponent();
            MakeItems();
            DataContext = this;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(_listBox1.Items);
            view.GroupDescriptions.Add(new PropertyGroupDescription("Flag"));
        }

        private void MakeItems()
        {
            _items = new List<Item>();
            _items.Add(new Item() { Name = "1", Flag = true });
            _items.Add(new Item() { Name = "2", Flag = true });
            _items.Add(new Item() { Name = "3", Flag = false });
            _items.Add(new Item() { Name = "4", Flag = true });
            _items.Add(new Item() { Name = "5", Flag = false });
        }

        private void UpdateItems()
        {
            Items = new List<Item>(_items);
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            UpdateItems();
        }

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

    }
}

它可以工作,但是有一个我无法解决的奇怪错误。每次我更新其项目源时,带有分组的列表 #1 都会失去焦点。而没有分组的列表#2保持焦点。

这是完整项目源的链接:https ://dl.dropbox.com/u/60611528/GroupTest.zip

有什么建议么?提前致谢!

更新 我尝试将 Items 设为 ObservableCollection<>,但这并没有帮助。焦点仍然从分组列表中消失。

更新 2 我的真实应用程序在模型类中有项目,它不知道列表框。我希望有一个解决方案可以在不紧密耦合窗口和模型类的情况下解决问题。

4

1 回答 1

0

这是解决问题的方法。

我已经对其进行了修改,以明确数据正在发生变化。可能有一个更简单的解决方案。

这个想法是检测 GroupedListBox是否将焦点放在其任何项目上......如果是这样,那么焦点将恢复回列表框......因为这就是丢失的内容。

我想它与货币有关,并且ListCollectionView在分组模式下,或者它可能与 UI 虚拟化有关......当您在 a 上使用 Grouping 时禁用ListBox(因为 Style sets ScrollViewer.CanContentScroll=False

(您可以查看参考源代码以更深入地了解行为)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace GroupTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public class Item
        {
            public string Name { get; set; }
            public bool Flag { get; set; }
        }

        public ObservableCollection<Item> Items { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            Items = new ObservableCollection<Item>();
            InitializeComponent();
            MakeItems();
            DataContext = this;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(_listBox1.Items);
            view.GroupDescriptions.Add(new PropertyGroupDescription("Flag"));
        }

        bool bFlip = false;

        private void MakeItems()
        {
            Items.Clear();
            if (bFlip)
            {
                Items.Add(new Item() { Name = "1", Flag = true });
                Items.Add(new Item() { Name = "2", Flag = true });
                Items.Add(new Item() { Name = "3", Flag = false });
                Items.Add(new Item() { Name = "4", Flag = true });
                Items.Add(new Item() { Name = "5", Flag = false });

                bFlip = false;
            }
            else
            {
                Items.Add(new Item() { Name = "1", Flag = true });
                Items.Add(new Item() { Name = "2", Flag = true });
                Items.Add(new Item() { Name = "3", Flag = false });
                Items.Add(new Item() { Name = "4", Flag = true });
                Items.Add(new Item() { Name = "5", Flag = false });
                Items.Add(new Item() { Name = "A", Flag = false });
                Items.Add(new Item() { Name = "B", Flag = false });

                bFlip = true;
            }
        }

        private void UpdateItems()
        {
            bool bListBox1HadFocus = false;
            IInputElement focussedelement = Keyboard.FocusedElement;
            ListBoxItem lbifocussed = focussedelement as ListBoxItem;
            Item itemwithfocus = (lbifocussed != null ? lbifocussed.Content as Item : null);

            for (int i = 0; i < Items.Count; i++)
            {
                ListBoxItem lbi = _listBox1.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;

                if (lbi == lbifocussed)
                {
                    bListBox1HadFocus = true;
                    break;
                }
            }

            Item oldselecteditem1 = _listBox1.SelectedItem as Item;
            Item oldselecteditem2 = _listBox2.SelectedItem as Item;

            MakeItems();

            // Set back the selections to what they were

            foreach (Item item in Items)
            {
                if (oldselecteditem1 != null && item.Name == oldselecteditem1.Name)
                {
                    _listBox1.SelectedItem = item;
                }
                if (oldselecteditem2 != null && item.Name == oldselecteditem2.Name)
                {
                    _listBox2.SelectedItem = item;
                }
            }

            if (bListBox1HadFocus)
            {
                _listBox1.Focus();
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            UpdateItems();
        }

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

如果您想要一个更接近原始版本的版本,那么:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace GroupTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public class Item
        {
            public string Name { get; set; }
            public bool Flag { get; set; }
        }

        private List<Item> _items;
        public List<Item> Items
        {
            get { return _items; }
            set
            {
                if (_items != value)
                {
                    _items = value;
                    NotifyPropertyChanged("Items");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MainWindow()
        {
            InitializeComponent();
            MakeItems();
            DataContext = this;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ICollectionView view = CollectionViewSource.GetDefaultView(_listBox1.Items);
            view.GroupDescriptions.Add(new PropertyGroupDescription("Flag"));
        }

        private void MakeItems()
        {
            _items = new List<Item>();
            _items.Add(new Item() { Name = "1", Flag = true });
            _items.Add(new Item() { Name = "2", Flag = true });
            _items.Add(new Item() { Name = "3", Flag = false });
            _items.Add(new Item() { Name = "4", Flag = true });
            _items.Add(new Item() { Name = "5", Flag = false });
        }

        private void UpdateItems()
        {
            bool bListBox1HadFocus = false;
            IInputElement focussedelement = Keyboard.FocusedElement;
            ListBoxItem lbifocussed = focussedelement as ListBoxItem;
            Item itemwithfocus = (lbifocussed != null ? lbifocussed.Content as Item : null);
            bool blistbox1hasfocus = (lbifocussed != null ? lbifocussed.IsFocused : false);

            for (int i = 0; i < Items.Count; i++)
            {
                ListBoxItem lbi = _listBox1.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;

                if (lbi == lbifocussed)
                {
                    bListBox1HadFocus = true;
                    break;
                }
            }

            Items = new List<Item>(_items);

            if (bListBox1HadFocus)
            {
                _listBox1.Focus();
            }
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            UpdateItems();
        }

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
于 2012-08-20T22:18:56.810 回答