基于MvvmCross(Xamarin.iOS)的车次查询项目UITextField跳转需求求助
Hey there! Let's walk through implementing this station selection flow for your MvvmCross Xamarin.iOS train query app. This solution follows MvvmCross's MVVM patterns to keep your code clean and maintainable:
First, we'll create two view models: one for your main query screen, and another for the station selection list.
1.1 Main Query ViewModel (e.g., TrainQueryViewModel)
This view model handles triggering navigation to the station picker and receiving the selected station to update the text field's bound property.
public class TrainQueryViewModel : MvxViewModel { private readonly IMvxNavigationService _navigationService; private string _departureStation; public string DepartureStation { get => _departureStation; set => SetProperty(ref _departureStation, value); } public IMvxCommand OpenStationPickerCommand { get; } public TrainQueryViewModel(IMvxNavigationService navigationService) { _navigationService = navigationService; OpenStationPickerCommand = new MvxAsyncCommand(OpenStationPickerAsync); } private async Task OpenStationPickerAsync() { // Navigate to station picker and await the selected station result var selectedStation = await _navigationService.Navigate<StationSelectionViewModel, string>(); if (!string.IsNullOrEmpty(selectedStation)) { DepartureStation = selectedStation; } } }
1.2 Station Selection ViewModel (e.g., StationSelectionViewModel)
This view model holds the list of stations and handles returning the selected station to the main view model.
public class StationSelectionViewModel : MvxViewModel<string> { private readonly IMvxNavigationService _navigationService; private List<string> _stations; public List<string> Stations { get => _stations; set => SetProperty(ref _stations, value); } public IMvxCommand<string> SelectStationCommand { get; } public StationSelectionViewModel(IMvxNavigationService navigationService) { _navigationService = navigationService; SelectStationCommand = new MvxAsyncCommand<string>(SelectStationAsync); // Load your actual station data here (replace with your data source) Stations = new List<string> { "Beijing South", "Shanghai Hongqiao", "Guangzhou South", "Shenzhen North" }; } private async Task SelectStationAsync(string station) { // Return the selected station to the previous view model await _navigationService.Close(this, station); } }
Now let's wire up the iOS views to work with our view models.
2.1 Main Query View (e.g., TrainQueryView)
We'll bind the UITextField to the DepartureStation property, and add a trigger to launch the station picker when the text field is tapped.
public class TrainQueryView : MvxViewController<TrainQueryViewModel> { private UITextField _departureTextField; public override void ViewDidLoad() { base.ViewDidLoad(); // Initialize text field (adjust layout constraints as needed for your app) _departureTextField = new UITextField { Frame = new CGRect(20, 100, View.Bounds.Width - 40, 40), BorderStyle = UITextBorderStyle.RoundedRect, Placeholder = "Select departure station" }; View.AddSubview(_departureTextField); // Bind text field content to view model property var bindingSet = this.CreateBindingSet<TrainQueryView, TrainQueryViewModel>(); bindingSet.Bind(_departureTextField).To(vm => vm.DepartureStation); bindingSet.Apply(); // Disable default keyboard since we're using the text field to trigger navigation _departureTextField.InputView = new UIView(); // Add tap gesture to launch station picker var tapGesture = new UITapGestureRecognizer(() => ViewModel.OpenStationPickerCommand.Execute()); _departureTextField.AddGestureRecognizer(tapGesture); // Alternative: Use EditingDidBegin event to trigger navigation _departureTextField.EditingDidBegin += (s, e) => { ViewModel.OpenStationPickerCommand.Execute(); _departureTextField.ResignFirstResponder(); }; } }
2.2 Station Selection View (e.g., StationSelectionView)
We'll use an MvxTableView to display stations, bind the list data, and handle selection via the view model command.
public class StationSelectionView : MvxViewController<StationSelectionViewModel> { private MvxTableView _stationsTableView; public override void ViewDidLoad() { base.ViewDidLoad(); Title = "Select Station"; // Initialize table view _stationsTableView = new MvxTableView(View.Bounds); _stationsTableView.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight; View.AddSubview(_stationsTableView); // Bind table view to view model data and command var bindingSet = this.CreateBindingSet<StationSelectionView, StationSelectionViewModel>(); bindingSet.Bind(_stationsTableView) .For(v => v.Source) .To(vm => vm.Stations); bindingSet.Bind(_stationsTableView) .For(v => v.SelectedItem) .To(vm => vm.SelectStationCommand); bindingSet.Apply(); // Register and configure table view cell _stationsTableView.RegisterClassForCellReuse(typeof(UITableViewCell), "StationCell"); _stationsTableView.Source = new MvxSimpleTableViewSource( _stationsTableView, "StationCell", UITableViewCellStyle.Default, nameof(UITableViewCell.TextLabel), nameof(UITableViewCell.TextLabel.Text)); } }
Don't forget to register your views and view models in your Setup.cs so MvvmCross can resolve them:
public class Setup : MvxIosSetup<App> { protected override void InitializeViewLookup() { base.InitializeViewLookup(); var viewLookup = new Dictionary<Type, Type> { { typeof(TrainQueryViewModel), typeof(TrainQueryView) }, { typeof(StationSelectionViewModel), typeof(StationSelectionView) } }; Mvx.IoCProvider.RegisterSingleton<IMvxViewsContainer>(new MvxViewsContainer(viewLookup)); } }
This implementation sticks to MvvmCross best practices:
- Uses
IMvxNavigationServicefor clean, view-model-first navigation - Leverages view model result return to pass data between screens without tight coupling
- Binds UI elements directly to view model properties/commands to keep business logic out of views
内容的提问来源于stack exchange,提问作者HoereeBauke

