7

I have a Nested ListBox. On inner listbox mouse double click event, I need to open a new window based on some logic and for that I need the inner ListBox SelectedItem and its corresponding Outer ListBox SelectedItem. How can get this in an MVVM manner?

<ListBox ItemsSource="{Binding OuterCollection}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding OuterProperty1}" />
                            <ListBox Width="200" ItemsSource="{Binding InnerCollection}">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding InnerProperty1}" />
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Things to remember:

1) There is no relation between the inner collection and outer collection item.

2) I'm using MVVMLight Toolkit and as a temporary solution I just passes inner ListBox Mouse Double Click event argument to the View model and traverse through the tree to find the Outer ListBox item. I know this is against the MVVM rules, so how can I do it in a proper MVVM manner?

Dennis Jose
  • 1,589
  • 15
  • 35

1 Answers1

1

Got one possible solution

You can use the property SelectedItem property on both the ListBox.

Here is the code which I used to solve your problem. Though I used Cinch it should not be a problem with light framework

MainWindow.xaml

<Window x:Class="TestWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{Binding OuterCollection}" SelectedItem="{Binding OuterListBoxSelectedItem}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding}" />
                    <ListBox Width="150" DataContext="{Binding DataContext, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                             ItemsSource="{Binding InnerCollection}"
                             SelectedItem="{Binding InnerListBoxSelectedItem}">
                        <i:Interaction.Triggers>
                            <i:EventTrigger EventName="MouseDoubleClick">
                                <i:InvokeCommandAction Command="{Binding TestCommand}"
                                                       CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}, AncestorLevel=2},Path=SelectedItem}"/>
                            </i:EventTrigger>
                        </i:Interaction.Triggers>
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <TextBlock Text="{Binding}" />
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace TestWPF
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new ViewModel();
        }
    }
}

Last but not the least my model

using System.Collections.ObjectModel;
using System.Windows.Input;
using Cinch;

namespace TestWPF
{
    public class ViewModel : ViewModelBase
    {
        public ICommand TestCommand { get; private set; }
        public ObservableCollection<string> OuterCollection { get; private set; }
        public string OuterListBoxSelectedItem { get; set; }
        public ObservableCollection<string> InnerCollection { get; private set; }
        public string InnerListBoxSelectedItem { get; set; }

        public ViewModel()
        {
            OuterCollection = new ObservableCollection<string> { "Outer 1", "Outer 2", "Outer 3", "Outer 4" };
            InnerCollection = new ObservableCollection<string> { "Inner 1", "Inner 2", "Inner 3" };
            TestCommand = new SimpleCommand<object, object>(Test);
            NotifyPropertyChanged("OuterCollection");
            NotifyPropertyChanged("InnerCollection");
            NotifyPropertyChanged("TestCommand");
        }
        public void Test(object o)
        {
            var a = InnerListBoxSelectedItem;
            var b = OuterListBoxSelectedItem;
            "".ToString();
        }
    }
}

I also needed to add one more reference to System.Windows.Interactivity

I hope this helps

Sandesh
  • 2,966
  • 1
  • 20
  • 34
  • "There is no relation between the inner collection and outer collection item." doesn't mean that they are different collections. It's a collection inside another collection. i.e. Each OuterCollection record consist of an InnerCollection List. Also how did u select the outer listbox item on inner listbox mouse double click event? – Dennis Jose Nov 26 '13 at 09:09
  • Never mind about the different collection I used. That was just for illustration. Okay coming to your second doubt in the `CommandParameter` you will observe that I have bound it to the First `ListBox` using the `RelativeSource`. So what this does is it looks up the Visual tree and tries to find the `ListBox` which which meets the `AncestorLevel` criteria. If it finds one then it binds to it. By the way did you execute my code? Did it work the way it required? – Sandesh Nov 26 '13 at 10:16
  • Suppose I have selected the first item in the Outer ListBox and double click on an inner ListBox item which resides inside the third item in the outer listbox. Then your ancestotype will only return the first listbox item. not the third one... – Dennis Jose Nov 26 '13 at 15:08
  • Yes I know this problem. If the inner collection in your case is unique then you are in luck. What you can do is after double clicking on the inner collection you can just add one verification using the Outer `ListBox` `SelectedItem` as a key to see if the inner selected item is indeed a part of outer selected item. – Sandesh Nov 27 '13 at 02:50