Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

xamarin broadcast receiver access viewmodel

Notes: Xamarin 4.2.1.64, Visual Studio 2015 proff.

I have created a cross platform app that is to work on a android device that scans barcodes.

Currently When scanned the software has an optional output mode, (buffer, keyboard,clipboard and intent).

Currently using keyboard mode.

Flow

User clicks device button scanning a barcode, software attempts to dump to an input on the screen, if not it instead tabs (app is set focus to entry field on startup). When a button on the app is clicked it calls my service to query a set of data and return the results, the list of results is then updated for the user to see.

Flow i need to change

User clicks device button scanning a barcode, only this time device is set to intent and broadcasts, my app reciever picks up the broadcast, reads the barcode from the intent and calls my viewmodel to update the field with the barcode. the viewmodel will now change to detect field change and run the method accordingly.

Code so far

Portable Xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         x:Class="StockCheckApp.Views.ScannerPage"
         xmlns:ViewModels="clr-namespace:StockCheckApp.ViewModels;assembly=StockCheckApp">
  <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />

  <StackLayout Orientation="Vertical">

<Entry x:Name="myBox" Text="{Binding UserInput, Mode=TwoWay}" />
<Button Text="Check Stock" Command="{Binding PostCommand}"/>

<ListView ItemsSource="{Binding StockList}"
        HasUnevenRows="True">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <StackLayout Orientation="Vertical" Padding="12,6">
          <Label Text="{Binding St_Code}" FontSize="24" />
          <Label Text="{Binding St_Desc}" />
          <Label Text="{Binding Sl_Loc}" />
          <Label Text="{Binding Sa_Datetime}" />
          <Label Text="{Binding Sa_Qty}" />
        </StackLayout>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>

Portable Xaml Code behind

 public partial class ScannerPage : ContentPage
{
    public ScannerPage()
    {
        InitializeComponent();
        BindingContext = new MainViewModel(this);
    }

    protected override void OnAppearing()
    {
        base.OnAppearing();
        myBox.Focus();
    }

    public Entry MyBox
    {
        get
        {
            return myBox;
        }
    }
}

Portable Main view model

public class MainViewModel : INotifyPropertyChanged
{
    ScannerPage page;
    private List<Stock> _stockList;
    private string _userInput;
    public List<Stock> StockList
    {
        get { return _stockList; }
        set
        {
            _stockList = value;
            OnPropertyChanged();
        }
    }

    public string UserInput
    {
        get { return _userInput; }
        set
        {
            _userInput = value;
            OnPropertyChanged();
        }         
    }

    public MainViewModel(ScannerPage parent)
    {
        page = parent;
        page.MyBox.Focus();
    }

    public Command PostCommand
    {
        get
        {
            return new Command(async () =>
            {
                var stockService = new StockService();
                StockList = await stockService.GetStockAsync(UserInput);
                page.MyBox.Focus();
            });
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        //use this to clear? then reset focus?
    }
}

Android reciever class

[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "android.intent.action.bcr.newdata" })]
public class Receiver1 : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        Toast.MakeText(context, "Received intent!", ToastLength.Short).Show();
        Services.StockService meh = new Services.StockService();
        //MainViewModel md = new MainViewModel();
        Dowork(meh, intent);
    }

    //this isnt correct i need to update viewmodel not call service direct!
    private async void Dowork (Services.StockService meh, Intent intent)
    {
        string action = intent.Action.ToString();
        string decodedBarcode = intent.GetStringExtra(BCRIntents.ExtraBcrString);
        //now i just need to update my field in the xaml....somehow
    }
}

What I'm stuck on

I step through and my code hits my breakpoints, but at this stage I need my reciver to somehow update the entry field.

I'm not familiar with Xamarin yet and I'm learning alot, so I realize this may actually be a simple answer.

Also

Am I correct in what I intend to do? recieve the barcode number and access the viewmodels "userinput" property and change it? os should I somehow access the field on the view and change it instead then allow my property changed method to carry out business logic?

like image 774
lemunk Avatar asked Mar 10 '23 14:03

lemunk


1 Answers

You could use the Messaging Center that ships with Xamarin.Forms https://developer.xamarin.com/guides/xamarin-forms/messaging-center/

Have your ViewModel Subscribe to the MessagingCenter for an event coming from your service, then use the event to update the property that is bound to the field. If you need a type in your PCL to map the subscribe to, create an interface for your service in the PCL that doesn't need to have any actual method contracts, then have your service implement it so you can set up your subscribe with strong types:

// in your PCL
public interface IScanReceiver
{
}

// in your android project
public class Receiver1 : BroadcastReceiver, IScanReceiver

// in your viewmodel
MessagingCenter.Subscribe<IScanReceiver>();

Alternatively, you could set up your ViewModels in a dependency service and then use the Service Locator (anti)pattern to find your ViewModel instance and update the property that is bound to the field. MVVM Light: http://www.mvvmlight.net/ does a great job at giving you tools to do this.

like image 80
SuavePirate Avatar answered Mar 19 '23 20:03

SuavePirate