Select ListboxItem over Index(Highlight as i would click on it)

前端 未结 1 486
你的背包
你的背包 2021-01-27 08:39

About my Project: I want to Highlight over a Search function ListboxItems trough their Index.

Current Stage:

private void Menu_Search_Click(object sender         


        
相关标签:
1条回答
  • 2021-01-27 08:57

    It can be done with Binding, DataTrigger and INotifyPropertyChanged interface implementation (the power of WPF widely used in MVVM programming pattern).

    I'll use MVVM approach for the example of what you asking for.

    Example features

    • User may type any text in TextBox and press Search button, all items containing search text will be highlighted with yellow background.

    Step 0: Helpers

    The following class needed to notifying UI about change in Property. I'll derive from in other classes and simply call OnPropertyChanged() in property setters.

    NotifyPropertyChanged.cs

    public class NotifyPropertyChanged : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
            => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    

    In MVVM developers aren't using Click Event because there's more powerful Command exists. Next class implements ICommand interface (grabbed from the documentation page linked above). It needed for easy commands implementation in the main code.

    RelayCommand.cs

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
        public void Execute(object parameter) => _execute(parameter);
    }
    

    Step 1: Data

    Person.cs

    public class Person : NotifyPropertyChanged
    {
        private bool _highlighted;
    
        public string Name { get; set; }
        public bool Highlighted
        {
            get => _highlighted;
            set
            {
                _highlighted = value;
                OnPropertyChanged();
            }
        }
    }
    

    Step 2: View Model

    View Model in MVVM is class where View (UI/XAML) may set its DataContext which required for Binding.

    MainViewModel.cs

    public class MainViewModel : NotifyPropertyChanged
    {
        private ObservableCollection<Person> _persons;
        private ICommand _searchCommand;
    
        public ObservableCollection<Person> Persons
        {
            get => _persons;
            set
            {
                _persons = value;
                OnPropertyChanged();
            }
        }
    
        // It's C# 8.0 but for earlier versions of C# use the following:
        //                               _searchCommand ?? (_searchCommand = new ...
        public ICommand SearchCommand => _searchCommand ??= new RelayCommand(parameter =>
        {
            if (parameter is string searchText)
            {
                foreach(Person person in Persons)
                {
                    person.Highlighted = searchText.Length > 0 && person.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase);    
                }
            }
        });
        // }));
    
        public MainViewModel()
        {
            // example data
            Persons = new ObservableCollection<Person>
            {
                new Person { Name = "Alex" },
                new Person { Name = "Jane" },
                new Person { Name = "Nick" },
                new Person { Name = "John" },
                new Person { Name = "Brett" },
                new Person { Name = "Peter" },
                new Person { Name = "Mike" },
                new Person { Name = "George" },
                new Person { Name = "Anthony" }
            };
        }
    }
    

    Step 3: View

    I'm providing the full markup to make everything clear. Here's DataContext setup and all the Controls.

    MainWindow.xaml

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Window.DataContext>
            <local:MainViewModel/>
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <TextBox x:Name="SearchTextBox" Width="300" Margin="5"/>
                <Button Content="Search" Margin="5" Command="{Binding SearchCommand}" CommandParameter="{Binding Text,ElementName=SearchTextBox}"/>
            </StackPanel>
            <ListBox Margin="5" Grid.Row="1" ItemsSource="{Binding Persons}">
                <ListBox.Resources>
                    <Style TargetType="ListBoxItem">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding Highlighted}" Value="True">
                                <Setter Property="Background" Value="Yellow"/>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ListBox.Resources>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Window>
    

    That's it.

    P.S. Code-behind class :)

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
    

    Updated

    In case i was wrong with understanding the question and you need an automatic selection instead of highlight, the code may be changed in the following way:

    Person.cs

    public class Person : NotifyPropertyChanged
    {
        private bool _selected;
    
        public string Name { get; set; }
        public bool Selected
        {
            get => _selected;
            set
            {
                _selected = value;
                OnPropertyChanged();
            }
        }
    }
    

    MainViewModel.cs

    public ICommand SearchCommand => _searchCommand ??= new RelayCommand(parameter =>
    {
        if (parameter is string searchText)
        {
            foreach(Person person in Persons)
            {
                person.Selected = searchText.Length > 0 && person.Name.Contains(searchText, StringComparison.InvariantCultureIgnoreCase);    
            }
        }
    });
    

    MainWindow.xaml

    <ListBox Margin="5" Grid.Row="1" ItemsSource="{Binding Persons}" SelectionMode="Extended">
        <ListBox.Resources>
            <Style TargetType="ListBoxItem">
                <Setter Property="IsSelected" Value="{Binding Selected}"/>
            </Style>
        </ListBox.Resources>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    0 讨论(0)
提交回复
热议问题