2

WPFでグループ化したリストボックスを作ろうとしています。これは、WPF4 Unleashed および Web 上の他のチュートリアルで説明されているように簡単に実行できます。

XAML (共通の項目ソースを更新するためのグループ化 + ボタンがある場合とない場合の 2 つのリストを次に示します):

<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仮想化と関係があると思います... ListBox(スタイルが設定されているため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 に答える