Search code examples
c#wpfdata-bindingpath-findingcommandparameter

How to Pass CommandParameter of Outer Collection Property to a Inner Collection in WPF


Here I'm having Outer ObservableCollection of model MobileModel. The MobileModel has an inner Collection of model MobileModelInfo

I need to Pass the Property of OS as a CommandParameter which is inside the Inner Collection.

The C# Source Code is

public class MobileModel : Notify
{
    private string _brand = string.Empty;
    private ObservableCollection<MobileModelInfo> _model = new ObservableCollection<MobileModelInfo>();
    private string _os = string.Empty;

    public string Brand
    {
        get { return _brand; }
        set { _brand = value; OnPropertyChanged(); }
    }
    public ObservableCollection<MobileModelInfo> Model
    {
        get { return _model; }
        set { _model = value; OnPropertyChanged(); }
    }

    public string OS
    {
        get { return _os; }
        set { _os = value; OnPropertyChanged(); }
    }
}

public class MobileModelInfo
{
    public string Name { get; set; }
    public bool IsMobileSelected { get; set; }
}

public void GetMobile()
    {
        List<MobileModel> mList = new List<MobileModel>();
        List<MobileModelInfo> modList = new List<MobileModelInfo>();
        MobileModel mob = new MobileModel();

        modList.Clear();
        mob.Brand = "Apple";
        modList.Add(new MobileModelInfo { Name = "iPhone 4" });
        modList.Add(new MobileModelInfo { Name = "iPhone 5" });
        modList.Add(new MobileModelInfo { Name = "iPhone 6" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "IOS";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "Samsung";
        modList.Add(new MobileModelInfo { Name = "S4" });
        modList.Add(new MobileModelInfo { Name = "S5" });
        modList.Add(new MobileModelInfo { Name = "S6" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Android";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "MicroSoft";
        modList.Add(new MobileModelInfo { Name = "Lumina 9900" });
        modList.Add(new MobileModelInfo { Name = "Opera X220" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Windows";
        mList.Add(mob);

        mob = new MobileModel();
        modList.Clear();
        mob.Brand = "Sony Ericssion";
        modList.Add(new MobileModelInfo { Name = "S4" });
        modList.Add(new MobileModelInfo { Name = "S5" });
        modList.Add(new MobileModelInfo { Name = "S6" });
        mob.Model = new ObservableCollection<MobileModelInfo>(modList);
        mob.OS = "Android";
        mList.Add(mob);

        MobileList = new ObservableCollection<MobileModel>(mList);
    }

The XAML Source Code

<ListBox Name="MobieDetail" ItemsSource="{Binding MobileList}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Name="Brand" Width="200"/>
                        <ColumnDefinition Name="Mobile" Width="90"/>
                        <ColumnDefinition Name="OS" Width="120"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding Brand}" MaxWidth="195" TextWrapping="Wrap"  Grid.Column="0"/>
                <ComboBox Name="MobileName" ItemsSource="{Binding Model,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Width="220" HorizontalAlignment="Left" VerticalAlignment="Center">
                    <ComboBox.ItemTemplate>
                        <DataTemplate>
                            <CheckBox x:Name="ChkSelect" Content="{Binding Name}" IsChecked="{Binding IsMobileSelected}">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="Checked">
                                        <commandHelper:EventToCommand Command="{Binding ElementName=MobieDetail, Path=MobieDetailCommand}">
                                            <commandHelper:EventToCommand.CommandParameter>
                                                <MultiBinding>
                                                    <Binding Path="DataContext"></Binding>
                                                    <Binding ></Binding>
                                                </MultiBinding>
                                            </commandHelper:EventToCommand.CommandParameter>
                                        </commandHelper:EventToCommand>
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                            </CheckBox>
                        </DataTemplate>
                    </ComboBox.ItemTemplate>
                    <TextBlock Text="{Binding OS}" MaxWidth="195" TextWrapping="Wrap"  Grid.Column="2"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

How to Pass the Property OS within the <Binding Path="DataContext"></Binding>


Solution

  • First of all create a converter for your MultiValueBinding that will combine the two properties into one Tuple, that will be passed as a parameter to your Command:

     public class CombineTupletConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values==null)            
                return null;
            return new Tuple<object,object>(values[0],values[1]);
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    and add it to your resources:

      <Window.Resources>
        <YourNameSpace:CombineTupletConverter x:Key="CombineTupletConverter"/>
    </Window.Resources>
    

    Use that converter when multi binding. To get the Os property and the Selected MobileModelInfo, use RelativeSource Binding :

     <ComboBox Name="MobileName" ItemsSource="{Binding Model,UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Width="220" HorizontalAlignment="Left" VerticalAlignment="Center">
                            <ComboBox.ItemTemplate>
                                <DataTemplate>
                                    <CheckBox x:Name="ChkSelect" Content="{Binding Name}" IsChecked="{Binding IsMobileSelected}">
                                        <i:Interaction.Triggers>
                                            <i:EventTrigger EventName="Checked">
                                                <commandHelper:EventToCommand Command="{Binding ElementName=MobieDetail, Path=DataContext.MobieDetailCommand}">
                                                    <commandHelper:EventToCommand.CommandParameter>
                                                        <MultiBinding Converter="{StaticResource CombineTupletConverter}">
                                                            <Binding Path="DataContext" RelativeSource="{RelativeSource AncestorType={x:Type CheckBox}}"></Binding>
                                                            <Binding Path="DataContext.OS" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"></Binding>                                                            
                                                        </MultiBinding>
                                                    </commandHelper:EventToCommand.CommandParameter>
                                                </commandHelper:EventToCommand>
                                            </i:EventTrigger>
                                        </i:Interaction.Triggers>
                                    </CheckBox>
                                </DataTemplate>
                            </ComboBox.ItemTemplate>
                        </ComboBox>
    

    Your command should look something like that:

    private RelayCommand<Tuple<object,object>> _mobieDetailCommand;
        public RelayCommand<Tuple<object, object>> MobieDetailCommand
        {
            get
            {
                return _mobieDetailCommand
                    ?? (_mobieDetailCommand = new RelayCommand<Tuple<object, object>>(
                    (tuple) =>
                    {
                        var mobileInfo=tuple.Item1 as MobileModelInfo;
                        var os=tuple.Item2.ToString();                        
                        //Your logic
                    }));
            }
        }