How to have a Command(able) view or a Button with content in MAUI? - binding

I need a composite clickable MAUI component on which I can bind a command.
Buttons can't contain content, only text.
ContentViews don't have Command so I tried this way
//
public partial class BtnView : BaseContentView<BtnVm>
{
public ICommand Command { get; set; }
public object CommandParameter { get; set; }
public BtnView() : base()
{
InitializeComponent();
}
public static BindableProperty CommandProperty { get; set; } = BindableProperty.Create(nameof(Command), typeof(Command), typeof(BtnView));
public static BindableProperty CommandParameterProperty { get; set; } = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(BtnView));
}
This way, I hoped Command property to be bindable but:
I get an XFC0009 error when binding a Command
No property, BindableProperty, or event found for "Command", or mismatching type between value and property.
There's no mechanism to fire it by clicking.
How am I meant to implement this kind of component?

Like Xamarin Forms, Maui can attach a TapGestureRecognizer to any visual element. See also TapGestureRecognizer doc.
Add this to the ContentView in xaml:
<ContentView.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Tapped="OnTapped">
</TapGestureRecognizer>
</ContentView.GestureRecognizers>
Add this to the "code behind" in xaml.cs:
protected void OnTapped(object sender, EventArgs args)
{
// Do something here.
}

Related

Save data to firebase realtime from a picker's item which data is retrieved from firebase also, in xamarin form

My problem is how to save data from picker's value to firebase realtime. Anyway, Im super beginner to xamarin and so far I cant find any solution to my problem.
This is what I tried.
//my model
`
public int DeliveryPrice { get; set; }
public int PickUp_Price { get; set; }
public string gallonType { get; set; }
public string refillName { get; set; }
public int refill_id { get; set; }
public string waterType { get; set; }
`
//Code how I get the data from my firebase realtime db
`
public async Task<List<PRODUCT_REFILL>> GetAllProductRefill()
{
return
(await firebaseClient.Child
(nameof(PRODUCT_REFILL))
.OnceAsync<PRODUCT_REFILL>()).Select(item => new PRODUCT_REFILL
{
DeliveryPrice=item.Object.DeliveryPrice,
PickUp_Price=item.Object.PickUp_Price,
gallonType=item.Object.gallonType,
refillName=item.Object.refillName,
waterType=item.Object.waterType,
refill_id=item.Object.refill_id
}).ToList();
}
`
//my picker design UI
`
<Picker x:Name="Picker_ProductType"
ItemsSource="{Binding refillName}"
ItemDisplayBinding="{Binding refillName}">
<Picker.Items > <x:String >
</x:String>
<x:String>
</x:String>
</Picker.Items>
</Picker>
`
//my codebehind
`
Product_RefillRepo productrefill = new Product_RefillRepo();
async protected override void OnAppearing()
{
var productRefill = await productrefill.GetAllProductRefill();
Picker_ProductType.ItemsSource = productRefill;
}
`
//in my order button, which is also inside in code behind.cs
string orderProductType = Picker_ProductType.SelectedItem.ToString();
Then if click the order button, I got this error.
Edit; This is the error----- Error says; System.InvalidCastException: 'Specified cast is not valid.'
any link that will be commented that are related to my post, will highly appreciated, Thank you so much.

There is no registered service of type <ClassName>

