简体   繁体   中英

Handle UWP AutoSuggestionBox events the MVVM way

I am trying to create an AutoSuggestBox that allows the user to search for a specific weather station.

To handle the TextChanged event I added a binding to the respective ViewModel property in the markup:

    <AutoSuggestBox Grid.Row="1" 
                    PlaceholderText="Station" 
                    VerticalAlignment="Center" 
                    QueryIcon="Forward"
                    Width="300"
                    Height="50"
                    DisplayMemberPath="Name"
                    TextMemberPath="Name"
                    ItemsSource="{Binding Path=Stations}">

        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior EventName="TextChanged">
                <core:InvokeCommandAction Command="{Binding TextChanged}"></core:InvokeCommandAction>
            </core:EventTriggerBehavior>
        </i:Interaction.Behaviors>

    </AutoSuggestBox>

My ViewModel looks as follows:

public class StationCollectionVM : INotifyPropertyChanged
{
    private IStationManager stationManager;
    private ICommand textChanged;

    public ObservableCollection<StationVM> Stations { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public StationCollectionVM(IStationManager stationManager)
    {
        this.stationManager = stationManager;
        Stations = new ObservableCollection<StationVM>();
        LoadStations();
    }

    private async void LoadStations()
    {
        Stations.Clear();
        IEnumerable<Station> stations = await stationManager.GetAllStationsAsync();
        IEnumerator<Station> e = stations.GetEnumerator();
        while (await Task.Factory.StartNew(() => e.MoveNext()))
        {
            Stations.Add(new StationVM(stationManager, e.Current));
        }
    }

    public ICommand TextChanged
    {
        get
        {
            if (textChanged == null)
            {
                textChanged = new RelayCommand(args =>
                {
                    // ICommand.Execute(...) takes only 1 param.
                    // How do you get both the AutoSuggestBox and 
                    // AutoSuggestBoxTextChangedEventArgs param
                    // sent from the AutoSuggestBox?

                    // Filter stations based on the user input here...
                });
            }
            return textChanged;
        }
    }
}

Please note that RelayCommand is just an implementation of ICommand :

public class RelayCommand : ICommand
{
    readonly Action<object> executeAction;
    readonly Predicate<object> canExecutePredicate;

    public event EventHandler CanExecuteChanged;

    public RelayCommand(Action<object> execute)
      : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        executeAction = execute ?? throw new ArgumentNullException(nameof(execute));
        canExecutePredicate = canExecute;
    }

    public void Execute(object parameter)
    {
        executeAction(parameter);
    }

    public bool CanExecute(object parameter)
    {
        return canExecutePredicate == null ? true : canExecutePredicate(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, new EventArgs());
    }
}

How do I access both event parameters in StationCollectionVM 's TextChanged property? Also, what is the preferred way to pass the filtered station list back to the AutoSuggestBox?

If you just want to filter data based on the input value of AutoSuggestBox , then only 1 argument is sufficient. You can pass Text property of AutoSuggestBox as a CommandParamenter like below:

<AutoSuggestBox x:Name="autoSuggestBox" 
                        Grid.Row="1" 
                        PlaceholderText="Station" 
                        VerticalAlignment="Center" 
                        QueryIcon="Forward"
                        Width="300"
                        Height="50"
                        DisplayMemberPath="Name"
                        TextMemberPath="Name"
                        ItemsSource="{Binding Path=ComboBoxList}">

                <interactivity:Interaction.Behaviors>
                    <core:EventTriggerBehavior EventName="TextChanged">
                        <core:InvokeCommandAction Command="{Binding TextChanged}" CommandParameter="{Binding Text, ElementName=autoSuggestBox}"></core:InvokeCommandAction>
                    </core:EventTriggerBehavior>
                </interactivity:Interaction.Behaviors>

</AutoSuggestBox>

Also, note that you need additional property to store your actual collection which you can retrieve in case of no filter value.

Your VM:

public class StationCollectionVM : INotifyPropertyChanged
{
    private IStationManager stationManager;
    private ICommand textChanged;
    private IEnumerable<StationVM> stationsVM { get; set; }

    public ObservableCollection<StationVM> Stations { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;

    public StationCollectionVM(IStationManager stationManager)
    {
        this.stationManager = stationManager;
        Stations = new ObservableCollection<StationVM>();
        LoadStations();
    }

    private async void LoadStations()
    {
        Stations.Clear();
        IEnumerable<Station> stations = await stationManager.GetAllStationsAsync();
        IEnumerator<Station> e = stations.GetEnumerator();
        while (await Task.Factory.StartNew(() => e.MoveNext()))
        {
            stationsVM.Add(new StationVM(stationManager, e.Current));
        }
        Stations = ObservableCollection<StationVM>(stationsVM);
    }

    public ICommand TextChanged
    {
        get
        {
            if (textChanged == null)
            {
                textChanged = new RelayCommand(args =>
                {
                   if(!string.IsEmpty(args))
                   {
                     Stations = staionsVM.Where(x=>x.SomeTextProperty.StartsWith(args));
                   }
                   else
                   {
                     Stations = ObservableCollection<StationVM>(stationsVM);
                   }
                });
            }
            return textChanged;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM