I have the following code (changed object names, so syntax/spelling errors ignore).
public class ViewModel
{
ViewModelSource m_vSource;
public ViewModel(IViewModelSource source)
{
m_vSource= source;
m_vSource.ItemArrived += new Action<Item>(m_vSource_ItemArrived);
}
void m_vSource_ItemArrived(Item obj)
{
Title = obj.Title;
Subitems = obj.items;
Description = obj.Description;
}
public void GetFeed(string serviceUrl)
{
m_vFeedSource.GetFeed(serviceUrl);
}
public string Title { get; set; }
public IEnumerable<Subitems> Subitems { get; set; }
public string Description { get; set; }
}
Here is the code I have in my page's codebehind.
ViewModel m_vViewModel;
public MainPage()
{
InitializeComponent();
m_vViewModel = new ViewModel(new ViewModelSource());
this.Loaded += new RoutedEventHandler(MainPage_Loaded);
this.DataContext = m_vViewModel;
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
m_vViewModel.GetItems("http://www.myserviceurl.com");
}
Finally, here is a sample of what my xaml looks like.
<!--TitleGrid is the name of the application and page title-->
<Grid x:Name="TitleGrid" Grid.Row="0">
<TextBlock Text="My Super Title" x:Name="textBlockPageTitle" Style="{StaticResource PhoneTextPageTitle1Style}"/>
<TextBlock Text="{Binding Path=Title}" x:Name="textBlockListTitle" Style="{StaticResource PhoneTextPageTitle2Style}"/>
</Grid>
Is there anything I'm doing wrong here?
I think your ViewModel should implement the INotifyPropertyChanged interface:
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then your property would look like that:
private title;
public string Title
{
get
{
return this.title;
}
set
{
if (this.title!= value)
{
this.title= value;
this.RaisePropertyChanged("Title");
}
}
}
Michael
Well, go figure, 10 mins after I post it, I figure it out.
I was missing the INotifyProperty implementation. Thanks if anyone is looking at this.
Related
I'm having an issue with data not binding correctly on a details page when I have clicked through from a ListView via a button. The ListView binds perfectly and the object gets passed through to the details page. The Id of the object is read and a full version of the object is called from an API and set to a new instance of the object. When I add a breakpoint, the full object is available, but Labels on the view aren't populated. Here is the ViewModel:
DetailsViewModel.cs
public class DetailsViewModel
{
public Deal Deal { get; set; }
public int DealId { get; set; }
public DetailsViewModel(int id)
{
Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if(deal != null)
{
Deal = deal;
}
}
}
The codebehind looks like this:
DetailPage.Xaml.cs
DetailsViewModel viewModel;
int dealId;
public DetailPage(int id)
{
InitializeComponent();
dealId = id;
viewModel = new DetailsViewModel(dealId);
BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.GetDeal();
}
And the Xaml file is
DetailPage.Xaml
<ContentPage.Content>
<ScrollView>
<StackLayout x:Name="detailsLayout">
<Label Text="{Binding Deal.Name}" />
</StackLayout>
</ScrollView>
</ContentPage.Content>
When I put a breakpoint in Deal = deal on DetailsViewModel, the Deal object exists and has the correct data, but I just get a blank screen. I have tried Labels with Text="{Binding Name}" and Text="{Binding Deal.Name}".
I have also tried manually creating a deal in the GetDeal function of the ViewModel and still nothing is bound.
1) Ensure your property Notifies the UI of a change implementing the INotifyPropertyChanged interface. See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
2) Ensure the set is done on the UI thread using Device.BeginInvokeOnMainThread. https://learn.microsoft.com/fr-fr/dotnet/api/xamarin.forms.device.begininvokeonmainthread?view=xamarin-forms
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace YourNamespace
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Deal _deal;
public Deal Deal
{
get => _deal;
set
{
if (_deal != value)
{
_deal = value;
OnPropertyChanged();
}
}
}
public int DealId { get; set; }
public DetailsViewModel(int id)
{
//!! useless assignation
//Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if (deal != null)
{
//Ensure we are on UI thread
Device.BeginInvokeOnMainThread(() => Deal = deal);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
New to Xamarin development! Using Visual Studio 2017 and all the latest installs and updates for my dev environment.
I have my app "shell" working in that it will run, navigate, crud to local db, and sync to rest services. So, the base of my app is sound. I am trying to integrate the ZXing barcode scanner into my app. Most of what help I can find relates to raw forms or code behind xaml. I am using views and view models and I don't know how to translate the information I am finding to this model.
I currently have the xaml view showing the "camera viewer" when a button is clicked so I can see a barcode using the camera. However, there is no "red line" in the camera view and subsequently, there are no events being fired to let me know what is going on. I really am confused as I am so new to Xamarin. I don't know where to start.
XAML View page
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:fe="clr-namespace:FreshEssentials;assembly=FreshEssentials"
xmlns:forms="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="Views.InventoryPage1"
Title="Inventory Page">
<StackLayout Spacing="5" Padding="10,10,10,0">
<!--<fe:BindablePicker ItemsSource="{Binding Areas}" SelectedItem="{Binding SelectedArea}" DisplayProperty="AreaName" Title="Select Your Area" />
<fe:BindablePicker ItemsSource="{Binding Reasons}" SelectedItem="{Binding SelectedReason}"
DisplayProperty="Description" Title="Select a Reason" />
<Label Text="Scan"/>
<Entry Text="{Binding Barcode}"/>
<Label Text="Quantity"/>
<Entry Text="{Binding Quantity}"/>
<Button Text="Save" Style="{StaticResource Button_Primary}" Command="{Binding SaveCommand}" />-->
<forms:ZXingScannerView WidthRequest="100" HeightRequest="100" IsScanning="{Binding IsScanning}" IsAnalyzing="{Binding IsAnalyzing}" Result="{Binding Result, Mode=TwoWay}" ScanResultCommand="{Binding QRScanResultCommand}" ></forms:ZXingScannerView>
</StackLayout>
</ContentPage>
ViewModel
public class InventoryPage1ViewModel : BindableBase, INavigationAware
{
private readonly IPageDialogService _pageDialogService;
private bool _isAnalyzing = true;
private bool _isScanning = true;
public ZXing.Result Result { get; set; }
public List<Area> Areas { get; private set; }
public Area SelectedArea { get; set; }
public List<Reason> Reasons { get; private set; }
public Reason SelectedReason { get; set; }
public int Quantity { get; set; }
public string Barcode { get; set; }
public DelegateCommand SaveCommand => new DelegateCommand(PerformSave);
public DelegateCommand QRScanResultCommand => new DelegateCommand(QRCommand);
private readonly IAreaService _areaService;
private readonly IScanService _scanService;
private readonly IReasonService _reasonService;
public InventoryPage1ViewModel(IAreaService areaService, IScanService scanService, IReasonService reasonService, IPageDialogService pageDialogService)
{
_pageDialogService = pageDialogService;
_reasonService = reasonService;
_scanService = scanService;
_areaService = areaService;
Areas = _areaService.GetAll();
Reasons = _reasonService.GetAll();
}
public bool IsScanning
{
get
{
return _isScanning;
}
set
{
_isScanning = value;
RaisePropertyChanged();
}
}
public bool IsAnalyzing
{
get
{
return _isAnalyzing;
}
set
{
_isAnalyzing = value;
RaisePropertyChanged();
}
}
private void QRCommand()
{
int x = 1;
}
private async void PerformSave()
{
var scan = new Scan()
{
AreaId = SelectedArea.Id,
InsertDateTime = DateTime.Now,
ReasonId = SelectedReason.Id,
ScanItem = Barcode,
ScanQty = Quantity,
IsUploaded = false
};
// Save it to the DB here.
var retVal = _scanService.Insert(scan);
if (retVal)
{
await _pageDialogService.DisplayAlertAsync("Saved", "Scan saved successfully.", "OK");
}
else
{
// TODO: Inform the user something went wrong.
}
}
int _index;
public int SelectIndex
{
get
{
return _index;
}
set
{
_index = value;
RaisePropertyChanged("SelectIndex");
}
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
}
public void OnNavigatingTo(NavigationParameters parameters)
{
}
}
MainActivity
public class MainActivity :
global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.tabs;
ToolbarResource = Resource.Layout.toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ZXing.Net.Mobile.Forms.Android.Platform.Init();
LoadApplication(new App(new AndroidInitializer()));
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
ZXing.Net.Mobile.Android.PermissionsHandler.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
container.RegisterType<IConnectionFactory, ConnectionFactory>();
}
}
UPDATE
I have a working view model now thanks to #Krzysztof over at Xamarin.Forms.
New ViewModel
public class InventoryPage1ViewModel : BindableBase, INavigationAware
{
private readonly IPageDialogService _pageDialogService;
public List<Area> Areas { get; private set; }
public Area SelectedArea { get; set; }
public List<Reason> Reasons { get; private set; }
public Reason SelectedReason { get; set; }
public int Quantity { get; set; }
public DelegateCommand SaveCommand => new DelegateCommand(PerformSave);
private readonly IAreaService _areaService;
private readonly IScanService _scanService;
private readonly IReasonService _reasonService;
public InventoryPage1ViewModel(IAreaService areaService, IScanService scanService, IReasonService reasonService, IPageDialogService pageDialogService)
{
_pageDialogService = pageDialogService;
_reasonService = reasonService;
_scanService = scanService;
_areaService = areaService;
Areas = _areaService.GetAll();
Reasons = _reasonService.GetAll();
}
public ZXing.Result Result { get; set; }
private string barcode = string.Empty;
public string Barcode
{
get
{
return barcode;
}
set
{
barcode = value;
RaisePropertyChanged();
}
}
private bool isAnalyzing = true;
public bool IsAnalyzing
{
get { return this.isAnalyzing; }
set
{
if (!bool.Equals(this.isAnalyzing, value))
{
this.isAnalyzing = value;
RaisePropertyChanged(nameof(IsAnalyzing));
}
}
}
private bool isScanning = true;
public bool IsScanning
{
get { return this.isScanning; }
set
{
if (!bool.Equals(this.isScanning, value))
{
this.isScanning = value;
RaisePropertyChanged(nameof(IsScanning));
}
}
}
public Command QRScanResultCommand
{
get
{
return new Command(() =>
{
IsAnalyzing = false;
IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
Barcode = Result.Text;
await _pageDialogService.DisplayAlertAsync("Scanned Item", Result.Text, "Ok");
});
IsAnalyzing = true;
IsScanning = true;
});
}
}
private async void PerformSave()
{
var scan = new Scan()
{
AreaId = SelectedArea.Id,
InsertDateTime = DateTime.Now,
ReasonId = SelectedReason.Id,
ScanItem = Barcode,
ScanQty = Quantity,
IsUploaded = false
};
// Save it to the DB here.
var retVal = _scanService.Insert(scan);
if (retVal)
{
await _pageDialogService.DisplayAlertAsync("Saved", "Scan saved successfully.", "OK");
}
else
{
// TODO: Inform the user something went wrong.
}
}
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
public void OnNavigatedTo(NavigationParameters parameters)
{
}
public void OnNavigatingTo(NavigationParameters parameters)
{
}
}
I want to binding available SerialPort to combobox in MVVM, so I have this:
XAML:
<ComboBox x:Name="cb_PortName" ItemsSource="{Binding PortsList}" SelectedValue="{Binding SelectedPort}" DisplayMemberPath="PortNames"/>
ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private SerialPort sp = new SerialPort();
public ObservableCollection<SerialPort> PortsList{ get; set; }
private SerialPort _selectedPort;
public SerialPort SelectedPort
{
get { return _selectedPort; }
set
{
_selectedPort = value;
NotifyPropertyChanged("SelectedPort");
}
}
public MainViewModel()
{
DefaultValue_Load();
}
public void DefaultValue_Load()
{
//what should I do
PortsList = new ObservableCollectioin<SerialPort>();
}
}
So what should I do to binding the serialport to combobox? Thanks!
I'm using the prism framework for my Xamarin.Forms application.
This is a common scenario, but it caused me headache.
MainPage
- MainPageViewModel
- ObserveableCollection<SomePageViewModel>
public class MainPageViewModel : BaseViewModel
{
private ObservableCollection<SomePageViewModel> viewModels;
public MainPageViewModel(INavigationService navigationService) : base(navigationService)
{
SomePageSelectedCommand = DelegateCommand.FromAsyncHandler(NavigateToSomePage);
}
public ICommand SomePageSelectedCommand { get; private set; }
public ObservableCollection<SomePageViewModel> ViewModels
{
get { return viewModels; }
set { SetProperty(ref viewModels, value); }
}
private async Task NavigateToSomePage(SomePageViewModel viewModel)
{
var navParams = new NavigationParameters
{
{viewModel.typeof(SomePageViewModel).Name, viewModel}
};
await Navigation.NavigateAsync(NavigationConstants.SomePageUri, navParams, false);
}
}
public class SomePageViewModel : BaseViewModel
{
protected SomeModel someModel;
public SomePageViewModel(INavigationService navigationService) : base(navigationService)
{
someModel = new SomeModel();
EditCommand = DelegateCommand.FromAsyncHandler(Edit);
}
public ICommand EditCommand { get; private set; }
public string Name
{
get { return SomeModel.Name; }
set { SetProperty(ref SomeModel.Name, value); }
}
public string Description
{
get { return SomeModel.Description; }
set { SetProperty(ref SomeModel.Description, value); }
}
public override void OnNavigatedTo(NavigationParameters parameters)
{
if (parameters.ContainsKey(typeof(SomePageViewModel).Name))
{
var viewModel = (SomePageViewModel)parameters[typeof(SomePageViewModel).Name];
Name = viewModel.Name;
Description = viewModel.Name;
}
}
private async Task Edit()
{
var navParams = new NavigationParameters
{
{viewModel.typeof(SomePageViewModel).Name, this}
};
await Navigation.NavigateAsync(NavigationConstants.SomePageEditUri, navParams, false);
}
}
public class SomePageEditViewModel : BaseViewModel
{
public SomePageEditViewModel(INavigationService navigationService) : base(navigationService)
{
SaveCommand = DelegateCommand.FromAsyncHandler(Save);
}
public ICommand SaveCommand { get; private set; }
private async Task Save()
{
App.ContentService.Save(someModel);
await Navigation.GoBackAsync();
}
}
So lets navigate from the MainPage to a SomePage. We want to edit it so we navigate to SomePageEdit afterwards and save finally.
What is a proper way to make the changes visible to the SomePage and the MainPage according mvvm/prsim? For the first one I could pass the changes as NavigationParameter into GoBackAsync. But what about the MainPage?
Well it appears you have a bit of a design problem. To properly architect your app you want something closer to:
Model
public class TodoItem : ObservableObject
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
private bool _done;
public bool Done
{
get { return _done; }
set { SetProperty(ref _done, value); }
}
}
Model Collection Page ViewModel
public class TodoItemListPageViewModel : BaseViewModel, INavigationAware
{
private INavigationService _navigationService { get; }
public TodoItemListViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
TodoItems = new ObservableRangeCollection<TodoItem>();
AddTodoItemCommand = new DelegateCommand(OnAddTodoItemCommandExecuted);
EditTodoItemCommand = new DelegateCommand<TodoItem>(OnEditTodoItemCommandExecuted);
}
public ObservableRangeCollection<TodoItem> TodoItems { get; }
public DelegateCommand AddTodoItemCommand { get; }
public DelegateCommand<TodoItem> EditTodoItemCommand { get; }
public void OnNavigatingTo(NavigationParameters parameters)
{
// Initialize your collection
}
public void OnNavigatedTo(NavigationParameters parameters)
{
if(parameters.GetValue<NavigationMode>(KnownNavigationParameters.NavigationMode) == NavigationMode.Back)
{
// Option 1
// Fetch an updated list of TodoItems from your data source
TodoItems.ReplaceRange(updatedTodoItems);
// Option 2
// Replace the updated item or add a new item
}
}
Edit Model Page ViewModel
public void OnNavigatedFrom(NavigationParameters parameters)
{
}
private async void OnAddTodoItemCommandExecuted() =>
await _navigationService.NavigateAsync("AddTodoItemPage");
private async void OnEditTodoItemCommandExecuted(TodoItem item) =>
await _navigationService.NavigateAsync("EditTodoItemPage", new NavigationParameters { { "item", item } });
}
public class EditTodoItemPageViewModel : BaseViewModel
{
private INavigationService _navigationService { get; }
public EditTodoItemPageViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
SaveCommand = new DelegateCommand(OnSaveCommandExecuted, () => IsNotBusy)
.ObservesProperty(() => IsBusy);
}
private TodoItem _model;
public TodoItem Model
{
get { return _model; }
set { SetProperty(ref _model, value); }
}
public DelegateCommand SaveCommand { get; }
public void OnNavigatingTo(NavigationParameters parameters)
{
Model = parameters.GetValue<TodoItem>("item");
}
private async void OnSaveCommandExecuted()
{
IsBusy = true;
// Persist any changes
// Option 1
await _navigationService.GoBackAsync();
// Option 2
await _navigationService.GoBackAsync(new NavigationParameters { { "updatedItem", Model } });
IsBusy = false;
}
}
The Why...
Your ObservableCollection should be where T : TModel not where T : TViewModel. Another issue you would have immediately is that the INavigationService is dependent on knowing what Page you're navigating to/from. So you cannot follow the pattern you're doing there.
Now a couple of notes here.
You'll notice this sample is actually using some helpers from the MvvmHelpers library. The BaseViewModel class from that library gives you the IsBusy/IsNotBusy property as well as a Title property and the ObservableRangeCollection.
ObservableRangeCollection vs ObservableCollection
The ObservableRangeCollection gives you a little better performance particularly when working with larger datasets. You may have noticed the Option 1 where we simply get the updated dataset and replace the entire dataset. This is where the ObservableRangeCollection really shines in my opinion since you're able to ensure you have an up to date dataset while minimizing the notifications to the UI resulting in fewer CPU cycles taken up.
Models, Views, ViewModels
I do not mean for this to an authoritative answer but to at least provide food for thought. From a high level overview of MVVM patterns you generally are working with a View which provides the UX, a ViewModel which provides the business logic for who/what/why/when/where/etc, and a Model which is the data we want to work with. In some cases it can become necessary to introduce a DTO which further abstracts our raw data from the Model we want to work with as a logical unit.
i am invoking the QuerySubmitted command of the AutoSuggestBox control in UWP.
the command binds to ICommand in the view model.
the problem is it requires to accept AutoSuggestBoxQuerySubmittedEventArgs which is pure UI and it's not acceptable in MVVM.
my code looks like that:
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find"
>
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="QuerySubmitted">
<core:InvokeCommandAction Command="{x:Bind ViewModel.SearchCommand}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AutoSuggestBox>
and my view model looks like that:
public DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs> SearchCommand { get; }
public MainPageViewModel()
{
SearchCommand = new DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs>(ExecuteMethod);
}
private void ExecuteMethod(AutoSuggestBoxQuerySubmittedEventArgs o)
{
// CODE HERE
}
ofcours AutoSuggestBoxQuerySubmittedEventArgs is not acceptable in the view model.
looking for alternatives...
same goes to SuggestionChosen...
InvokeCommandAction has a parameter named InputConverter which you can use to convert the event args to some other parameter that can be passed to your ViewModel.
First create a IValueConverter class to extract what you need from your event args like this:-
public class AutoSuggestQueryParameterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
// cast value to whatever EventArgs class you are expecting here
var args = (AutoSuggestBoxQuerySubmittedEventArgs)value;
// return what you need from the args
return (string)args.ChosenSuggestion;
}
}
Then use that converter in your XAML like this:
<Page.Resources>
<converters:AutoSuggestQueryParameterConverter x:Key="ArgsConverter" />
</Page.Resources>
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="QuerySubmitted">
<core:InvokeCommandAction
Command="{x:Bind ViewModel.SearchCommand}"
InputConverter="{StaticResource ArgsConverter}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</AutoSuggestBox>
Finally in your viewmodel change your command to accept a string as parameter. So you would have the following in your vm:
public DelegateCommand<string> SearchCommand { get; }
public MainPageViewModel()
{
SearchCommand = new DelegateCommand<string>(ExecuteMethod);
}
private void ExecuteMethod(string o)
{
// CODE HERE
}
You can bind the search string (Text property) to a view model property and the events to parameter-less methods. This way the view model wont have to deal with UI objects:
XAML:
<AutoSuggestBox Header="What's your name?"
TextChanged="{x:Bind ViewModel.FilterUsuals}"
QuerySubmitted="{x:Bind ViewModel.ProcessQuery}"
SuggestionChosen="{x:Bind ViewModel.ProcessChoice}"
ItemsSource="{x:Bind ViewModel.Usuals, Mode=OneWay}"
Text="{x:Bind ViewModel.SearchText, Mode=TwoWay}"
QueryIcon="Find" />
Code behind:
public class MainPageViewModel : SomeViewModelBaseClass
{
/* Boilerplate code and constructor not included */
private string _SearchText;
public string SearchText {/* getter and setter INotyfyPropertyChange compliant */ }
private List<string> _Usuals; // Initialized on constructor
public string Usuals {/* getter and setter INotyfyPropertyChange compliant */ }
public void FilterUsuals()
{
// the search string is in SearchText Example:
Usuals = _UsualsStore.Where(u => u.Contains(_SearchText.ToLower())).ToList();
}
public void ProcessQuery() { /* TODO - search string is in SearchText */ }
public void ProcessChoice() { /* TODO - search string is in SearchText */ }
}
If you don't mind doing non pure MVVM way.
MainPage.xaml :
<AutoSuggestBox Name="SearchAutoSuggestBox"
PlaceholderText="Search by keywords"
QueryIcon="Find" QuerySubmitted="{x:Bind ViewModel.SearchQuerySubmitted}" IsEnabled="{x:Bind ViewModel.CanExecuteSearchCommand, Mode=TwoWay}"
>
</AutoSuggestBox>
MainPageViewModel.cs :
public class MainPageViewModel : INotifyPropertyChanged
{
private bool _canExecuteSearchCommand;
public MainPageViewModel()
{
this.CanExecuteSearchCommand = true;
}
public bool CanExecuteSearchCommand
{
get { return _canExecuteSearchCommand; }
set
{
bool changed = _canExecuteSearchCommand != value;
_canExecuteSearchCommand = value;
if(changed)
this.OnPropertyChanged();
}
}
public void SearchQuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
// Just example disabling SearchBox
this.CanExecuteSearchCommand = false;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainPage.cs :
MainPageViewModel ViewModel = new MainPageViewModel();
UWP Binding Command/Delegate to AutoSuggestBox in MVVM
For UWP Mobile Application
Make a DelegateCommand class
public class DelegateCommand<T> : ICommand
{
private readonly Action<T> executeAction;
Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<T> executeAction)
: this(executeAction, null)
{
//var a = ((Page)(((Func<object, bool>)(executeAction.Target)).Target)).Name;
//((ViewModel.VMBranchSelection)(executeAction.Target)).;
}
public DelegateCommand(Action<T> executeAction, Func<object, bool> canExecute)
{
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null ? true : canExecute(parameter);
}
public void Execute(object parameter)
{
executeAction((T)parameter);
}
public void RaiseCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
In View Model
public ICommand SuggessionSelectCity_QuerySubmitted
{
get { return new DelegateCommand<AutoSuggestBoxQuerySubmittedEventArgs>(this.SuggessionSelectCityQuerySubmitted); }
}
private void SuggessionSelectCityQuerySubmitted(AutoSuggestBoxQuerySubmittedEventArgs obj)
{
if (obj.ChosenSuggestion != null)
{
AutosuggestionTextBoxName.Text = ((ModelName) (obj.ChosenSuggestion)).Model's Property name;
//or
AutosuggestionTextBoxName.Text =(obj.ChosenSuggestion).property name
}
else
{
}
}
In XAML Code
<AutoSuggestBox Grid.Column="1" x:Name="SuggessionSelectCity"
PlaceholderText="Search by keywords" QueryIcon="Find"
ItemsSource="{Binding PApplicantCityList}"
HorizontalAlignment="Center" VerticalAlignment="Center" DisplayMemberPath="Description" Width="250" Height="45">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="TextChanged">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCityTextChange}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="QuerySubmitted">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCity_QuerySubmitted}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
<Core:EventTriggerBehavior EventName="SuggestionChosen">
<Core:EventTriggerBehavior.Actions>
<Core:InvokeCommandAction Command="{Binding SuggessionSelectCitySuggestionChosen}"/>
</Core:EventTriggerBehavior.Actions>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</AutoSuggestBox>
</Grid>
Create a list in View Model for Autosuggestion TextBox Itemssource
private ObservableCollection<ResultMasterModel> ApplicantCityList;
public ObservableCollection<ResultMasterModel> PApplicantCityList
{
get { return ApplicantCityList; }
set { this.SetProperty(ref this.ApplicantCityList, value); }
}
add some hard code value in above list
Create a Model In Model Folder
public class ResultMasterModel
{
public string Code { get; set; }
public string Description { get; set; }
}