uwpcomboboxbindingdatatemplateitemsource

Binding the IsSelected property of ComboBoxItem in DataTemplate UWP


I am trying to bind on property IsSelected of ComboBoxItem in order to show correct icon, if item is selected the red icon should be shown, otherwise gray for the rest of the opened list. The way how am I trying to achieve that functionality is shown in the code below.

<ComboBox
    ItemsSource="{Binding Icons}"
    SelectedItem="{Binding SelectedIcon, Mode=TwoWay}"
    Style="{ThemeResource DefaultComboBoxStyle}"
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                    <Image
                        Width="20"
                        Height="20"
                        Source="{Binding IconId, Mode=TwoWay, Converter={StaticResource IconConverter}, ConverterParameter={StaticResource True}}"
                        Visibility="{Binding Path=IsSelected,RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}" />
                    <Image
                        Width="20"
                        Height="20"
                        Source="{Binding IconId, Mode=TwoWay, Converter={StaticResource IconConverter}}"
                        Visibility="{{Binding Path=IsSelected,RelativeSource={RelativeSource Mode=TemplatedParent} Converter={StaticResource BoolToVisibilityInverted}}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

The icon id of an item is the same for both images converter will return correct image link depending on passed parameter. I have tried to get parent data context and try to use IsSelected property of comboBoxItem (SelectorItem) but it doesn't work.

I don't want to add IsSlected property in ViewModel through which I iterate, I assume that some syntax exists but I couldn't find it. I just want to get value of property IsSelected and to bind in xaml without code changing code in the ViewModel.

Something like in the code below is achieved but hard coded elements. Depending on IsSelected item property of ComboBoxItem correct icon is shown. I want the same functionality using ItemsSource and DataTemplate.

 <ComboBox
    SelectedIndex="{Binding IconType, Mode=TwoWay, Converter={StaticResource IconTypeIndexToIconConverter}}"
    Style="{ThemeResource DefaultComboBoxStyle}">
    <ComboBoxItem x:Name="SwitchIcon">
        <StackPanel Orientation="Horizontal">
                <Image
                    Source="{Binding Source=1, Converter={StaticResource IconConverter}}"
                    Style="{ThemeResource DeviceTypeImageBlockStyle}"
                    Visibility="{Binding IsSelected, ElementName=SwitchIcon, Converter={StaticResource BoolToVisibilityInverted}}" />
                <Image
                    Source="{Binding Source=1, Converter={StaticResource IconConverter}, ConverterParameter={StaticResource True}}"
                    Style="{ThemeResource DeviceTypeImageBlockStyle}"
                    Visibility="{Binding IsSelected, ElementName=SwitchIcon, Converter={StaticResource BoolToVisibilityConverter}}" />
        </StackPanel>
    </ComboBoxItem>
    <ComboBoxItem x:Name="LightDimmingIcon">
        <StackPanel Orientation="Horizontal">
                <Image
                    Source="{Binding Source=2, Converter={StaticResource IconConverter}}"
                    Style="{ThemeResource DeviceTypeImageBlockStyle}"
                    Visibility="{Binding IsSelected, ElementName=LightDimmingIcon, Converter={StaticResource BoolToVisibilityInverted}}" />
                <Image
                    Source="{Binding Source=2, Converter={StaticResource IconConverter}, ConverterParameter={StaticResource True}}"
                    Style="{ThemeResource DeviceTypeImageBlockStyle}"
                    Visibility="{Binding IsSelected, ElementName=LightDimmingIcon, Converter={StaticResource BoolToVisibilityConverter}}" />
        </StackPanel>
    </ComboBoxItem>
</ComboBox>

Any ideas? Solution?


Solution

  • Binding the IsSelected property of ComboBoxItem in DataTemplate UWP

    I'm afraid you can't bind the IsSelected property of ComboBoxItem in DataTemplate UWP. Because TemplatedParent RelativeSource is ContentPresenter but not ComboBoxItem, and you could use the follow xaml to verify.

    <ComboBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <Border DataContext="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}}">
                    <TextBlock x:Name="Print" Text="{Binding}"/>
                </Border>
                <TextBlock Text="{Binding}" />
            </StackPanel>
        </DataTemplate>
    </ComboBox.ItemTemplate>
    

    It will will display Windows.UI.Controls.ContentPresenter in Print textblock.

    I don't want to add IsSlected property in ViewModel through which I iterate, I assume that some syntax exists but I couldn't find it.

    In general, we do deal with the scenario by add IsSlected into model class. And get the select item modify the IsSlected value to effect UI. I have make complete sample below.

    Code Behind

    public sealed partial class MainPage : Page, INotifyPropertyChanged
    {
        public List<CBContent> Items { get; set; } = new List<CBContent>();
        public MainPage()
        {
            this.InitializeComponent();
            Items.Add(new CBContent { Content = "Item1", IsSelected = false });
            Items.Add(new CBContent { Content = "Item2", IsSelected = false });
            Items.Add(new CBContent { Content = "Item3", IsSelected = false });
            Items.Add(new CBContent { Content = "Item4", IsSelected = false });
            Items.Add(new CBContent { Content = "Item5", IsSelected = false });
            Items.Add(new CBContent { Content = "Item6", IsSelected = false });
            this.DataContext = this;
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        private CBContent _selectItem;
    
        private CBContent _previousSelectItem;
        public CBContent SelectItem
        {
            get
            {
                return _selectItem;
            }
            set
            {
                _selectItem = value;              
                value.IsSelected = true;
                if(_previousSelectItem != null)
                {
                    _previousSelectItem.IsSelected = false;
                }
                _previousSelectItem = _selectItem;
                OnPropertyChanged();               
            }
        }
    }
    public class CBContent : INotifyPropertyChanged
    {
        public string Content { get; set; }
        private bool _isSelect;
        public bool IsSelected
        {
            get
            {
                return _isSelect;
            }
            set
            {
                _isSelect = value;
                OnPropertyChanged();
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    Xaml

    <Grid>
        <ComboBox ItemsSource="{Binding Items, Mode=OneWay}" SelectedItem="{Binding SelectItem,Mode=TwoWay}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
    
                        <Rectangle
                            Width="20"
                            Height="20"
                            Fill="Red"
                            Visibility="{Binding IsSelected, Mode=TwoWay}"
                            />
                        <TextBlock Text="{Binding Content}" />
                    </StackPanel>
    
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>