A ListBox for strings that supports add and delete
Ok, I was quite annoyed when I could not bind to an ObservableCollection
I was sad that this does’t exist by default, so I created one. This is actually a UserControl that hosts a ListBox, but it works just like a ListBox. I can add to and delete from a bound ObservableCollection
I wanted it to support pressing delete. Right-clicking and choosing delete. Typing in a new item at the bottom and clicking Add. Or click enter after typing.
I wanted it to work with List
<UserControl x:Class="AddRemoveStringList.StringListBox" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Label Name="TitleLabel" Content="{Binding Title}" Grid.ColumnSpan="2"/> <ListBox x:Name="Box" Grid.Row="1" Grid.ColumnSpan="2" KeyDown="OnDeletePressed" ItemsSource="{Binding ItemsSource}"> <ListBox.Resources> <ContextMenu x:Key="ContextMenuDelete"> <MenuItem Header="_Delete" Click="MenuDeleteClicked" /> </ContextMenu> </ListBox.Resources> <ListBox.ItemContainerStyle> <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}"> <Setter Property="ContextMenu" Value="{DynamicResource ContextMenuDelete }"> </Setter> </Style> </ListBox.ItemContainerStyle> </ListBox> <TextBox Name="AddStringBox" Grid.Row="2" KeyDown="OnEnterPressed" /> <Button Name="AddStringButton" Content="Add" Grid.Row="2" Grid.Column="1" Click="AddClicked" MinWidth="30" /> </Grid> </UserControl>
using System.Collections.Generic; using System.Collections.Specialized; using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace AddRemoveStringList { /// <summary> /// Interaction logic for StringListBox.xaml /// </summary> public partial class StringListBox : UserControl { public StringListBox() { InitializeComponent(); Box.DataContext = this; TitleLabel.DataContext = this; } #region Events private void AddClicked(object sender, RoutedEventArgs e) { ItemsSource.Add(AddStringBox.Text); AddStringBox.Clear(); object o = this.DataContext; if (!(o is INotifyCollectionChanged)) { this.DataContext = null; this.DataContext = o; } } private void MenuDeleteClicked(object sender, RoutedEventArgs e) { ItemsSource.Remove(Box.SelectedItem.ToString()); } private void OnDeletePressed(object sender, KeyEventArgs e) { if (e.Key == Key.Delete) { MenuDeleteClicked(sender, e); } } private void OnEnterPressed(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { AddClicked(this, e); } } #endregion #region ItemsSource DependencyProperty public ICollection<string> ItemsSource { get { return (ICollection<string>)GetValue(ItemsSourceProperty); } set { SetValue(ItemsSourceProperty, value); } } public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(ICollection<string>), typeof(StringListBox), null); #endregion #region Title DependencyProperty public string Title { get { return (string)GetValue(TitleProperty); } set { SetValue(TitleProperty, value); } } public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("TitleProperty", typeof(string), typeof(StringListBox), null); #endregion } }
And here is how you use it just like you would a ListBox.
<Window x:Class="AddRemoveStringList.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:loc="clr-namespace:AddRemoveStringList" Title="MainWindow" Height="350" Width="525" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <loc:StringListBox x:Name="ListViewStringList1" ItemsSource="{Binding StringList1}" /> <ListBox x:Name="ListViewStringList2" ItemsSource="{Binding StringList2}" Grid.Row="1" /> </Grid> </Window>
For those who wonder why I used code-behind and question if this is breaking MVVM, it is not. I am creating a reusable control here. When creating a control, the goal is to have everything in one object. Everything in the code behind is related to the UI code or uses an interface. You can use this and bind to it in MVVM just like you would a ListBox.
This coede will never work. There is no Add property to ItemsSource object.
5 years ago, this code worked perfectly. Perhaps this property solves your concern:
Awesome! Thank you. I needed something quick and simple.