I am a beginner, so this should be an easy fix for someone with experience.
I am a veterinarian trying to build an app for vets to use. I am trying to learn via youtube and previous posts online, but I cannot find a solution for my roadblock:
I am trying to call a method already written in the App class:
Settings form binds a volume of onhand penicillin.
Usage form displays that volume, and asks the user to enter a volume used.
On button click I need the onhand volume to be 'updated' but i can't call the method in the App class (which has a get and set)
I have simplified for easier evaluation:
App code behind:
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace methods
{
public partial class App : Application
{
private const string penicillin = "penicillin";
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
public string Penicillin
{
get
{
if (Properties.ContainsKey(penicillin))
{
return Properties[penicillin].ToString();
}
return "";
}
set
{
Properties[penicillin] = value;
}
}
}
}
Settings Form:
<?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="methods.Settings">
<ContentPage.Content>
<StackLayout>
<Label Text="Penicillin Onhand" ></Label>
<Entry Text="{Binding Penicillin}" Keyboard="Numeric" ></Entry>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Settings Code behind:
using Xamarin.Forms;
namespace methods
{
public partial class Settings : ContentPage
{
public Settings()
{
InitializeComponent();
BindingContext = Application.Current;
}
}
}
Usage form:
<?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="methods.Usage">
<ContentPage.Content>
<StackLayout>
<Label Text="Penicillin Held" ></Label>
<Label x:Name="Held" Text="{Binding Penicillin}" ></Label>
<Label Text="Penicillin Used" ></Label>
<Entry x:Name="Using" Placeholder="ml" Keyboard="Numeric" ></Entry>
<Button Text="Use" Clicked="Used_Clicked"></Button>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Usage Code behind:
using Xamarin.Forms;
namespace methods
{
public partial class Usage : ContentPage
{
public Usage()
{
InitializeComponent();
BindingContext = Application.Current;
}
void Used_Clicked(object sender, System.EventArgs e)
{
var VUsed = double.Parse(Using.Text);
var VHeld = double.Parse(Held.Text);
var Remaining = VHeld - VUsed;
//how do i code the following? Error:'App' does not contain definition for Penicillin????
App.Penicillin.value = Remaining;
}
}
}
You don't need to create a new instance of App - Xamarin Forms already maintains a reference to the current instance
((App)Application.Current).Penicillin = Remaining;
Related
So I'm pretty new to the whole MVVM thing. I pretty much understand the basics of it. I have an example that's working just find as it is but I'm trying to change it into the MVVM style. Im just trying out different examples so I can learn.
(LoginPage.xaml)
On the XAML side I have a label with a tap gesture. pretty single
<!-- Existing Tab -->
<Label x:Name="labelName"
Grid.Column="0"
Style="{StaticResource CenteredTextTitle}"
Text="Log In">
<Label.GestureRecognizers>
<TapGestureRecognizer Tapped="SelectorOption_Tapped" />
</Label.GestureRecognizers>
</Label>
LoginPage.xaml.cs page
I can access it by creating a tapped function.
private async void SelectorOption_Tapped(object sender, EventArgs e)
{
if (!(sender is Xamarin.Forms.View view)) return;
int index = Grid.GetColumn(view);
...
}
now im trying to change that into MVVM..
So the XAML stays the same
on the ViewModel page
loginpageviewmodel.cs page
public LoginPageViewModel()
{
btnSubmit_Clicked = new Command(SubmitClicked);
}
public ICommand btnSubmit_Clicked { get; set; }
private void SubmitClicked(object parameter)
{
if (!(parameter is Xamarin.Forms.View view)) return;
var index = Grid.GetColumn(view);
}
i can run the function submitclicked. But i cant pass on the parameter. Its always null.
i want to get the column from the grid that i have based on what the user selects. it works fine the original way but if i try to change it to MVVM i cant pass the view form to get the getcolumn..
is there anyway i can pass that.?
The parameter comes from the CommandParameter you set in the xaml:
<StackLayout>
<!-- Place new controls here -->
<Label x:Name="myLabel" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Text="Log In">
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding btnSubmit_Clicked}" CommandParameter="{x:Reference myLabel}"/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
In code behind:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new LoginPageViewModel();
}
}
public class LoginPageViewModel
{
public LoginPageViewModel()
{
btnSubmit_Clicked = new Command(SubmitClicked);
}
public ICommand btnSubmit_Clicked { get; set; }
private void SubmitClicked(object parameter)
{
if (!(parameter is Xamarin.Forms.View view)) return;
Console.WriteLine(parameter);
}
}
I have Template1 that inherits from ContentView. For the purpose of this question I removed a lot of things in the template to make it simple.
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Japanese;assembly=Japanese" x:Class="Japanese.Template1" x:Name="this">
<Label Grid.Column="0" Text="{Binding Text, Source={x:Reference this}}" />
</ContentView>
and the Template1 C# back end
namespace Japanese
{
public partial class Template1 : ContentView
{
public CardOrderTemplate()
{
InitializeComponent();
}
}
}
I would like this template to share bindings in this class and so I tried some different ways to do this but none seem to work. Here's what I was trying. Not sure but I guess I need something like a ViewModel for the template but I am not sure how I could go about implementing this.
namespace Japanese
{
public class SegBase : ContentView
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SegTemplate), default(string));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
Does anyone know how I can do this? I tried making the Template1 class inherit from SegBase but it gives me an error because the XAML is a type ContentView and the C# is not inheriting from ContentView.
I have this template:
<?xml version="1.0" encoding="utf-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Japanese.OpenPageTemplate"
x:Name="this">
<Label Text="ABC" VerticalTextAlignment="Center" />
</ViewCell>
and the code behind:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class OpenPageTemplate : ViewCell
{
public OpenPageViewCellTemplate()
{
InitializeComponent();
}
protected override void OnTapped()
{
base.OnTapped();
// I need to call openPage() here or at least have openPage() called from the place where the template is used.
}
}
}
Can someone tell me how I can make this template call a method called openPage() when the user taps on the ViewCell? In a previous question there was an answer about using the .Invoke method that went something like this:
ClickAction?.Invoke(this, new EventArgs());
However this time there's just one action to invoke and I don't need to pass information on the action into ClickAction.
Wrap the ViewCell in a DataTemplate:
<?xml version="1.0" encoding="utf-8" ?>
<DataTemplate xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:template="clr-namespace:Japanese"
x:Class="Japanese.OpenPageTemplate">
<ViewCell Tapped="OnOpenPageTapped">
...
</ViewCell>
</DataTemplate>
Codebehind:
namespace Japanese
{
public partial class OpenPageTemplate : DataTemplate
{
public OpenPageTemplate ()
{
InitializeComponent ();
}
private void OnOpenPageTapped(object sender, EventArgs e)
{
//example: template is an itemtemplate for a list of class A.B with property C
var c = ((sender as ViewCell)?.BindingContext as A.B)?.C;
}
...
Subscribe to the desired Cell.Tapped Event and invoke the action when the event is raised.
XAML
<template:OpenPageTemplate Tapped="OnOpenPageTapped" />
Code behind
private void OnOpenPageTapped(object sender, EventArgs args) {
openPage();
}
assuming that openPage() is accessible from the place where the template is used.
I have an application that I am looking at. Currently it has this:
C# code
namespace Japanese
{
public partial class PhrasesPage : ContentPage
public PhrasesFrame phrasesFrame = new PhrasesFrame();
public PhrasesPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
phrasesStackLayout.Children.Add(phrasesFrame);
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"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.PhrasesPage"
x:Name="PhraseContentPage">
<ContentPage.Content>
<StackLayout x:Name="phrasesStackLayout">
</StackLayout>
</ContentPage.Content>
</ContentPage>
And then inside of that:
C# code
namespace Japanese
{
public partial class PhrasesFrame : Frame
XAML
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Japanese.PhrasesFrame" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" BackgroundColor="Transparent" Padding="0" HasShadow="false">
<StackLayout x:Name="phrasesFrameStackLayout" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
There's a custom renderer for the frame that implements rightSwipeGestureRecognizer
[assembly: ExportRenderer(typeof(PhrasesFrame), typeof(PhrasesFrameCustomRenderer))]
namespace Japanese.iOS
{
public class PhrasesFrameCustomRenderer : FrameRenderer
{
UISwipeGestureRecognizer leftSwipeGestureRecognizer;
UISwipeGestureRecognizer rightSwipeGestureRecognizer;
PhrasesFrame frame;
bool rightSwipeEnabled = false;
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
frame = Element as PhrasesFrame;
rightSwipeGestureRecognizer = new UISwipeGestureRecognizer();
rightSwipeGestureRecognizer.Direction = UISwipeGestureRecognizerDirection.Right;
rightSwipeGestureRecognizer.NumberOfTouchesRequired = 1;
rightSwipeGestureRecognizer.AddTarget((obj) =>
{ });
My question is, do I need to have a frame inside of the content page to implement a SwipeGestureRecognizer?
I think your code could also work on any given other element. Since you're currently inheriting the CustomRenderer from FrameRenderer it will only work on Frame objects and more specifically PhrasesFrame objects (as defined in the ExportRenderer attribute. You can easily change this to e.g. PageRenderer like such:
[assembly: ExportRenderer(typeof(MyContentPage), typeof(ContentPageRenderer))]
namespace Japanese.iOS
{
public class ContentPageRenderer : PageRenderer
{
You could then change every piece of code referencing the Frame in this renderer to reference to ContentPage instead and it will probably work. That does mean that every ContentPage object gets this functionality, so you will most likely have to create your own object inheriting from ContentPage and apply the CustomRenderer to that.
public class MyContentPage : ContentPage
{
}
And update your XAML accordingly:
<?xml version="1.0" encoding="UTF-8"?>
<local:MyContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.PhrasesPage"
x:Name="PhraseContentPage">
<ContentPage.Content>
<StackLayout x:Name="phrasesStackLayout">
</StackLayout>
</ContentPage.Content>
</local:MyContentPage>
In my xamarin forms app using Prism MVVM Framework, How should I use IoC containers?
There are no
Container.Resolve<Interface,Implentation>
I think we need to resolve the IoC and initialize in app.xaml.cs. But I am not able to find proper way to do it.
Can anyone help me out ?
UPDATE:
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"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="CoC.Core.Views.MenuPage">
<ContentPage.Content>
<ListView x:Name="info"
SeparatorVisibility="None"
SeparatorColor="Transparent"
HasUnevenRows="false"
RowHeight="50" BackgroundColor="#485366" >
<ListView.ItemTemplate>
<DataTemplate>
<!--<ImageCell Text="{Binding Title}" ImageSource="{Binding IconSource}" /> -->
<ViewCell>
<!--<Label Text="{Binding Title}" />-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
ViewModel:
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using Prism.Navigation;
namespace CoC.Core.ViewModels
{
public class MenuPageViewModel : BindableBase , INavigationAware
{
IMenuList _menuList;
public ObservableCollection<MenuItem> ConductList { get; set; }
public void OnNavigatedFrom(NavigationParameters parameters)
{
//throw new NotImplementedException();
}
public void OnNavigatedTo(NavigationParameters parameters)
{
//throw new NotImplementedException();
}
public MenuPageViewModel(IMenuList MenuList)
{
_menuList = MenuList;
ConductList = _menuList.GetMenuList();
}
}
}
APP:
using Prism.Unity;
using CoC.Core.Views;
namespace CoC.Core
{
public partial class App : PrismApplication
{
protected override void OnInitialized()
{
InitializeComponent();
//NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms");
NavigationService.NavigateAsync("MenuPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<RootPage>();
Container.RegisterTypeForNavigation<MenuPage>();
}
}
}
When I run this code, I am getting error in Main.cs of IOS pro
Error:
Foundation.MonoTouchException
NSInternalLnconistencyException Reason: Application window are expected to have a root view controller at the end of the application.
What you are looking for is an extension method in Unity. You need to add the using Microsoft.Practices.Unity namespace to your code file. The you can register your services using Container.Register<IService,MyService>().