简体   繁体   中英

How to get selected listbox item to update another listbox?

I'm very new to wpf and mvvm and I hope this makes sense...

I have a ListBox of CheckBox items. When I check or uncheck an item, I want to know how to fire an event or whatever that will give me the ability to add the selected items text to different ListBox.

Here's what I've done thus far:

XAML:

<ListBox ItemsSource="{Binding Target}" IsEnabled="{Binding IsControlEnabled}">
     <ListBox.ItemTemplate>
          <DataTemplate>
                <CheckBox Content="{Binding TitleName}" IsChecked="{Binding IsChecked}" />
          </DataTemplate>
     </ListBox.ItemTemplate>
</ListBox>

Main ViewModel Class:

private ObservableCollection<CheckServerItem> _target = new ObservableCollection<CheckServerItem>();

Small Class to handle checkbox events:

public class CheckServerItem : ViewModelBase
{
    private bool _isChecked { get; set; }
    private string _Title { get; set; }

    public bool IsChecked
    {
        get { return _isChecked; }
        set
        {
            _isChecked = value;
            RaisePropertyChanged("IsChecked");
        }
    }
    public string TitleName
    {
        get { return _Title; }
        set
        {
            _Title = value;
            RaisePropertyChanged("TitleName");
        }
    }
}

The checks are handled correctly by the small class, but I can't figure out how to have that class either call a method in the Main ViewModel Class that manages the other ListBox or what I should.

Thanks for the help!

Based on the answer of Filippo Vigani, you can also do the following if you only check/uncheck the checkbox by mouse,

        <ListBox.ItemTemplate>
            <DataTemplate>
                <CheckBox Content="{Binding TitleName}"
                          IsChecked="{Binding IsChecked}"
                          Command="{Binding DataContext.SelectionChangedCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                          CommandParameter="{Binding}" />
            </DataTemplate>
        </ListBox.ItemTemplate>

I would recommend binding the other ListBox's ItemsSource to the same ObservableCollection but then using a Converter to get you just the selected items. Then you don't have to mess around with attaching and detaching event handlers at all. The Converter would be:

[ValueConversion(typeof(object), typeof(object))]
public class IsCheckedConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ObservableCollection<CheckServerItem> result = new ObservableCollection<CheckServerItem>();
        foreach(CheckServerItem item in (value as ObservableCollection<CheckServerItem>))
        {
            if (item.IsChecked)
            {
                result.Add(item);
            }
        }
        return result;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return Binding.DoNothing;
    }
}

You would need to put the converter in the Resources section of your App or Window so that you can use it in your ListBox:

<this:IsCheckedConverter x:Key="MyIsCheckedConverter" />

And then your binding for the other ListBox's ItemsSource would look like this:

<ListBox ItemsSource="{Binding Target, Converter={StaticResource MyIsCheckedConverter}}>

I would suggest using an ICommand and bind it to the Checked RoutedEvent of the CheckBox using AttachedCommandBehaviour (you can get it on nuget):

Install-Package AttachedCommandBehavior

the xaml would look something like this:

...
xmlns:acb="clr-namespace:AttachedCommandBehavior;assembly=AttachedCommandBehavior"
...
<ListBox ItemsSource="{Binding Target}"
         IsEnabled="{Binding IsControlEnabled}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <CheckBox Content="{Binding TitleName}"
                      IsChecked="{Binding IsChecked}">
                <acb:CommandBehaviorCollection.Behaviors>
                    <acb:BehaviorBinding Event="Checked"
                                         Command="{Binding DataContext.SelectionChangedCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                         CommandParameter="{Binding}" />
                    <acb:BehaviorBinding Event="Unchecked"
                                         Command="{Binding DataContext.SelectionChangedCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                                         CommandParameter="{Binding}" />
                </acb:CommandBehaviorCollection.Behaviors>
            </CheckBox>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

and then in your ViewModel you would have a command that handles the check/uncheck event. You can use Prism for a class that implements ICommand which is called DelegateCommand, you can get it from nuget:

Install-Package Prism.Core

The command in your viewmodel could be something like:

private DelegateCommand<CheckServerItem> selectionChangedCommand;

    public DelegateCommand<CheckServerItem> SelectionChangedCommand
    {
        get
        {
            return this.selectionChangedCommand ?? (this.selectionChangedCommand = new DelegateCommand<CheckServerItem>((x) =>
            {
                if (x.IsChecked)
                {
                    MyOtherList.Add(x);
                } else
                {
                    MyOtherList.Remove(x);
                }
            }));
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM