-1

I have a little problem with multiselect and SelectedItem property. My application works in following way: When I click on a record in Listview, data from this record displays in textboxes. And now here is my problem. I want to achieve funcionality like: When I click on a record one after another I want to display data of last selected item. Unfortunately SelectedItem works only for first element. Could you help me? I attached necessary portion of code:

enter image description here

MainWindow.xaml

<ListView Name="EmployeeListView" HorizontalAlignment="Left" Height="160" Margin="0,153,0,0" VerticalAlignment="Top" Width="755" Grid.Row="1" ItemsSource="{Binding FilteredCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedEmployee, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="{Binding SelectedIndex}" IsSynchronizedWithCurrentItem="True">
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="EmployeeName" Width="150" DisplayMemberBinding="{Binding EmployeeName}" />
                    <GridViewColumn Header="EmployeeID" Width="150" DisplayMemberBinding="{Binding EmployeeID}" />
                    <GridViewColumn Header="EmployeeSalary" Width="150" DisplayMemberBinding="{Binding EmployeeSalary}" />
                    <GridViewColumn Header="EmployeeDesigner" Width="150" DisplayMemberBinding="{Binding EmployeeDesigner}" />
                    <GridViewColumn Header="EmployeeEmailID" Width="150" DisplayMemberBinding="{Binding EmployeeEmailID}" />
                </GridView>
            </ListView.View>
            <ListView.ItemContainerStyle>
                <Style TargetType="{x:Type ListViewItem}">
                    <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected, UpdateSourceTrigger=PropertyChanged}"/>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>

Employee.cs

            public IEnumerable<Employee> SelectedEmployees
            {
                get
                {
                    selectedEmployees = Employees.Where(o => o.IsSelected).ToList();
                    return selectedEmployees;
                }
                set
                {
                    selectedEmployees = value;
                    OnPropertyChanged("SelectedEmployees");
                }
            }

            public bool IsSelected
            {
                get
                {
                    //Application.Current.Dispatcher.BeginInvoke(new Action(() => MessageBox.Show("SELE")));

                    return isSelected;
                }
                set
                {
                    isSelected = value;
                    OnPropertyChanged("IsSelected");
                }
            }

            public ObservableCollection<Employee> Employees
            {
                get
                {
                    return employees;
                }
                set
                {
                    employees = value;
                    OnPropertyChanged("Employees");
                }
            }

            public ICollectionView FilteredCollection
            {
                get
                {
                    return filteredCollection;
                }
                set
                {
                filteredCollection = value;
                OnPropertyChanged("FilteredCollection");
                }
            }


            public Employee SelectedEmployee
            {
                get
                {
                    //Application.Current.Dispatcher.BeginInvoke(new Action(() => MessageBox.Show(selectedEmployee.SelectedEmployee.ToString())));
                    return selectedEmployee;
                }
                set
                {
                    selectedEmployee = value;

                    if (selectedEmployee == null)
                    {
                        ModeOfExecuting = "Searching / Adding Mode";
                        OnPropertyChanged("SelectedEmployee");
                        OnPropertyChanged("ModeOfExecuting");
                        OnPropertyChanged("FilteredCollection");
                    }

                    if (selectedEmployee != null)
                    {
                        //MessageBox.Show(Employees[SelectedIndex.GetValueOrDefault()].EmployeeName.ToString());

                        //List<Employee> FilteredCollectionList = FilteredCollection.Cast<Employee>().ToList();
                        //MessageBox.Show(FilteredCollectionList[0].EmployeeName);
                        if (selectedEmployee.EmployeeName != string.Empty)
                        {
                            RememberValueEmployeeName = selectedEmployee.EmployeeName;
                            DynamicSearchEmployeeName = RememberValueEmployeeName;
                        }
                        if (selectedEmployee.EmployeeID != null)
                        {
                            RememberValueEmployeeID = selectedEmployee.EmployeeID;
                            DynamicSearchEmployeeID = RememberValueEmployeeID;
                        }
                        if (selectedEmployee.EmployeeSalary != null)
                        {
                            RememberValueEmployeeSalary = selectedEmployee.EmployeeSalary;
                            DynamicSearchEmployeeSalary = RememberValueEmployeeSalary.ToString();
                        }
                        if (selectedEmployee.EmployeeDesigner != string.Empty)
                        {
                            RememberValueEmployeeDesigner = selectedEmployee.EmployeeDesigner;
                            DynamicSearchEmployeeDesigner = RememberValueEmployeeDesigner;
                        }
                        if (selectedEmployee.EmployeeEmailID != string.Empty)
                        {
                            RememberValueEmployeeEmailID = selectedEmployee.EmployeeEmailID;
                            DynamicSearchEmployeeEmailID = RememberValueEmployeeEmailID;
                        }

                        ModeOfExecuting = "Editing Mode";

                        OnPropertyChanged("SelectedEmployee");

                    }

                    OnPropertyChanged("SelectedEmployee");
                }
            }
  • What are you targetting: Winforms, WPF, ASP..? YOU should __always__ TAG your questions correctly so one can see it on the questions page! – TaW Dec 03 '18 at 12:11
  • I always tag my question like now... Nobody has problem with that. You are first... WPF – Adrian Przemysław Drozdowski Dec 03 '18 at 12:22
  • @AdrianPrzemysławDrozdowski Have you tried using `SelectedItems`? The order within that list should be the order of selection (except bulk selection where they get added like they are displayed) – nosale Dec 03 '18 at 13:28
  • Unfortunately SelectedItems property is not avaiable in Listview :( – Adrian Przemysław Drozdowski Dec 03 '18 at 13:54
  • @AdrianPrzemysławDrozdowski Are you using a custom `ListView`? Cause `ListView` definitely has `SelectedItems` (See https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.listview?view=netframework-4.7.2) – nosale Dec 03 '18 at 14:09
  • No, it has not. I attached a code of my Listview. I do not anything special with Listview. When I type SelectedItems in xaml file, it is underlined. – Adrian Przemysław Drozdowski Dec 03 '18 at 14:29
  • That's because SelectedItems is a read-only property. What exactly do you want to do with the last selected item? – mm8 Dec 03 '18 at 15:42
  • Scenario: I click on one of records with holding ctrl key, data displays in textboxes, i click on another record with holding ctrl key, data of this record displays in textboxes, I click again on another record with holding ctrl key, data of this record displays in textboxes etc. – Adrian Przemysław Drozdowski Dec 03 '18 at 17:03

2 Answers2

0

I played around a bit and came up with this solution.
My goal was to make the use of Binding possible.
If you discover any bug let me know.

I created an attached behavior and corresponding attached property

public static class LastSelectedItemBehavior
{
    public static readonly DependencyProperty ExposeLastSelectedItemProperty =
        DependencyProperty.RegisterAttached("ExposeLastSelectedItem", typeof(bool), typeof(LastSelectedItemBehavior),
            new PropertyMetadata(false, ExposeLastSelectedItemChanged));
    public static bool GetExposeLastSelectedItem(ListBox element)
    {
        return (bool)element.GetValue(ExposeLastSelectedItemProperty);
    }
    public static void SetExposeLastSelectedItem(ListBox element, bool value)
    {
        element.SetValue(ExposeLastSelectedItemProperty, value);
    }
    private static void ExposeLastSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is ListBox listBox && e.NewValue is bool boolValue)
        {
            if (boolValue)
            {
                listBox.SelectionChanged += ListBox_SelectedItemsChanged;
            }
            else
            {
                listBox.SelectionChanged -= ListBox_SelectedItemsChanged;
            }
        }
    }
    private static void ListBox_SelectedItemsChanged(object sender, SelectionChangedEventArgs e)
    {
        var listBox = (ListBox) sender;
        listBox.SetValue(LastSelectedItemPropertyKey, listBox.SelectedItems.Count > 0 ? listBox.SelectedItems[listBox.SelectedItems.Count - 1] : default(object));
    }

    private static readonly DependencyPropertyKey LastSelectedItemPropertyKey =
        DependencyProperty.RegisterAttachedReadOnly(
            "LastSelectedItem", 
            typeof(object), 
            typeof(LastSelectedItemBehavior),
            new PropertyMetadata(default(object)));
    public static readonly DependencyProperty LastSelectedItemProperty = LastSelectedItemPropertyKey.DependencyProperty;

    public static object GetLastSelectedItem(ListBox element)
    {
        return element.GetValue(LastSelectedItemProperty);
    }
}

On your ListView you have to set ExposeLastSelectedItem

<ListView x:Name="listView" local:LastSelectedItemBehavior.ExposeLastSelectedItem="True" />

And then you can bind like this

<ContentControl Content="{Binding ElementName=listView, Path=(local:LastSelectedItemBehavior.LastSelectedItem)}" />

Edit:

MVVM friendly solution using method from Dmitry Tashkinov

<ListView x:Name="listView" local:LastSelectedItemBehavior.ExposeLastSelectedItem="True">
    <local:DataPiping.DataPipes>
        <local:DataPipeCollection>
            <local:DataPipe 
                Source="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=(local:LastSelectedItemBehavior.LastSelectedItem)}"
                Target="{Binding Path=LastSelectedItem, Mode=OneWayToSource}"/>
        </local:DataPipeCollection>
    </local:DataPiping.DataPipes>
</ListView>
nosale
  • 808
  • 6
  • 14
  • I am very thankful for your solution but it is not written in pure MVVM architecture :( – Adrian Przemysław Drozdowski Dec 04 '18 at 08:46
  • @AdrianPrzemysławDrozdowski I am relatively new to the MVVM pattern, but I thought the solution would be fully MVVM compatible. Can you please explain why it's not? – nosale Dec 04 '18 at 10:11
  • I am newbie in MVVM pattern too :) But esence of MVVM is separate Logic from UI. Unfortunately you use View controls in code behind. You should always bind your properties to view controls (their properties). – Adrian Przemysław Drozdowski Dec 04 '18 at 11:54
  • @AdrianPrzemysławDrozdowski Thanks for your explanation. With some additional research, i think i get the point now. I added another solution which should be MVVM friendly. – nosale Dec 04 '18 at 17:27
  • Sorry for no responding for a long time but I had to read about dependency, attached properties and behaviors :) Now I am trying to use your solution to my app. – Adrian Przemysław Drozdowski Dec 06 '18 at 08:59
-1

I have solved my problem on my own. I attached portions of added code. Before all you have to do steps from this article. It is necessary to handle interaction triggers. Solution: When I select item I add its index to a list. SelectedItem property set at selected item which I chose. Then IsSelected property set at true for these Employees which indexes are in a list. I use HashSet to avoid duplicated values: When I chose second item to a list, an index of first selected item was added to a list once more time. Feel free to comment :)

enter image description here

XAML file (add to ListView control):

            <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <i:InvokeCommandAction Command="{Binding SelectionChanged}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>

Employee.cs file

        public HashSet<int> IndexesOfSelectedEmployees
        {
            get
            {
                return indexesOfSelectedEmployees;
            }
            set
            {
                indexesOfSelectedEmployees = value;
                OnPropertyChanged("IndexesOfSelectedEmployees");
            }
        }

EmployeeViewModel.cs file:

          public void OnSelectionChanged(object parameter)
          {

            if(SelectedEmployee == null)
            {
                IndexesOfSelectedEmployees.Clear();
            }
            if(SelectedEmployee != null)
            {
                foreach (Employee item in Employees)
                {
                    if (item.IsSelected == true)
                    {
                        IndexesOfSelectedEmployees.Add(SelectedIndex.GetValueOrDefault());
                    }
                }
                foreach (int itemIndexesOfSelectedEmployees in IndexesOfSelectedEmployees)
                {
                    foreach (Employee itemEmployees in Employees)
                    {
                        if (itemIndexesOfSelectedEmployees == Employees.IndexOf(itemEmployees))
                        {
                            itemEmployees.IsSelected = true;
                        }
                    }
                }
          }