我正在尝试在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&lt;&gt;,但这没有帮助。焦点仍然从分组列表中消失。
更新2 我的真实应用程序我在模型类中有项目,它不知道列表框。我希望有一个解决方案,可以在不紧密耦合窗口和模型类的情况下解决问题。
答案 0 :(得分:0)
这是问题的解决方案。
我修改了它以明确数据正在发生变化。可能有一个更简单的解决方案。
想法是检测Grouped ListBox
是否关注其任何项目......如果是,那么焦点将恢复到列表框....因为这就是丢失的内容
我认为它与货币和ListCollectionView
在分组模式下有关,或者它可能与UI虚拟化有关...当你在{{1}上使用分组时被禁用}(因为样式设置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));
}
}
}
}