none
Selecting a new item from a listbox does not de-select the existing selected item from another listbox despite sharing the same selected item property RRS feed

  • Question

  • Summary

    I'm trying to create an invoice. Each item on the invoice can have a sub collection of items. Implementing it was pretty standard, I created an item template to be used by the listbox and created text blocks to bind the listbox's itemsource's properties. Apart from text blocks, this template also houses another listbox containing the collection of sub items.

    What I wanted to do:

    I wanted to treat this main and sub listbox as one big listbox, at least in terms of the selecteditem. I want to cancel the currently selected row (either from main/sub) whenever I select a new row (still either from main/sub).

    What I've tried:

    1. First I've tried binding both the main/sub listbox's selected item to a single property but that was a failure. I think it has something to do with the main listbox's selected item getting triggered if I select an item on the sub listbox.

    2. My second option was to create two properties with the same type to be used as the main and sub's selected item and placed an instruction to cancel out the each other's value if ever the new selected value is valid.

    What's currently happening (this is the result of my 2nd try):

    1. If I select a sub row it gets selected with no problems

    2. If I select another sub row under the same parent as the previously selected row, it also works

    3. The problem is that when I select a sub row from another parent after previously selecting a sub row. What happens is that the previous row's selected item status (at least visually) does not get de-selected (it's still highlighted as green as per my is selected trigger) yet the new row gets selected as well and shows the green highlight.

    The Result

    https://gfycat.com/fearlesssnivelinganemone

    Code:

    ShopModel

    public class ShopModel
    {
        public class Sale : INotifyPropertyChangedClass
        {
            public Sale()
            {
                SaleItemMain = new ObservableCollection<SaleItem>();
            }
    
            private int id;
            public int Id
            {
                get { return id; }
                set { id = value; RaisePropertyChanged("Id"); }
            }
    
            private ObservableCollection<SaleItem> saleItemMain;
            public ObservableCollection<SaleItem> SaleItemMain
            {
                get { return saleItemMain; }
                set { saleItemMain = value; RaisePropertyChanged("SaleItemMain"); }
            }
        }
    
        public class SaleItem : INotifyPropertyChangedClass
        {
            private int id;
            private string name;
            private decimal unitPrice;
            private decimal quantity;
            private ObservableCollection<SaleItem> saleItemSub;
    
            public int Id
            {
                get { return id; }
                set { id = value; RaisePropertyChanged("Id"); }
            }
    
            public string Name
            {
                get { return name; }
                set { name = value; RaisePropertyChanged("Name"); }
            }
    
            public decimal Quantity
            {
                get { return quantity; }
                set { quantity = value;
                      RaisePropertyChanged("Quantity");
                      RaisePropertyChanged("UnitPriceAmount");
                    }
            }
    
            public decimal UnitPrice
            {
                get { return unitPrice; }
                set { unitPrice = value;
                    RaisePropertyChanged("UnitPrice");
                    RaisePropertyChanged("UnitPriceAmount");
                }
            }
    
            public decimal UnitPriceAmount
            {
                get { return Quantity * UnitPrice; }
            }
    
            public ObservableCollection<SaleItem> SaleItemSub
            {
                get { return saleItemSub; }
                set { saleItemSub = value; RaisePropertyChanged("SaleItemSub"); }
            }
        }
    }
    
    public class INotifyPropertyChangedClass : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    ShopViewModel

    public class ShopViewModel : INotifyPropertyChangedClass
    {
        public ShopViewModel()
        {
            Sale = new Sale();
    
            SaleItem mySaleItemSub1 = new SaleItem()
            {
                Id = 1,
                Name = "Sub 1",
                UnitPrice = 200,
                Quantity = 5
            };
    
            SaleItem mySaleItemSub2 = new SaleItem()
            {
                Id = 1,
                Name = "Sub 2",
                UnitPrice = 200,
                Quantity = 5
            };
    
            SaleItem mySaleItemSub3 = new SaleItem()
            {
                Id = 1,
                Name = "Sub 3",
                UnitPrice = 200,
                Quantity = 5
            };
    
            SaleItem mySaleItemSub4 = new SaleItem()
            {
                Id = 1,
                Name = "Sub 4",
                UnitPrice = 200,
                Quantity = 5
            };
    
            SaleItem mySaleItemMain1 = new SaleItem()
            {
                Id = 1,
                Name = "Main 1",
                UnitPrice = 200,
                Quantity = 5,
                SaleItemSub = new ObservableCollection<SaleItem>()
            };
    
            SaleItem mySaleItemMain2 = new SaleItem()
            {
                Id = 1,
                Name = "Main 2",
                UnitPrice = 200,
                Quantity = 5,
                SaleItemSub = new ObservableCollection<SaleItem>()
            };
    
            SaleItem mySaleItemMain3 = new SaleItem()
            {
                Id = 1,
                Name = "Main 3",
                UnitPrice = 200,
                Quantity = 5,
                SaleItemSub = new ObservableCollection<SaleItem>()
            };
    
            mySaleItemMain1.SaleItemSub.Add(mySaleItemSub1);
            mySaleItemMain1.SaleItemSub.Add(mySaleItemSub2);
            mySaleItemMain1.SaleItemSub.Add(mySaleItemSub3);
            mySaleItemMain2.SaleItemSub.Add(mySaleItemSub4);
            Sale.SaleItemMain.Add(mySaleItemMain1);
            Sale.SaleItemMain.Add(mySaleItemMain2);
            Sale.SaleItemMain.Add(mySaleItemMain3);
        }
    
        private Sale sale;
        public Sale Sale
        {
            get { return sale; }
            set { sale = value; RaisePropertyChanged("Sale"); }
        }
    
        private SaleItem selectedSaleItemMain;
        public SaleItem SelectedSaleItemMain
        {
            get { return selectedSaleItemMain; }
            set { selectedSaleItemMain = value;
                    RaisePropertyChanged("SelectedSaleItemMain");
                if (SelectedSaleItemMain != null)
                {
                    SelectedSaleItemSub = null;
                    RaisePropertyChanged("SelectedSaleItemSub");
                }
            }
        }
    
        private SaleItem selectedSaleItemSub;
        public SaleItem SelectedSaleItemSub
        {
            get { return selectedSaleItemSub; }
            set { selectedSaleItemSub = value;
                RaisePropertyChanged("SelectedSaleItemSub");
                if (SelectedSaleItemSub != null)
                {
                    SelectedSaleItemMain = null;
                    RaisePropertyChanged("SelectedSaleItemMain");
                }
            }
        }
    }


    ShopView

    public partial class ShopView : UserControl
    {
        public ShopView()
        {
            DataContext = new ShopViewModel();
            InitializeComponent();
        }
    }


    Converter (My naming scheme goes as this: (property result)_(value sent)_(summary of what happens)

    public class Visibility_SaleItemCollection_IfCountGreaterThan0VisibleElseCollapsed : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            ObservableCollection<SaleItem> saleItemCollection = value as ObservableCollection<SaleItem>;
            if (saleItemCollection != null)
                if (saleItemCollection.Count > 0)
                    return Visibility.Visible;
            return Visibility.Collapsed;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    XAML

    <ListBox 
        SelectedItem="{Binding SelectedSaleItemMain,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
        ItemsSource="{Binding Sale.SaleItemMain,UpdateSourceTrigger=PropertyChanged}" 
        ItemContainerStyle="{DynamicResource InvoiceListBoxItemMain}" 
        VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" 
        BorderThickness="0" Grid.Row="3"/>

    ListBox's ListBoxItem Style:
    <Style x:Key="InvoiceListBoxItemMain" TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="backgroundBorder" Width="Auto" Background="White" >
                        <ContentPresenter Content="{TemplateBinding Content}">
                            <ContentPresenter.ContentTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                            <ColumnDefinition Width="60" MaxWidth="60"/>
                                            <ColumnDefinition Width="70" MaxWidth="70"/>
                                            <ColumnDefinition Width="80" MaxWidth="80"/>
                                            <ColumnDefinition Width="50" MaxWidth="50"/>
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="1*" MinHeight="36"/>
                                            <RowDefinition Height="Auto"/>
                                            <RowDefinition Height="1"/>
                                        </Grid.RowDefinitions>
                                        <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="20,0,0,0" Foreground="{DynamicResource BlueGray.Dark.B}" Height="15"/>
                                        <TextBlock Grid.Column="1" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <TextBlock Grid.Column="2" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding UnitPrice, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <TextBlock Grid.Column="3" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding UnitPriceAmount, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <Button local:Extensions.PathData="{DynamicResource TrashCan.1}" local:Extensions.PathWidth="8" Grid.Column="4" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Width="22" Style="{DynamicResource StandardButtonStyle}" Foreground="{DynamicResource Red.Default.B}" Height="22" FontSize="10"/>
                                        <ListBox Visibility="{Binding Path=ItemSource,RelativeSource={RelativeSource AncestorType=ListBox},Converter={StaticResource Visibility_SaleItemCollection_IfCountGreaterThan0VisibleElseCollapsed}}" 
                                                 SelectedItem="{Binding DataContext.SelectedSaleItemSub,RelativeSource={RelativeSource AncestorType=ListBox},UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" 
                                                 BorderThickness="0" Grid.ColumnSpan="5" Grid.Row="1" 
                                                 ItemContainerStyle="{DynamicResource InvoiceListBoxItemSub}" 
                                                 ItemsSource="{Binding SaleItemSub,UpdateSourceTrigger=PropertyChanged}" 
                                                 VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" Margin="-1,0" />
                                        <Rectangle Fill="{DynamicResource Gray.Default.B}" Grid.ColumnSpan="5" Grid.Row="2"/>
                                    </Grid>
                                </DataTemplate>
                            </ContentPresenter.ContentTemplate>
                        </ContentPresenter>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="backgroundBorder" Property="Background" Value="{DynamicResource Green.Light.B}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    
    <Style x:Key="InvoiceListBoxItemSub" TargetType="ListBoxItem">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListBoxItem}">
                    <Border x:Name="backgroundBorder" Width="Auto" Background="{DynamicResource LightGray.Default.B}">
                        <ContentPresenter Content="{TemplateBinding Content}">
                            <ContentPresenter.ContentTemplate>
                                <DataTemplate>
                                    <Grid>
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="2*"/>
                                            <ColumnDefinition Width="60" MaxWidth="60"/>
                                            <ColumnDefinition Width="70" MaxWidth="70"/>
                                            <ColumnDefinition Width="80" MaxWidth="80"/>
                                            <ColumnDefinition Width="50" MaxWidth="50"/>
                                        </Grid.ColumnDefinitions>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="Auto" MinHeight="36"/>
                                        </Grid.RowDefinitions>
                                        <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Margin="35,0,0,0" Foreground="{DynamicResource BlueGray.Dark.B}" Height="15"/>
                                        <TextBlock Grid.Column="1" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding Quantity, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <TextBlock Grid.Column="2" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding UnitPrice, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <TextBlock Grid.Column="3" HorizontalAlignment="Right" TextWrapping="Wrap" Text="{Binding UnitPriceAmount, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" Foreground="{DynamicResource BlueGray.Dark.B}"/>
                                        <Button local:Extensions.PathData="{DynamicResource TrashCan.1}" local:Extensions.PathWidth="8" Grid.Column="4" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center" Width="22" Style="{DynamicResource StandardButtonStyle}" Foreground="{DynamicResource Red.Default.B}" Height="22" FontSize="10"/>
                                    </Grid>
                                </DataTemplate>
                            </ContentPresenter.ContentTemplate>
                        </ContentPresenter>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="backgroundBorder" Property="Background" Value="{DynamicResource Green.Light.B}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

    • Edited by MarkusBrown Monday, November 18, 2019 4:28 AM
    Sunday, November 17, 2019 5:32 PM

All replies