Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
376 views
in Technique[技术] by (71.8m points)

silverlight 3.0 - Sync SelectedItems in a muliselect listbox with a collection in ViewModel

I have a multi-select listbox in a SL3 app using prism and I need a collection in my viewmodel that contains the currently selected items in the listbox.

The viewmodel doesn't know anything about the view so it does not have access to the listbox control. Also I need to be able to clear the selected items in the listbox from the viewmodel.

Not sure how to approach this problem

thanks Michael

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

So, assume you have a ViewModel with the following properties:

public ObservableCollection<string> AllItems { get; private set; }
public ObservableCollection<string> SelectedItems { get; private set; }

You would start by binding your AllItems collection to the ListBox:

<ListBox x:Name="MyListBox" ItemsSource="{Binding AllItems}" SelectionMode="Multiple" />

The problem is that the SelectedItems property on ListBox is not a DependencyProperty. This is pretty bad, since you can't bind it to something in your ViewModel.

The first approach is to just put this logic in the code-behind, to tweak the ViewModel:

public MainPage()
{
    InitializeComponent();

    MyListBox.SelectionChanged += ListBoxSelectionChanged;
}

private static void ListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var listBox = sender as ListBox;
    if(listBox == null) return;

    var viewModel = listBox.DataContext as MainVM;
    if(viewModel == null) return;

    viewModel.SelectedItems.Clear();

    foreach (string item in listBox.SelectedItems)
    {
        viewModel.SelectedItems.Add(item);
    }
}

This approach will work, but it is really ugly. My preferred approach is to extract this behavior into an "Attached Behavior". If you do that, you can completely eliminate your code-behind and set it up in the XAML. The bonus is that this "Attached Behavior" is now re-usable in any ListBox:

<ListBox ItemsSource="{Binding AllItems}" Demo:SelectedItems.Items="{Binding SelectedItems}" SelectionMode="Multiple" />

And here is the code for the Attached Behavior:

public static class SelectedItems
{
    private static readonly DependencyProperty SelectedItemsBehaviorProperty =
        DependencyProperty.RegisterAttached(
            "SelectedItemsBehavior",
            typeof(SelectedItemsBehavior),
            typeof(ListBox),
            null);

    public static readonly DependencyProperty ItemsProperty = DependencyProperty.RegisterAttached(
            "Items",
            typeof(IList),
            typeof(SelectedItems),
            new PropertyMetadata(null, ItemsPropertyChanged));

    public static void SetItems(ListBox listBox, IList list) { listBox.SetValue(ItemsProperty, list); }
    public static IList GetItems(ListBox listBox) { return listBox.GetValue(ItemsProperty) as IList; }

    private static void ItemsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var target = d as ListBox;
        if (target != null)
        {
            GetOrCreateBehavior(target, e.NewValue as IList);
        }
    }

    private static SelectedItemsBehavior GetOrCreateBehavior(ListBox target, IList list)
    {
        var behavior = target.GetValue(SelectedItemsBehaviorProperty) as SelectedItemsBehavior;
        if (behavior == null)
        {
            behavior = new SelectedItemsBehavior(target, list);
            target.SetValue(SelectedItemsBehaviorProperty, behavior);
        }

        return behavior;
    }
}

public class SelectedItemsBehavior
{
    private readonly ListBox _listBox;
    private readonly IList _boundList;

    public SelectedItemsBehavior(ListBox listBox, IList boundList)
    {
        _boundList = boundList;
        _listBox = listBox;
        _listBox.SelectionChanged += OnSelectionChanged;
    }

    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        _boundList.Clear();

        foreach (var item in _listBox.SelectedItems)
        {
            _boundList.Add(item);
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...