I need to have communication between 2 components. I created a class that goes:
public interface IApplicationState
{
string PlateNumber { get; }
event Action OnPlateInput;
void SetPlateNumber(string plateNumber);
}
public class ApplicationState : IApplicationState
{
public string? PlateNumber { get; private set; }
public event Action OnPlateInput;
public void SetPlateNumber(string plateNumber)
{
PlateNumber = plateNumber;
NotifyPlateNumberChanged();
}
private void NotifyPlateNumberChanged() => OnPlateInput?.Invoke();
}
Then registered it in my Program.cs
builder.Services.AddScoped(sp => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
builder.Services.AddSingleton<IApplicationState, ApplicationState>();
Then called it in 2 of my components:
public partial class SideWidgetComponent : ComponentBase
{
[Inject] ApplicationState ApplicationState { get; set; }
private string _plateNUmber = string.Empty;
public async Task SetPlateNumber()
{
await Task.Run(() =>
{
if (_plateNUmber == string.Empty) return;
ApplicationState?.SetPlateNumber(_plateNUmber);
});
}
}
partial class PlateListComponent : ComponentBase
{
[Inject] private HttpClient? HttpClient { get; set; }
[Inject] private ApplicationState ApplicationState { get; set; }
protected override async Task OnInitializedAsync()
{
ApplicationState.OnPlateInput += ApplicationState_OnPlateInput;
}
}
When I start the program I get an error of
Cannot provide a value for property 'ApplicationState' on type 'ALPR_WebUi.Client.Pages.HomeComponents.PlateListComponent'. There is no registered service of type 'ALPR_WebUi.Shared.ApplicationState'.
You have registered the interface IApplicationState in Program.cs, but are trying to inject ApplicationState, ie the concrete type. Since you didn't register the concrete type, it doesn't know how to resolve it.
So, either register the concrete type (ie ApplicationState without the I) or inject the interface. Either way will work.

Template 10 dependency injection using MEF

I'm familiar with using MEF in .NET Framework 4.6.* but not in .NET Core. I'm messing about with the Hamburger template from Template 10 to see if it is suitable for my needs but I haven't been able to figure out how to compose my view models using MEF.
My question is how can I navigate to a view using the navigation service in such a way that its view model will be injected by MEF?
I have one way of getting this working but it seems a bit code smelly so better answers are welcomed. I created a static class holding an instance of the CompositionHost. It has a method for resolving imports. The view's code behind calls the static class to create its view model.
public static class Container
{
public static CompositionHost Host { get; set; }
public static T Get<T>()
{
T obj = Host.GetExport<T>();
Host.SatisfyImports(obj);
return obj;
}
}
In the App class:
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
var config = new ContainerConfiguration();
Container.Host = config.WithAssembly(GetType().GetTypeInfo().Assembly).CreateContainer();
await NavigationService.NavigateAsync(typeof(Views.MainPage));
}
In the view's code behind:
public sealed partial class MainPage : Page
{
private MainPageViewModel ViewModel { get; }
public MainPage()
{
InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
ViewModel = Container.Get<MainPageViewModel>();
DataContext = ViewModel;
}
}
My bad, I hadn't spotted this:
How do I use a Unity IoC container with Template10?
In the end, I went for a solution like this:
public interface IView
{
ViewModelBase ViewModel { get; }
}
[Export]
public sealed partial class MainPage : Page, IView
{
public ViewModelBase ViewModel
{
get
{
return VM as ViewModelBase;
}
}
[Import]
public MainPageViewModel VM { get; set; }
public MainPage()
{
InitializeComponent();
NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
}
}
And in the App.xaml.cs:
public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
var config = new ContainerConfiguration();
_container = config.WithAssembly(GetType().GetTypeInfo().Assembly).CreateContainer();
await NavigationService.NavigateAsync(typeof(Views.MainPage));
}
public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
_container.SatisfyImports(page);
return (page as IView)?.ViewModel;
}

MvvmCross & MvxAdapter & Polymorphic Types with custom controls

I am currently trying to implement my own version of the polymorphic types demo that is located here:
https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/Working%20With%20Collections
And I have it working as the demo shows. However, I am looking to extend that demo to have more complex controls inside of the MvxListView. I am wanting to have each of the list items control a fragment that has a View and a core ViewModel for additional processing.
I am unsure of the correct way of implementing this.
The code that I am using to create the custom view is this:
protected override View GetBindableView(View convertView, Object source, Int32 templateId)
{
var listItem = (TodayPanel) source;
if (listItem != null)
templateId = (Int32) typeof (Resource.Layout).GetField(listItem.View).GetValue(null);
return base.GetBindableView(convertView, source, templateId);
}
As always, it's probably something simple that I am missing, but any help would be appreciated.
Thanks!
I hate it when this happens, but after posting my question, I stepped away from the computer for a little bit and started to do something else. At that point, everything clicked into place. Stuart, in response to your question, the TodayPanel was NOT an MvxModelView, and therein was the crux of the problem. What I was doing was passing a list of TodayPanels into the listview, which was an SQLite entity object and not an MvxModelView object.
For others that might be struggling with this, I am going to post my solution here.
So here is what I ended up doing. I first created a class for each of the TodayPanel entity objects that inherited from an abstract base class that inherited from MvxModelView.
public abstract class TodayBaseViewModel : MvxViewModel
{
protected TodayViewModel TodayViewModel { get; set; }
protected IDataService DataService { get; set; }
public String Name { get; set; }
public String Title { get; set; }
public Boolean CanHide { get; set; }
public Boolean Visible { get; set; }
public Int32 SortOrder { get; set; }
public String View { get; set; }
protected abstract void SetEventHandlers();
protected BaseViewModel(IDataService dataService)
{
DataService = dataService;
}
public void Init(TodayViewModel todayViewModel)
{
TodayViewModel = todayViewModel;
SetEventHandlers();
}
}
I made it abstract as I wanted 0 or more event handlers to be attached in the final class. which is done through the abstract SetEventHandlers() method:
public class CoachSaysViewModel : TodayBaseViewModel
{
public CoachSaysViewModel(IDataService dataService)
: base(dataService)
{
}
protected override void SetEventHandlers()
{
TodayViewModel.ConnectionUpdated += TodayViewModelConnectionUpdated;
TodayViewModel.NewActivityReceived += TodayViewModelNewActivityReceived;
}
protected void TodayViewModelNewActivityReceived(Object sender, EventArgs.ActivityReceivedEventArgs e)
{
}
protected void TodayViewModelConnectionUpdated(Object sender, EventArgs.ConnectionUpdatedEventArgs e)
{
}
}
Then I created an extension method that converts the TodayPanel entity to one of the classes that inherits from TodayBaseViewModel.
public static BaseViewModel ToBaseViewModel(this TodayPanel todayPanel, TodayViewModel todayViewModel)
{
BaseViewModel model = null;
switch (todayPanel.View)
{
case "Today_QuickView":
model = Mvx.IocConstruct<QuickViewViewModel>();
break;
case "Today_CoachSays":
model = Mvx.IocConstruct<CoachSaysViewModel>();
break;
}
if (model == null)
return null;
model.CanHide = todayPanel.CanHide;
model.Name = todayPanel.Name;
model.SortOrder = todayPanel.SortOrder;
model.Title = todayPanel.Title;
model.View = todayPanel.View;
model.Visible = todayPanel.Visible;
model.Init(todayViewModel);
return model;
}
That then allowed me to create a list of MvxViewModels that are then bound to the MvxListView and hence are allowed to do the additional processing that I am wanting to do.
I'm sure that there are some improvements that I can do to the end result, and if you see anything feel free to point it out. :)

