I am using the Rg.Plugins.Popup plugin for displaying a popup on a tap. My issue is how to call a popuppage from a viewmodel class? (I am using MVVM)
I am using the following code in viewmodel for the tap handler:
public async void OnForgotPasswordAsync()
{
await Application.Current.MainPage.Navigation.PushPopupAsync(new ForgotPassword());
}
This is a Command
Here I have the view class for the popuppage
<?xml version="1.0" encoding="UTF-8"?>
<pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="WeHelp.View.ForgotPassword"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup">
<StackLayout Margin="12" Padding="24" BackgroundColor="White" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<StackLayout>
<Label Text="Please type your Email. Once you click on 'Send', please check your inbox or spam folder" />
<Entry Placeholder="Email" Keyboard="Email"/>
</StackLayout>
<Button Text="Send" Command="{Binding SendPassword}"/>
</StackLayout>
</pages:PopupPage>
So, my problem occurs at the command
await Application.Current.MainPage.Navigation.PushPopupAsync(new ForgotPassword());
I get the error Argument 2: cannot convert from 'WeHelp.View.ForgotPassword' to 'Rg.Plugins.Popup.Pages.PopupPage'
change your xaml to something like this
<pages:PopupPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-
namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
x:Class="HealthMobile.PopPages.ForgotPassowordPop">
</pages:PopupPage>
and page.cs to
public partial class ForgotPassowordPop : PopupPage
{
}
Related
I need to create a standard layout which I can use on a number of pages, but pass in content which is shown inside... I'm having limited success...
Here's how I'm calling the custom control...
<?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:control="clr-namespace:myApp"
x:Class="myApp.MainPage">
<StackLayout>
<control:CustomPopupLayoutControl
BackgroundColor="LightGreen">
<control:CustomPopupLayoutControl.Content>
<Button Text="Hello" />
<!-- lots of other controls, buttons, labels etc, layout -->
</control:CustomPopupLayoutControl.Content>
</control:CustomPopupLayoutControl>
</StackLayout>
</ContentPage>
So you can see here I wan't to display some content..
Here's my custom control...
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.CustomPopupLayoutControl"
x:Name="CustomPopupLayouts">
<ContentView.Content>
<StackLayout BackgroundColor="LightBlue" Margin="30">
<Button Text="Close" /><!-- important-->
<!-- lots of other layout -->
<StackLayout BackgroundColor="Red" x:Name="Inner">
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
So I'd like to show my hello button inside the Inner stack layout, I'll also need binding to work...
So the final page should look like...
<page>
<StackLayout BackgroundColor="LightBlue" Margin="30">
<Button Text="Close" /><!-- important-->
<StackLayout BackgroundColor="Red">
<button text="Hello">
</StackLayout>
<StackLayout>
</page>
Since you had defined a Button in Custom Control , You just need to pass the Title that you want to display on the Button from ContentPage to Custom Control.
in CustomPopupLayoutControl.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.CustomPopupLayoutControl"
x:Name="CustomPopupLayouts">
<ContentView.Content>
<StackLayout BackgroundColor="LightBlue" HeightRequest="450" Margin="30">
<Button HeightRequest="150" WidthRequest="80" Text="{Binding Source={x:Reference CustomPopupLayouts}, Path=ButtonTitle}" /><!-- important-->
<StackLayout BackgroundColor="Red" HeightRequest="300" x:Name="Inner">
</StackLayout>
</StackLayout>
</ContentView.Content>
</ContentView>
in CustomPopupLayoutControl.xaml.cs
Define a bindable property
public static BindableProperty ButtonTitleProperty =
BindableProperty.Create(nameof(ButtonTitle), typeof(string), typeof(CustomPopupLayoutControl),string.Empty);
public string ButtonTitle
{
get => (string)GetValue(ButtonTitleProperty);
set => SetValue(ButtonTitleProperty, value);
}
in ContentPage
You could set the title of button directly or use data binding .
<control:CustomPopupLayoutControl ButtonTitle = "Hello World!" BackgroundColor="LightGreen" / >
I've found a solution :)
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myapp.CustomPopupLayoutControl"
x:Name="CustomPopupLayouts">
<ContentView.ControlTemplate>
<ControlTemplate>
<StackLayout BackgroundColor="LightBlue" Margin="30">
<Frame CornerRadius="5" Margin="20" HasShadow="False"
BackgroundColor="Red">
<StackLayout>
<Button Text="Close"
Command="{TemplateBinding
Parent.BindingContext.NavigateCommand}" />
<!-- important-->
<ContentPresenter />
</StackLayout>
</Frame>
</StackLayout>
</ControlTemplate>
</ContentView.ControlTemplate>
</ContentView>
.
<?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:control="clr-namespace:myapp"
x:Class="myapp.MainPage">
<StackLayout>
<control:CustomPopupLayoutControl BackgroundColor="LightGreen">
<StackLayout>
<Button Text="Hello" Command="{Binding NavigateCommand}" />
<Button Text="Goodbye" />
</StackLayout>
</control:CustomPopupLayoutControl>
</StackLayout>
</ContentPage>
I have a Page with a StackLayout using BindableLayout.ItemsSource, inside each item I have a ListView, for each item in this nested ListView I need to do a Binding to a property on the Page's ViewModel. I'm trying to use the Source+Path approach but the app crashes as soon as I open the page that contains this structure.
MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="BindableLayoutReferenceBug.ListViewPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:BindableLayoutReferenceBug">
<StackLayout BindableLayout.ItemsSource="{Binding Items}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<local:MessageListViewTemplate />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ContentPage>
MessageListViewTemplate.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="listView">
<ListView ItemsSource="{Binding Options}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding .}" />
<Button
BackgroundColor="Blue"
Command="{Binding Source={x:Reference Name=listView}, Path=Parent.BindingContext.ShowMessageCommand}"
CommandParameter="{Binding .}"
Text="Show Message" />
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView>
The exception shows that there is a problem finding the reference to the x:Name I used: Can not find the object referenced by listView
This only happens when I have a nested structure as I mentioned (StackLayout with BindableLayout > ListView). I'm not sure if this is not a supported scenario or if it is a bug.
I tried using different Paths for the binding, but since this is a problem parsing the XAML not finding the x:Name referenced, I don't think it even starts evaluating my Path.
Am I doing something wrong or is this a bug in Xamarin.Forms?
Xamarin.Forms version used: 3.6.0.293080
Repro sample: https://github.com/akamud/BindableLayoutReferenceBug
Maybe, x:Name wasn't recognized at runtime even though you have set it to your content view in XAML. You could raise an issue on GitHub.
Here I tried this binding using code behind and it works fine. You could use it as an alternative:
public MessageListViewTemplate()
{
InitializeComponent();
listView.ItemTemplate = new DataTemplate(() =>
{
ViewCell viewCell = new ViewCell();
Label label = new Label();
label.SetBinding(Label.TextProperty, new Binding("."));
Button button = new Button() { Text = "Show Message", BackgroundColor = Color.Blue };
button.SetBinding(Button.CommandProperty, new Binding("Parent.BindingContext.ShowMessageCommand", source: ContentView));
button.SetBinding(Button.CommandParameterProperty, new Binding("."));
viewCell.View = new StackLayout
{
Children =
{
label,
button
}
};
return viewCell;
});
}
XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="BindableLayoutReferenceBug.MessageListViewTemplate"
x:Name="ContentView">
<StackLayout>
<ListView ItemsSource="{Binding Options}" x:Name="listView" HasUnevenRows="True">
</ListView>
</StackLayout>
</ContentView>
Here, I defined the parent content view as ContentView and named ListView listView.
Please notice that the list view's data template should be a view cell. So I used a view cell to wrap the content here.
It was a bug after all, it is now fixed and the above should work: https://github.com/xamarin/Xamarin.Forms/issues/6192
I am developing a cross platform app using xaml forms and have yet to figured out how to call a ContentView ontapGesture. I am able to call ContentView on xaml forms using <local:MailRoomList/> but I cant show a view ontap or on click event.
I have tried to show view on stacklayout using StackLayoutID.Children.Add(new MailRoomList()); but thats not what I want. I want to show full contentview on content page with back button enabled.
Please advise. Thank you
MailRoomList
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="xx.xxxx.xx.Views.MailRoomList">
<ContentView.Content>
<StackLayout>
<Label Text="This is my MailRoom List" />
</StackLayout>
</ContentView.Content>
</ContentView>
OnTapGesture
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="OnRegisterMail" NumberOfTapsRequired="1"/>
</Frame.GestureRecognizers>
CodeBehind
async void OnRegisterMail(object sender, EventArgs e)
{
await Navigation.PushAsync(new MailRoomList());
}
Edit:
Home Page
<?xml version="1.0" encoding="utf-8" ?>
<xf1:BottomBarPage
xmlns:xf1="clr-namespace:xx.xxxx.xx.BottomBar"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:xx.xxxx.xx.Views"
x:Class="xx.xxxx.xx.Views.Home">
<ContentPage x:Name="MailRoomPage" Title="MailRoom" Icon="mailroom.png" xf1:BottomBarPageExtensions.TabColor="Orange" xf1:BottomBarPageExtensions.BadgeCount="5">
<local:MailRoom/>
</ContentPage>
<ContentPage Title="Transport" Icon="transport.png" xf1:BottomBarPageExtensions.TabColor="Green" xf1:BottomBarPageExtensions.BadgeCount="10">
<Label VerticalOptions="Center" HorizontalTextAlignment="Center" Text="Transport" FontSize="Large"/>
</ContentPage>
<ContentPage Title="Communication" Icon="communication.png" xf1:BottomBarPageExtensions.TabColor="Blue" xf1:BottomBarPageExtensions.BadgeCount="3">
<Label VerticalOptions="Center" HorizontalTextAlignment="Center" Text="Communication" FontSize="Large"/>
</ContentPage>
<ContentPage Title="HSE" Icon="hse.png" xf1:BottomBarPageExtensions.TabColor="Red" xf1:BottomBarPageExtensions.BadgeCount="2">
<Label VerticalOptions="Center" HorizontalTextAlignment="Center" Text="HSE" FontSize="Large"/>
</ContentPage>
<ContentPage Title="Meeting" Icon="meeting.png" xf1:BottomBarPageExtensions.TabColor="DarkCyan" xf1:BottomBarPageExtensions.BadgeCount="5">
<Label VerticalOptions="Center" HorizontalTextAlignment="Center" Text="Meeting" FontSize="Large"/>
</ContentPage>
From homepage above I am calling my view <local:MailRoom/> which is calling another view MailRoomList. This is where I need your advice on how to call a content view from another content view.
ContentView is a View not a Page. Change your MainRoomList Xaml to this
<?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="xx.xxxx.xx.Views.MailRoomList">
<StackLayout>
<Label Text="This is my MailRoom List" />
</StackLayout>
</ContentPage>
View's dont support navigation, they can only be navigated to if they belong to a Page which are the objects that contain the navigation infrastructure.
IF you want to mantain your MailRoomList as a ContentView, then you need to include her in some other page, and perform the navigation ( inside the tap method ) to that other page
The Image Source="icons.png" works fine
but when i insert this code: Button Image="icons.png"
the App won't display in the Xamarin live
This is my code:
<?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="Project.CustomImages">
<StackLayout>
<Button Image="icons.png"/>
<Label Text="TEST"/>
<Image Source="icons.png"/>
</StackLayout>
</ContentPage>
This is the screen shot of Recources/drawable:
Screenshot
Try to place an image instead of button.
You can make use of Tap Gesture Recogniser to take click events.
Try something like this :
<Image Source="icons.jpg">
<Image.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnTapped" />
</Image.GestureRecognizers>
</Image>
The code for the event handler
void OnTapped(object sender, EventArgs args) {
// Do something
DisplayAlert ("Alert", "Image Button Clicked", "OK");
}
I have a Forms XAML Page and in there I have a listview, and each element has a Switch (xamarin default). I can bind the data from the items to the listview, but I cannot subscrive the Switch event "Toggled", as it causes the item not to show. I also tried with ICommand and Command, as it is instructed to do with buttons, but the result is the same, nothing shown. How can I handle the switch toggle from the my viewmodel?
View
<?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="TouristicWallet.Views.WalletManagementPage"
xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
xmlns:converters="clr-namespace:TouristicWallet.Converters"
>
<ContentPage.BindingContext>
<vm:WalletManagementViewModel x:Name="ViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="MyCurrencies" ItemsSource="{Binding Currencies, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Currency.Initials, Mode=OneWay}" />
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="{Binding Toggled}"
/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
ViewModel
public class WalletManagementViewModel : ViewModelBase
{
private readonly List<OwnedCurrencyWrapper> _currencies = new List<OwnedCurrencyWrapper>();
public List<OwnedCurrencyWrapper> Currencies { get { return _currencies; } }
public WalletManagementViewModel()
{
CurrencyDataAccess cda = new CurrencyDataAccess();
foreach (var item in cda.GetCurrencies())
{
Currencies.Add(new OwnedCurrencyWrapper(item));
}
OnPropertyChanged(nameof(Currencies));
}
public class OwnedCurrencyWrapper
{
public Currency Currency { get; private set; }
public Boolean IsOwned { get; set; }
public ICommand Toggled { get; set; }
public OwnedCurrencyWrapper(Currency currency)
{
Currency = currency;
WalletDataAccess wda = WalletDataAccess.Instance;
IsOwned = wda.IsOwned(Currency.Id);
Toggled = new Command(() => Update());
}
public void Update()
{
WalletDataAccess wda = WalletDataAccess.Instance;
if (IsOwned) wda.RemoveOwnedCurrency(Currency.Id);
else wda.OwnCurrency(Currency.Id);
}
public void Toggled_handler(object sender, ToggledEventArgs e)
{
Update();
}
}
}
I am not using any mvvm framework
First off a Switch can not bind to a Command. See:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/#Commanding_with_ViewModels
From the above, the Forms controls that can bind to an ICommand are:
Button
MenuItem
ToolbarItem
SearchBar
TextCell (and hence also
ImageCell )
ListView
TapGestureRecognizer
you can just do the following to run code in the View's code behind file, do this in the XAML:
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="Handle_Toggled" />
And then in the Code behind file:
void Handle_Toggled(object sender, Xamarin.Forms.ToggledEventArgs e)
{
// Do stuff
}
Alternately, since you are binding, you could run code in the actual OwnedCurrencyWrapper class (which is what you seem to want) just by adding code to the setter for IsOwned. IN this case, don't assign anything to the Toggled property of your switch::
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}" />
And then in your OwnedCurrencyWrapper class:
bool _isOwned;
public bool IsOwned {
get
{
return _isOwned;
}
set
{
_isOwned = value;
// Do any other stuff you want here
}
}
That said, your binding is not complete since your view model is not implementing INotifyPropertyChanged so changes made directly to the view model will not be reflected in the UI. For more info on binding with Forms MVVM, see:
https://developer.xamarin.com/guides/xamarin-forms/xaml/xaml-basics/data_bindings_to_mvvm/
UPDATE: I was not aware of Behaviors in Xamarin Forms. See:
https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior
In the context of commanding, behaviors are a useful approach for connecting a control to a command. In addition, they can also be used to associate commands with controls that were not designed to interact with commands. This sample demonstrates using a behavior to invoke a command when an event fires.
So this should allow you to bind the Toggled event to a Command.
If you adhere to Prism framework you may easily wire an event to a command. Your xaml will look like in the following example.
<?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:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
x:Class="TouristicWallet.Views.WalletManagementPage">
<ContentPage.Content>
<StackLayout VerticalOptions="CenterAndExpand" Padding="20">
<Switch IsToggled="{Binding IsOwned}" x:Name="IsOwnedSwitch">
<Switch.Behaviors>
<b:EventToCommandBehavior EventName="Toggled" Command="{Binding ToggleIsOwnedCommand}"/>
</Switch.Behaviors>
</Switch>
</StackLayout>
</ContentPage.Content>
</ContentPage>
As others have mentioned, you should bind the Toggled event to an eventHandler behavior which will forward a command. The code below can be used.
<Switch IsToggled="{Binding SwitchEnabled}" x:Name="MySwitch">
<Switch.Behaviors>
<!-- behaviors namespace comes from "Xamarin.Forms Behaviors" nuget -->
<behaviors:EventHandlerBehavior EventName="Toggled">
<behaviors:InvokeCommandAction Command="{Binding ToggleSwitchCommand}" />
</behaviors:EventHandlerBehavior>
</Switch.Behaviors>
</Switch>
Solution : After doing some R&D i found the root cause of this issue,
Error Code in very first post:
<Switch IsToggled="{Binding IsOwned, Mode=TwoWay}"
Toggled="{Binding Toggled}"
/>
Just do Two steps.
Declare event listener function OnToggled in ContentPage class and not into your ViewModel class that you need to bind
In your ContentPage class
void OnToggled(object sender, ToggledEventArgs e){
}
change Toggled="{Binding Toggled}" == to ==> Toggled="OnToggled"
it will fix the issue, Don't know why it don't work for event listener function declared in ViweModel class.
--I hope it will work.
I had the same issue and solved it in a very easy way.
=> Goal: Get items with a switch control in a listview to respond to a command.
<?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="TouristicWallet.Views.WalletManagementPage"
xmlns:vm="clr-namespace:TouristicWallet.ViewModels"
x:Name="pageName"
xmlns:converters="clr-namespace:TouristicWallet.Converters"
>
<ContentPage.BindingContext>
<vm:WalletManagementViewModel x:Name="ViewModel"/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<converters:CurrencyIdToCodeConverter x:Key="idToCodeConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="MyCurrencies" ItemsSource="{Binding Currencies, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Currency.Initials, Mode=OneWay}" />
<Switch IsToggled="{Binding Selected}" HorizontalOptions="Start">
<Switch.Behaviors>
<b:EventToCommandBehavior
EventName="Toggled" Command="
{Binding
Path=BindingContext.SendCommand,
Source={x:Reference
Name=pageName}}" />
</Switch.Behaviors>
</Switch>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
In ViewModel
Define your Command /ICommand
public ICommand SendCommand { get; set; }
SendCommand = new Command(() => //do something.....);
Please Take special note of the areas in bold.