CBreeze CBreeze - 2 months ago 13
C# Question

Remove ListBoxItem on Selection

I have a ListBox (secondListBox) that is populated with strings (ToAddToLBFilterStrings). When the user clicks on an ListBoxItem I want to remove it from the ListBox. This is how I am attempting to do this;

private void OnAddToFilterLBSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var _selectedString = secondListBox.SelectedItem as string;
if (!string.IsNullOrEmpty(_selectedString))
{
if (_selectedString == "Current")
{
currentItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Subcontractors")
{
subbieItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Suppliers")
{
suppliersItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Plant Hire")
{
plantHireItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Architects")
{
architectsItem.Visibility = Visibility.Visible;
}
if (_selectedString == "QS")
{
qsItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Project Managers")
{
projectManagerItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Structural Engineers")
{
structEngItem.Visibility = Visibility.Visible;
}
if (_selectedString == "Service Engineers")
{
servEngItem.Visibility = Visibility.Visible;
}

ToAddToLBFilterStrings.Remove(_selectedString);
secondListBox.Items.Refresh();
}
}


Instead of just removing one item, this sometimes removes all the items, sometimes a random group of the items, it is not working how I would expect it to.

Answer

First of all, i want to start that you are on a completely wrong path, because since you are using WPF, you have to use the MVVM-Model and DataBindings. I created a sample application which does only one thing: "remove clicked item from a listbox". And it looks like this...

The View:

<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Test"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        >
    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <Grid>
        <ListBox ItemsSource="{Binding Names}" SelectedItem="{Binding SelectedName}">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <i:InvokeCommandAction Command="{Binding ListItemClickCommand}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </ListBox>
    </Grid>
</Window>

The ViewModel:

public class MainViewModel : BaseViewModel
{
    public ObservableCollection<string> Names { get; set; } = new ObservableCollection<string>() {"A", "B", "C"};

    #region SelectedName

    private string selectedName;

    public string SelectedName
    {
        get
        {
            return selectedName;
        }
        set
        {
            if (value != selectedName)
            {
                selectedName = value;
                NotifyPropertyChanged();
            }
        }
    }

    #endregion

    #region ListItemClickCommand

    private ICommand listItemClickCommand;

    public ICommand ListItemClickCommand
    {
        get
        {
            if (listItemClickCommand == null)
            {
                listItemClickCommand = new RelayCommand(OnListItemClick);
            }

            return listItemClickCommand;
        }
    }

    void OnListItemClick(object param)
    {
        Names.Remove(SelectedName);
    }

    #endregion
}

BaseViewModel:

public abstract class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The RelayCommand class:

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}

As you see there is not any code behind code and the view and viewmodel are completely decoupled from each other.

A side note: To use the Blend interactions, you have to add the 'System.Windows.Interactivity' to References.

That's it. And in your case because of the Visibility changes. You can use again the same pattern, so that you can bind booleans to visibilities (you need a converter though)