WP8 - Bind ProgressBar visibility

I have a simple thing to code, i checked other questions but couldn't it yet.
I have an application which loads some data from an xml file retrieved from the web, and then displays it inside a longlistselector.
I did it, it works, now i would like to add an indeterminate progressbar which stays active until I finished the data loading.
I enclosed the progressbar in a stackpanel, before my longlistselector, and i bound its visibility to the function ProgressBarVisibility (see code below).
<phone:PivotItem Header="Status">
<StackPanel>
<ProgressBar Value ="0" IsIndeterminate="True" Visibility="{Binding ProgressBarVisibility}"/>
<phone:LongListSelector Margin="0,0,-12,0" ItemsSource="{Binding PivotOne}">
<phone:LongListSelector.ItemTemplate>
<!-- lots of code here -->
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</StackPanel>
</phone:PivotItem>
In the MainViewModel.cs , that's how i wrote the thing.
using System.Windows;
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
this.PivotOne = new ObservableCollection<ItemViewModel>();
this.PivotTwo = new ObservableCollection<ItemViewModel>();
this.PivotThree = new ObservableCollection<ItemViewModel>();
}
/// <summary>
/// A collection for ItemViewModel objects.
/// </summary>
public ObservableCollection<ItemViewModel> PivotOne { get; private set; }
public ObservableCollection<ItemViewModel> PivotTwo { get; private set; }
public ObservableCollection<ItemViewModel> PivotThree { get; private set; }
private string _detailPageTitle = "Default";
/// <summary>
/// DetailPageTitle ritorna il titolo della pagina di dettaglio. Viene settato nella funzione che carica la pagina secondaria
/// </summary>
/// <returns></returns>
public string DetailPageTitle
{
get
{
return _detailPageTitle;
}
set
{
if (value != _detailPageTitle)
{
_detailPageTitle = value;
NotifyPropertyChanged("DetailPageTitle");
}
}
}
public bool IsDataLoaded
{
get;
private set;
}
private Visibility _progressBarVisibility = Visibility.Collapsed;
public Visibility ProgressBarVisibility
{
get
{
return _progressBarVisibility;
}
set
{
if (value != _progressBarVisibility)
{
_progressBarVisibility = value;
NotifyPropertyChanged("ProgressBarVisibility");
}
}
}
private Visibility _progressBarVisibility = Visibility.Visible;
public Visibility ProgressBarVisibility
{
get
{
return _progressBarVisibility;
}
set
{
if (value != _progressBarVisibility)
{
_progressBarVisibility = value;
NotifyPropertyChanged("ProgressBarVisibility");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public void LoadData()
{
//progressbar is visible, data not loaded
this.IsDataLoaded = false;
ProgressBarVisibility = Visibility.Visible;
// Load Static and dynamic data -- populate the different pivots
LoadStaticData();
LoadXMLFile();
// data loaded, progressbar collapsed
this.IsDataLoaded = true;
ProgressBarVisibility = Visibility.Collapsed;
}
So i included system.windows library, and used the visibility class.
Anyway, i cannot get the progressbar to disappear when the loading is done, it keeps going.
Any suggestion? where am i doing it wrong?
Thanks in advance!
Solution: loaddata is executed on the app activation, so the content is not even rendered at that moment.
Your MainViewModel must implement INotifyPropertyChanged to signal to the View that one of the properties has changed. In addition, when you change the ProgressBarVisibility property, it should fire the PropertyChanged event.
There are a number of MVVM frameworks that come with some implementation of INotifyPropertyChanged, but you could easily implement something simple yourself.
You need to report the changed made to the view:
Change
public Visibility ProgressBarVisibility { get; set; }
by
private Visibility _progressBarVisibility;
public Visibility ProgressBarVisibility
{
get { return _progressBarVisibility;}
set { _progressBarVisibility = value; RaisePropertyChanged("ProgressBarVisibility");}
}
Make sure you implement INotifyPropertyChanged or a base ViewModel that implement it (MVVMLigth : ViewModelBase).

Resources