如何添加一个占位符项目到ItemsControl?



如何在列表框末尾添加自定义文本而不将其添加到使用ItemsSource的apple集合?

e.g.
Listbox:
Listbox Item1-Apple
Listbox Item2-Apple
Listbox Item3-Apple.. Could be more or less Apple the last item should say "ADD NEW..."
Listbox Item4-ADD NEW...

XAML:

<Grid>
<ListBox Name="lbxFruits" Margin="0,0,70,52">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "vertical" Background="Green">
<Label>Hello</Label>
<TextBlock Text = "{Binding Price, ElementName=lbxFruits}" Width = "14" />
<TextBlock Text = "{Binding Name, ElementName=lbxFruits}" />
</StackPanel >
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Button" HorizontalAlignment="Left" Margin="690,404,0,0" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>

c#:

private void RebuildList()
{
ListBoxItem addItem = new ListBoxItem() { Content = "ADD NEW ..." };
lbxFruits.ItemsSource = apples;
}
private ObservableCollection<Fruit> apples ;
public ObservableCollection<Fruit> Apples
{
get
{
return this.apples;
}
set
{
if (value != this.apples)
{
this.apples = value;
NotifyPropertyChanged();
}
}
}

您可以使用CollectionView的占位符特性:它将自动处理占位符项的定位(例如将其固定在末尾或开头)。当在集合上迭代时,这个占位符项不会出现,因此不会污染您的数据结构。

最大的优点是,由于专注于集合视图,您不必修改现有的数据模型及其相关逻辑。

通过将IEditableCollectionView.NewItemPlaceholderPosition属性设置为NewItemPlaceholderPosition.AtBeginningNewItemPlaceholderPosition.AtEnd来启用占位符项。实现IList的常见集合(例如ObservableCollection)由实现IEditableCollectionViewListCollectionView表示。
启用占位符特性后,底层源集合的集合视图现在将包含静态CollectionView.PlaceholderItem

你可以为类型为objectCollectionView.NewItemPlaceholder创建一个专用的DataTemplate(底层类型定义为internal,因此不能被。net库的客户端代码访问)。

自定义DataTemplateSelector将识别此占位符项以返回相应的DataTemplate

占位符项目的DataTemplate包含一个Button,允许在单击时添加一个新项目(使用ICommand或事件处理程序),并显示占位符项目的文本。

FruitTemplateSelector.cs

public class FruitTemplateSelector : DataTemplateSelector
{
/* Add more template properties in case you have more data types */
public DataTemplate AppleTemplate { get; set; }
public DataTemplate PlaceholderTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container) => item switch
{
var dataItem when dataItem == CollectionView.NewItemPlaceholder => this.PlaceholderTemplate,
Apple _ => this.AppleTemplate,
_ => base.SelectTemplate(item, container),
};
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
public ObservableCollection<string> TextItems { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;

this.TextItems = new ObservableCollection<string>
{
"Item #1",
"Item #2",
"Item #3"
};
// Get the default collection view of the source collection
ICollectionView textItemsView = CollectionViewSource.GetDefaultView(this.TextItems);
// Enable the placeholder item 
// and place it at the end of the collection view
IEditableCollectionView editableCollectionView = textItemsView as IEditableCollectionView;
editableCollectionView.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtEnd;
}
private void AddNewItem_OnClick(object sender, EventArgs e)
=> this.TextItems.Add($"Item #{this.TextItems.Count + 1}";
}

MainWindow.xaml

<Window xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox ItemsSource="{Binding TextItems}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplateSelector>
<local:FruitTemplateSelector>
<local:FruitTemplateSelector.AppleTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding}" />
</DataTemplate>
</local:FruitTemplateSelector.AppleTemplate>
<local:FruitTemplateSelector.PlaceholderTemplate>
<DataTemplate>
<Button Content="Add New Item..."
Click="AddNewItem_OnClick"
Background="Orange" />
</DataTemplate>
</local:FruitTemplateSelector.PlaceholderTemplate>
</local:FruitTemplateSelector>
</ListBox.ItemTemplateSelector>
</ListBox>
</Window>

有几种方法可以实现这一点。我建议您为Fruit定义一个接口,并让ObservableCollection包含实现该接口的类实例列表。比如:

interface IFruit
{
string Name{get;set;}
string Price{get;set;}
}
class Apple:IFruit
{
string Name{get;set;}
string Price{get;set;}
}
class AddApple:IFruit
{
string Name{get;set;} = "Add New Apple";
string Price{get;set;}
}
public ObservableCollection<IFruit> Apples

然后你只需要触发你的代码来添加一个新的Apple,或者通过对OnClick事件做出反应,并确定它是否来自AddApple类类型,或者通过在AddApple中实现的接口中添加一个命令来运行添加新苹果代码,并在Apple类中做其他事情(或什么都不做)。

实际上,您可以将接口添加到Fruit类,因为我假设Apple继承自它。

abstract class Fruit : IFruit
{
public virtual string Name{get;set;}
public virtual string Price{get;set;}
}
class Apple : Fruit
{
public override string Name { get; set; } = "Apple"
public override string Price { get; set; }
}

那么您就可以为所有继承自Fruit的类型编写类似的代码。

或者你只需要:

class NewApple : Fruit
{
public override string Name{get;set;} = "Add New Apple";
public override string Price{get;set;}
}

将其放在集合中,保持代码与以前基本相同。

最新更新