Trying to display a popup gives "The Parent must be of type Microsoft.Maui.Handlers.PageHandler." - mvvm

Trying to follow this example to display a custom popup but using MVVM and Shell gives me the error in the title:
https://www.youtube.com/watch?v=yM7opXlu-MU&ab_channel=GeraldVersluis
namespace MyPopupTest
{
public partial class MyViewModel : ObservableObject
{
public MyViewModel()
{
DisplayMyPopup();
}
private void DisplayMyPopup()
{
var popup = new MyPopup();
Shell.Current.ShowPopup(popup);
}
}
}
the popup
using CommunityToolkit.Maui.Views;
namespace MyPopupTest;
public partial class MyPopup : Popup
{
public MyPopup()
{
InitializeComponent();
}
}
This results in an exception: The Parent must be of type Microsoft.Maui.Handlers.PageHandler.
and the stack trace:
at CommunityToolkit.Maui.Core.Views.MauiPopup.SetElement(IPopup element) in /_/src/CommunityToolkit.Maui.Core/Views/Popup/MauiPopup.macios.cs:line 71
at CommunityToolkit.Maui.Core.Handlers.PopupHandler.ConnectHandler(MauiPopup platformView) in /_/src/CommunityToolkit.Maui.Core/Handlers/Popup/PopupHandler.macios.cs:line 91
at Microsoft.Maui.Handlers.ElementHandler`2[[CommunityToolkit.Maui.Core.IPopup, CommunityToolkit.Maui.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[CommunityToolkit.Maui.Core.Views.MauiPopup, CommunityToolkit.Maui.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].OnConnectHandler(Object platformView)
at Microsoft.Maui.Handlers.ElementHandler.ConnectHandler(Object platformView)
at Microsoft.Maui.Handlers.ElementHandler.SetVirtualView(IElement view)
at Microsoft.Maui.Controls.Element.SetHandler(IElementHandler newHandler)
at Microsoft.Maui.Controls.Element.set_Handler(IElementHandler value)
at Microsoft.Maui.Platform.ElementExtensions.ToHandler(IElement view, IMauiContext context)
at CommunityToolkit.Maui.Views.PopupExtensions.CreatePopup(Page page, Popup popup) in /_/src/CommunityToolkit.Maui/Views/Popup/PopupExtensions.shared.cs:line 59
at CommunityToolkit.Maui.Views.PopupExtensions.ShowPopup[LayingTrackPopup](Page page, LayingTrackPopup popup) in /_/src/CommunityToolkit.Maui/Views/Popup/PopupExtensions.shared.cs:line 27
at MyPopupTest.MyViewModel.DisplayLayingPopup() in /Users/…

Are you sure You are following all steps? Like setting all paths in xaml, setting good references. I have recreated this task but was not able to get this error.
Working example:
Downloading from nuget CommunityToolkit.Maui (version 2.0.0), CommunityToolkit.Mvvm (version 8.0.0)
MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
.UseMauiCommunityToolkit();
return builder.Build();
}
}
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MauiPopup"
x:DataType="viewmodels:MainPageViewModel"
x:Class="MauiPopup.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Command="{Binding ShowPopupCommand}"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainPageViewModel();
}
}
MainPageViewModel.cs
internal class MainPageViewModel : ObservableObject
{
public ICommand ShowPopupCommand { get; }
public MainPageViewModel()
{
//ShowPopupCommand = new Command(ShowPopup);
ShowPopup();
}
private void ShowPopup()
{
var popup = new PopupPage();
Shell.Current.ShowPopup(popup);
}
}
PopupPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<mct:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mct="clr-namespace:CommunityToolkit.Maui.Views;assembly=CommunityToolkit.Maui"
x:Class="MauiPopup.PopupPage">
<StackLayout>
<Label Text="Welcome to Maui Popup!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</mct:Popup>
PopupPage.xaml
public partial class PopupPage : Popup
{
public PopupPage()
{
InitializeComponent();
}
}

Related

NET MAUI how to create a hamburger menu

I am trying to create a hamburger menu in xaml, but zero success. I was trying to use the samples provided [here][1], but zero success.
My idea is to create a view model that has a list of FlyoutItems then inject this view model to the AppShell.
public partial class ShellViewModel : ObservableObject
{
public List<FlyoutItem> FlyoutItems { get; private set; } = new List<FlyoutItem>();
public ShellViewModel()
{
AddMenuItems();
}
private void AddMenuItems()
{
var flyoutItems = new List<FlyoutItem>
{
new FlyoutItem
{
Title = "Item 1"
},
new FlyoutItem
{
Title = "Item 2"
}
};
FlyoutItems.AddRange(flyoutItems);
}
}
public partial class AppShell : Shell
{
private ShellViewModel viewModel => BindingContext as ShellViewModel;
public AppShell(ShellViewModel viewModel)
{
BindingContext = viewModel;
RegisterRoutes();
InitializeComponent();
}
private void RegisterRoutes()
{
Routing.RegisterRoute(PageRoutes.LoginPage, typeof(LoginPage));
Routing.RegisterRoute(PageRoutes.RegisterPage, typeof(RegisterPage));
Routing.RegisterRoute(PageRoutes.HomePage, typeof(MainPage));
Routing.RegisterRoute(PageRoutes.DetailsPage, typeof(PlayerDetailsPage));
Routing.RegisterRoute(PageRoutes.AddOrUpdatePage, typeof(AddOrUpdatePlayer));
}
}
In the XAML sometign like this
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiUI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiUI"
xmlns:pages="clr-namespace:MauiUI.Pages">
<Shell.ItemTemplate>
<DataTemplate>
<CollectionView BindingContext="{x:Reference shell}"
IsGrouped="True"
ItemsSource="{Binding FlyoutItems}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Title}"
TextColor="White"
FontSize="18" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</DataTemplate>
</Shell.ItemTemplate>
<ShellContent
Title="Amazons of Volleyball"
ContentTemplate="{DataTemplate pages:SplashPage}"
Route="HomePage" />
</Shell>
thnx
[1]: https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/shell/flyout?view=net-maui-7.0
I am trying to create a hamburger menu in xaml, but zero success.
You can add this code in your AppShell.xaml:
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiUI.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiUI"
xmlns:views="clr-namespace:MauiUI.Pages">
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<ShellContent Title="Home"
Route="home"
ContentTemplate="{DataTemplate local:MainPage}" />
<ShellContent Title="NewPage1"
ContentTemplate="{DataTemplate pages:NewPage1}" />
</FlyoutItem>
</Shell>
Or you can add this code in your AppShell.xaml.cs:
public partial class AppShell : Shell    
{
  public AppShell ()        
{
        InitializeComponent ();
        
        FlyoutItem flyoutItem = new FlyoutItem ();
        flyoutItem.FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems;
        
        flyoutItem.Items.Add (new ShellContent () { Title = "NewPage1", Content = new NewPage1 () });
        flyoutItem.Items.Add (new ShellContent () { Title = "home", Content = new MainPage () });
        
        myshell.Items.Add (flyoutItem);
        
}
    
}

Child property of an ObservableProperty is not updating

Something isn't right with the XAML but it's not sticking out at me.
I've been working on the layout of one of my .net Maui XAML pages. I added a collectionView when I noticed that the top data was no longer showing. The other pages are working fine.
What's weird is that the data is there and while running the app in debug mode if I highlight, shift-delete, then paste it back in the bound data appears. I also noticed if I change the {Binding EditEvent.name} by removing the "name" from EditEvent then adding it back on, the view displays the data as well.
But if I leave and navigate back in the data won't show up until I repeat the above process. It's like the viewModel isn't updating the view when the data changes. But if I force the view to update by deleting and re-pasting it will show it.
Anyone have an idea what possibly could be the issue?
I've got 2 ObservableProperties in my ViewModel:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Newtonsoft.Json;
using SharedModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MyApp.ViewModels
{
public partial class EditEventViewModel : ObservableObject
{
#region XAML page Observables
[ObservableProperty]
attEventDx editEvent;
[ObservableProperty]
ObservableCollection<groupReturn> groupsItems;
#endregion
// pass object to edit into this view
public async void SetEditEvent(attEventDx incomingEvent)
{
editEvent = incomingEvent;
//await LoadGroupsAsync();
}
...
}
And this is the view:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Pages.EditEventPage"
Title="Edit Event"
xmlns:viewmodel="clr-namespace:MyApp.ViewModels"
xmlns:dm="clr-namespace:SharedModels;assembly=SharedModels"
x:DataType="viewmodel:EditEventViewModel"
NavigatedTo="ContentPage_NavigatedTo">
<VerticalStackLayout>
<Grid HorizontalOptions="Center" VerticalOptions="Start" Padding="0,40,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Label Text="Event Name" VerticalOptions="Center" HorizontalOptions="Center" Grid.Column="0" Grid.Row="0"/>
<Entry Text="{Binding EditEvent.name}" WidthRequest="200" Grid.Column="1" Grid.Row="0"/>
<Label Text="Event Date" VerticalOptions="Center" HorizontalOptions="Center" Grid.Column="0" Grid.Row="1"/>
<Entry Text="{Binding EditEvent.happeningOn}" WidthRequest="200" Grid.Column="1" Grid.Row="1"/>
</Grid>
<Label Text="Selectable Groupings" VerticalOptions="Center" HorizontalOptions="Center" Padding="20"/>
<CollectionView ItemsSource="{Binding GroupsItems}" SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="dm:groupReturn">
<SwipeView>
<SwipeView.RightItems>
<SwipeItem Text="Delete" BackgroundColor="Red"/>
</SwipeView.RightItems>
<Grid Padding="0,5">
<Label Text="Groups"/>
<ScrollView>
<Frame>
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:EditEventViewModel}}, Path=TapCommand}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<Label Text="{Binding groupName}" FontSize="20" FontAttributes="Bold"/>
</Frame>
</ScrollView>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
this is my xaml.cs for that page:
public partial class EditEventPage : ContentPage, IQueryAttributable
{
EditEventViewModel _vm;
attEventDx _editEvent;
public EditEventPage( EditEventViewModel vm)
{
InitializeComponent();
_vm = vm;
BindingContext = _vm;
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
_editEvent = query["EditEvent"] as attEventDx;
}
private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{
_vm.SetEditEvent(_editEvent);
}
}
attEventDx for reference (sits in another shared project between Azure Functions and the mobile app):
namespace SharedModels
{
public class attEventDx
{
public Guid? publicId { get; set; }
public int? createdBy { get; set; }
public string name { get; set; }
public DateTime dateCreated { get; set; }
public DateTime? happeningOn { get; set; }
}
}
As I referred to this is the page that IS working:
xaml.cs:
public partial class EventPage : ContentPage
{
EventViewModel _vm;
public EventPage(EventViewModel vm)
{
InitializeComponent();
_vm = vm;
BindingContext= _vm;
}
private async void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{
await _vm.LoadEventData();
}
private void ImageButton_Clicked(object sender, EventArgs e)
{
}
}
ViewModel:
public partial class EventViewModel : ObservableObject
{
#region XAML page Observables
[ObservableProperty]
ObservableCollection<attEventDx> eventItems;
[ObservableProperty]
attEventDx selectedEvent;
[ObservableProperty]
string text;
#endregion
public EventViewModel()
{
//EventItems = new ObservableCollection<attEventDx>();
}
[RelayCommand]
public async Task LoadEventData()
{
MyApp.globals.SetHttpClient();
try
{
var response = await MyApp.globals.httpClient.GetAsync(MyApp.globals.APIURL + "getEvents");
var allEvents = response.Content.ReadAsStringAsync().Result;
if (allEvents != null)
{
List<attEventDx> listOfEvents = JsonConvert.DeserializeObject<List<attEventDx>>(allEvents);
if (listOfEvents != null)
{
EventItems = new ObservableCollection<attEventDx>(listOfEvents);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message + "\r\b" + ex.StackTrace);
}
}
[RelayCommand]
async Task Add()
{
await Shell.Current.GoToAsync($"{nameof(AddEventPage)}");
}
[RelayCommand]
async Task Tap(attEventDx sender)
{
selectedEvent = sender;
var navigationParameter = new Dictionary<string, object>
{
["EditEvent"] = selectedEvent
};
await Shell.Current.GoToAsync($"{nameof(EditEventPage)}", navigationParameter);
}
[RelayCommand]
async Task Refresh()
{
await LoadEventData();
}
}
And the view of the working page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.EventPage"
Title="Events"
xmlns:viewmodel="clr-namespace:MyApp.ViewModels"
xmlns:dm="clr-namespace:SharedModels;assembly=SharedModels"
x:DataType="viewmodel:EventViewModel"
NavigatedTo="ContentPage_NavigatedTo">
<Grid RowDefinitions="100, Auto, 30, *"
ColumnDefinitions=".50*, .25*, .25*"
Padding="10">
<Image Grid.ColumnSpan="3"
Source="logo.png"
BackgroundColor="Transparent"/>
<ImageButton Source="plus.png" Grid.Row="0" Grid.Column="2" Scale=".7" Command="{Binding AddCommand}"></ImageButton>
<Label Text="New Event" Grid.Column="2" Grid.Row="0" HorizontalOptions="Center" VerticalOptions="End"></Label>
<!--<Entry Placeholder="Enter Text" Grid.Row="1" Text="{Binding Text}" />-->
<!--<Button Text="Search" Grid.Row="1" Grid.Column="1" />-->
<!--<Button Text="Add" Grid.Row="1" Grid.Column="2" Command="{Binding AddCommand}"/>-->
<Label Text="Upcoming Events" FontSize="22" Grid.Row="2"/>
<!--<Button Text="Refresh" Grid.Row="2" Grid.Column="2" Command="{Binding RefreshCommand}"/>-->
<CollectionView Grid.Row="3" Grid.ColumnSpan="3" ItemsSource="{Binding EventItems}" SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="dm:attEventDx">
<SwipeView>
<SwipeView.RightItems>
<SwipeItem Text="Delete" BackgroundColor="Red"/>
</SwipeView.RightItems>
<Grid Padding="0,5">
<Label Text="Event"/>
<ScrollView>
<Frame>
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:EventViewModel}}, Path=TapCommand}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
<Label Text="{Binding name}" FontSize="20" FontAttributes="Bold"/>
</Frame>
</ScrollView>
<Label Text="{Binding happeningOn}" HorizontalOptions="End" VerticalOptions="Center" Padding="0,0,5,0"></Label>
</Grid>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
Well, what ToolmakerSteve told me kind of worked, the items were initially displaying but not updating.
I then decided to build out another page in the app and do some experimenting along the way and figured out the issue.
When I created the new page by hand, I still had this issue and was doubting my sanity. I then partially copied in the page that was working and it worked! In comparing the two pages closely, I finally discovered what the problem was.
I was right, the CommunityToolkit's [ObservableProprerty] DOES work for all child items in an object; this is why I selected using this library from the start. I wasn't going crazy... (At least not on this)
This particular app was started a few months ago but then I got pulled into another project in another platform for a few months so what I had learned was partially forgotten when I picked it back up recently.
When you define a [ObservableProperty] like this:
[ObservableProperty]
myObject usedVariable;
The "usedVariable" will contain the data, but not the framework for INotifyPropertyChanged. CommunityToolkit builds out the framework on "UsedVariable".
While this code is "legal" in the ViewModel:
usedVariable = new myObject();
It will assign the data, but not the notification framework.
Instead it needs to be:
UsedVariable = new myObject();
Once the variable is defined with lowercase, you will never reference the variable that way again (as far as I can tell anyway). Instead, you will use the uppercase "UsedVariable".
When I referenced the lowercase version of the variable, I didn't see the data on the app page when it started. However, if I had the page open and I removed the XAML code for that control and pasted it back in, the data did show.
It's always something simple that causes the most grief...

Passing data to CommunityToolkit.Maui Popup

How do I pass data to a CommunityToolkit Popup in a .Net MAUI app?
Documentation shows how to send a result from Popup back to the page but doesn’t show how to pass data to the Popup.
I made the following Based on Gerald Versluis video:
toolkit popup Xaml:
<toolkit:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:model="modelnamespace"
x:Class="namespace.PopUpSelectService">
<VerticalStackLayout>
<CollectionView x:Name="selectService"
HorizontalOptions="Center"
VerticalOptions="Center">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:sal_ServiceResponse">
<Grid RowDefinitions="auto,auto,auto,auto,auto">
<Button Text="{Binding nombre_servicio}"
Grid.Row="0"
Clicked="Button_Clicked" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</toolkit:Popup>
PopupSelectService .cs
public partial class PopUpSelectService
{
public PopUpSelectService(List<salServiceDTO> sal_Service)
{
InitializeComponent();
selectService.ItemsSource = sal_Service;
}
private void Button_Clicked(object sender, EventArgs e)
{
this.Close(((Button)sender).Text);
}
}
How i call the popup In my viewmodel:
var popup = new PopUpSelectService(response.sal_Service);
var result = await Shell.Current.ShowPopupAsync(popup);
and also in var result you get the value you select on the popup!

Xamarin Forms - Set BindingContext of a controltemplate

I have a xamarin forms application. It has a tabbedpage within it multiple tabs. The tabbedpage and te tabs, each of them has their own viewmodel as a bindingcontext.
In the app.xaml I defined a controltemplate. I use this control template in each tab, because I want each of those tabs to have a button at the bottom of the page.
At this moment: the button in the controltemplate binds with a property defined in each tab. But I want the button to bind at one place. Isn't it possible to create a viewmodel special for the controltemplate and bind the button defined in the controltemplate with that viewmodel?
Current code:
<ControlTemplate x:Key="ActivityStatusButton">
<StackLayout>
<ContentPresenter>
</ContentPresenter>
<StackLayout VerticalOptions="EndAndExpand" HorizontalOptions="Fill" Padding="15">
<Button Style="{StaticResource RedBackGroundWithWhiteTextButtonStyle}" Command="{TemplateBinding BindingContext.ClickOnStatusButton, Mode=TwoWay}" Text="{TemplateBinding BindingContext.ok, Mode=TwoWay}"></Button>
</StackLayout>
</StackLayout>
</ControlTemplate>
A typical tab:
<ContentPage ...>
<ContentPage.Content>
<Label Text="hello"></Label>
</ContentPage.Content>
<!--The control template is placed here (the button) -->
You could create a Custom Control (a subclass of ContentView) like
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Name="template"
x:Class="App24.MyControlTemplate">
<ContentView.Content>
<StackLayout VerticalOptions="EndAndExpand" HorizontalOptions="Fill" Padding="15">
<Button Clicked="Button_Clicked" Command="{Binding Source={x:Reference template},Path=ButtonCommand}" Text="{Binding Source={x:Reference template},Path=ButtonText}" CommandParameter="{Binding Source={x:Reference template},Path=CommandParameter}" />
</StackLayout>
</ContentView.Content>
</ContentView>
using System;
using System.Windows.Input;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace App24
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MyControlTemplate : ContentView
{
public event EventHandler ButtonClick;
public static readonly BindableProperty ButtonTextProperty =
BindableProperty.Create("ButtonText", typeof(string), typeof(MyControlTemplate), default(string));
public string ButtonText
{
get => ((string)GetValue(ButtonTextProperty));
set => SetValue(ButtonTextProperty, value);
}
public static readonly BindableProperty ButtonCommandProperty =
BindableProperty.Create("ButtonCommand", typeof(ICommand), typeof(MyControlTemplate), null, BindingMode.Default, null);
public ICommand ButtonCommand
{
get => (ICommand)GetValue(ButtonCommandProperty);
set
{
SetValue(ButtonCommandProperty, value);
}
}
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.Create("CommandParameter", typeof(object), typeof(MyControlTemplate), null);
public object CommandParameter
{
get => (object)GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
public MyControlTemplate()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
ButtonClick?.Invoke(sender, e);
}
}
}
Now you could add it to any page and binding Text , Command or CommandParameter in code behind .
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<local:MyControlTemplate ButtonText="{Binding ButtonText}" ButtonCommand="{Binding ClickCommand}" CommandParameter="test" />
</StackLayout>

Using compiled bindings with Prism

I want to use compiled bindings in my Xamarin Forms app in combination with Prism.
I created a small xamarin forms app with a simple view, viewmodel and prism (prism:ViewModelLocator.AutowireViewModel="True"). Classic binding works as expected.
How should I implemented compiled binding without creating the binding context twice?
Classic binding with prism: HomePage.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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="CompiledBinding.Views.HomePage">
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding Name}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
HomePageViewModel.cs:
using Prism.Mvvm;
using Prism.Navigation;
using System;
using Xamarin.Forms;
namespace CompiledBinding.ViewModels
{
public class HomePageViewModel : BindableBase
{
string _name = "Compiled binding test";
public HomePageViewModel(INavigationService navigationService)
{
var nav = navigationService;
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
// Do something
Name = DateTime.Now.ToString("yyyy MMMM dd hh:mm:ss");
return true; // True = Repeat again, False = Stop the timer
});
}
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
}
}
Adding the binding context to the xaml page again, is not an option:
<?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"
xmlns:viewModels="clr-namespace:CompiledBinding.ViewModels"
x:Class="CompiledBinding.Views.HomePage"
x:DataType="viewModels:HomePageViewModel">
<ContentPage.BindingContext>
<viewModels:HomePageViewModel />
</ContentPage.BindingContext>
<StackLayout>
<!-- Place new controls here -->
<Label Text="{Binding Name}"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
Besides of defining the binding context again, it also results in the error: no public parameterless constructor.
Do I oversee something? Does anyone know how to work with compiled bindings together with prism?