Unable to add PopupPage to project in Visual Studio - xamarin.forms

I would like to use Rg.Plugins.Popup for Xamarin.Forms but unfortunately I cannot add PopupPage to the project. I am using VIsual Studio 2017. In AddNewItem window there is no PopupPage at all.
I tried to add ContentPage instead like this:
public partial class CustomPopupPage : ContentPage
{
public CustomPopupPage ()
{
InitializeComponent ();
}
}
but whenever i try to change type ContentPage to PopupPage I get following error: Partial declarations of 'CustomPopupPage' must not specify different base classes.
The problem is that second partial class is in auto-generated file CustomPopupPage.xaml.g.cs and I cannot modify that file because each time application is compiling it rewrites that file.
I think I am missing something obvious here because demo is working fine.

PopupPage is a subclass of ContentPage .So you have to add a new ContentPage and change the superclass both in xaml and code benind .
Firstly , install the package Rg.Plugins.Popup from nuget in share project and specific platform (iOS and Android).
The plugin requires to be initialized. To use a PopupPage inside an application, each platform application must initialize the Rg.Plugins.Popup. This initialization step varies from platform to platform and is discussed in the following sections.
iOS ->AppDelegate.cs
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Rg.Plugins.Popup.Popup.Init();
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new App ());
return base.FinishedLaunching (app, options);
}
Android->MainActivity
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
Rg.Plugins.Popup.Popup.Init(this, bundle);
Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication (new App ());
}
xaml
<?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"
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="MyProject.MyPopupPage">
<!--You can set an animation in the xaml file or in the csharp code behind-->
<pages:PopupPage.Animation>
<animations:ScaleAnimation
PositionIn="Center"
PositionOut="Center"
ScaleIn="1.2"
ScaleOut="0.8"
DurationIn="400"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
<!--You can use any elements here which are extended from Xamarin.Forms.View-->
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="20, 20, 20, 20">
<Label
Text="Test"/>
</StackLayout>
</pages:PopupPage>
in code behind
public partial class MyPopupPage : Rg.Plugins.Popup.Pages.PopupPage
{
public MyPopupPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
Update
It seems an existing issue of vs 2017 , on VS 2019 it works fine . And I will post this issue to product teams .

Related

Injecting sqlite database into MAUI ViewModels gives error that ViewModel does not define parameterless constructor

I am new to MAUI and I have a working project that uses an sqlite database to store my data for my project.
I am trying to inject my database access object into the ViewModel for one of my Content Pages.
I had it working previously by just creating ("new'ing up") my database access object and the database and project worked fine.
When I changed this so that I would inject my database access object into the ViewModel's constructor I get an error:
/Users/RemoteCommand/Projects/Notes/Views/AllNotesPage.xaml(9,9): Error: XLS0507: Type 'AllNotes' is not usable as an object element because it is not public or does not define a public parameterless constructor or a type converter. (Notes) IntelliSense
Here is my XAML file:
<?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:models="clr-namespace:Notes.Models"
x:Class="Notes.Views.AllNotesPage"
Title="AllNotesPage">
<ContentPage.BindingContext>
<models:AllNotes />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<!--<ToolbarItem Text="Add" Clicked="Add_Clicked" IconImageSource="{FontImage Glyph='+', Color=White, Size=22}"/>-->
<ToolbarItem Text="Add" Command="{Binding AddClickedCommand}" IconImageSource="{FontImage Glyph='+', Color=White, Size=22}"/>
</ContentPage.ToolbarItems>
<CollectionView x:Name="notesCollection"
ItemsSource="{Binding Notes}"
Margin="20"
SelectionMode="Single"
SelectionChanged="notesCollection_SelectionChanged">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Text}" FontSize = "22"/>
<Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
Here is my code behind:
namespace Notes.Views;
using Notes.Models;
using Notes.Views;
using Notes.Data;
using CommunityToolkit.Mvvm.Input;
public partial class AllNotesPage : ContentPage
{
public AllNotesPage()
{
InitializeComponent();
}
async void notesCollection_SelectionChanged(System.Object sender, Microsoft.Maui.Controls.SelectionChangedEventArgs e)
{
if(e.CurrentSelection.Count != 0)
{
var note = (Note)e.CurrentSelection[0];
await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.ID}");
}
}
}
Here is my ViewModel:
using System;
using System.Collections.ObjectModel;
using Notes.Data;
using Notes.Views;
using CommunityToolkit.Mvvm;
using CommunityToolkit.Mvvm.Input;
namespace Notes.Models;
public partial class AllNotes
{
NotesDatabase _notesDatabase;
public ObservableCollection<Note> Notes { get; set; } = new ObservableCollection<Note>();
public AllNotes(NotesDatabase notesDatabase)
{
_notesDatabase = notesDatabase;
LoadNotes();
}
[RelayCommand]
async void AddClicked()
{
await Shell.Current.GoToAsync(nameof(NotePage));
}
public async void LoadNotes()
{
Notes.Clear();
List<Note> notes = await _notesDatabase.GetItemsAsync();
foreach(Note note in notes)
{
Notes.Add(note);
}
}
}
and here is my MauiProgram where I define the dependency injection:
using Microsoft.Extensions.Logging;
using Notes.Views;
using Notes.Models;
using Notes.Data;
namespace Notes;
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");
});
#if DEBUG
builder.Logging.AddDebug();
#endif
builder.Services.AddSingleton<AllNotes>();
builder.Services.AddSingleton<AllNotesPage>();
builder.Services.AddSingleton<NotesDatabase>();
return builder.Build();
}
}
[Note: I have switched the AddSingleton to Add Transient for this page for these definitions to see if that would fix the problem but it did not]
I've tried a really basic dependency injection on an earlier test project where I injected my data access object into the code behind and I got the same error about missing a parameterless constructor and it turns out what I was missing was defining my data access object AND ContentPage as a Transient or Singleton in MauiProgram (Which is why for this project I added the data access object, ContentPage, and ViewModel as Singletons to the MauiProgram). Once I did that it worked, but now that I am using a ViewModel that I bind in the XAML I can't seem to get DI working for the ViewModel.
Please help this greenhorn!
Sincerely,
Doing this in XAML is an open issue: Resolve XAML BindingContext from ServiceCollection.
For now, do this via code behind's constructor, with a parameter:
public AllNotesPage(AllNotes vm)
{
InitializeComponent();
BindingContext = vm;
}
DI will inject vm, doing the needed instantiation of AllNotes and its NotesDatabase.

Set focus on Entry field in Xamarin.Forms MVVM (Prism)

I'm using Xamarin.Forms and Prism to create my mobile app.
I have a screen with 2 entries. When entering the screen, I'd like to set the focus on the first entry.
Then after the user entered data in this entry and validated it, I'd like to set the focus to the second entry.
Based on first answer:
I should do something wrong. I've created a small new Prism project to test it :
MainPage.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:testEntry"
x:Class="testEntry.Views.MainPage"
Title="{Binding Title}">
<StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Label Text="Welcome to Xamarin Forms and Prism!" />
<local:MyEntry Placeholder="" x:Name="entry1" />
<Button Text="set focus on entry1" Clicked="Button_Clicked"/>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using Xamarin.Forms;
namespace testEntry.Views
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
entry1.Focus(); //Not Working
}
private void Button_Clicked(object sender, EventArgs e)
{
entry1.Focus(); //Working
}
}
}
MyEntry.cs (in Main project)
using Xamarin.Forms;
namespace testEntry
{
public class MyEntry : Entry
{
}
}
MyEntryRenderer.cs (in Android Project)
using Android.Content;
using Android.Views;
using Android.Views.Accessibility;
using Xamarin.Forms.Platform.Android;
namespace testEntry.Droid
{
public class MyEntryRenderer : EntryRenderer
{
public MyEntryRenderer(Context context) : base(context)
{
}
public static void Focus(View view)
{
view.SendAccessibilityEvent(EventTypes.ViewFocused);
}
}
}
Unfortunately, still nofocus on my field :'(
Finally, and thanks to Saamer, I found another way of doing it by using EventAggregator.
public class FocusChanged : PubSubEvent<String> { }
Then in my view model :
IEventAggregator _ea;
public MainPageViewModel(INavigationService navigationService, IEventAggregator eventAggregator) : base(navigationService)
{
_ea = eventAggregator;
}
In the viewModel, whenever I want to set the focus to a field, I'm sending an event :
_ea.GetEvent<FocusChanged>().Publish("Source");
And in my view's code behind, I handle this event:
IEventAggregator _ea;
public MainPage(IEventAggregator eventAggregator)
{
InitializeComponent();
_ea = eventAggregator;
_ea.GetEvent<FocusChanged>().Subscribe(SetFocusOnControl); //Name of method which will handle this event
}
/// set the focus on entry based on parameter
/// each event value will set focus on a specific entry (example: source is linked to entry txtScanSrc)
private async void SetFocusOnControl(String fieldName)
{
Entry l_view;
switch(fieldName)
{
case "source": l_view = this.FindByName<Entry>("txtScanSrc"); break;
case "quantity": l_view = this.FindByName<Entry>("txtQty"); break;
case "tote": l_view = this.FindByName<Entry>("txtScanTote"); break;
case "pallet": l_view = this.FindByName<Entry>("txtScanPalout"); break;
case "destination": l_view = this.FindByName<Entry>("txtScanDest"); break;
default: l_view = this.FindByName<Entry>("txtScanSrc"); break;
}
await WaitAndExecute(500, () => { l_view.Focus(); });
}
There's a way of doing this using the Accessibility APIs of each of the platforms. Xamarin forms doesn't have all the platform features of accessibility yet so you d have to create a custom renderer and then call the focus method in a life cycle event of the page.
So calling this Focus function would cause the app to focus on that element. You generally don't want to do it because the app purposefully focuses on what it does so accessible users have a consistent experience. But if you really want to override the default behavior, in Android it's something like this
public static void Focus(View view)
{
view.SendAccessibilityEvent(EventTypes.ViewFocused);
}
And in iOS, you have to use the PostNotification apis which will be some variation of this
UIAccessibility.PostNotification(UIAccessibilityPostNotification.ScreenChanged, entry element)
You can look more into Accessibility Focus to get the exact answer

Content Page in Xamarin Forms Prism Module is Initializing Three times

I am using Prism for Xamarin Forms and Implemented Modularity in the applications. I have .Droid Project, Default PCL Project which has App.xaml file and other content pages. And I have 4 PCL projects which are created as Modules by implementing IModule. Modules are configured in the App.xaml.cs.
My problem is when app initialized , the login page, home page which are in default PCL project loaded properly. But any content page in Modules are Initializing exactly three times every time I navigated to it using _navigationService.NavigateAsync("") from another page either from the same module or the other module. Content page constructor was called three times along with the corresponding ViewModel constructor, but the OnNavigationCompleted in VM is running only once.
May be because of this issue or some other reason, my Content page in the module is taking few more fractions to load fully. This can be easily noticed while loading. Overall Navigation of Content Page in the module is not so smooth.
Is there any solution for this? I have already tried keeping an empty content
in the module page without any controls and the result is same.
Below are the code snippets of my project.
Configure Module in app.xaml.cs
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
Type module1 = typeof(QNPL.Mobile.Module1);
Type module2 = typeof(QNPL.Mobile.Module2);
Type module3 = typeof(QNPL.Mobile.Module3);
Type module4 = typeof(QNPL.Mobile.Module4);
ModuleCatalog.AddModule(
new ModuleInfo()
{
ModuleName = module1.Name,
ModuleType = module1,
InitializationMode = InitializationMode.OnDemand
});
ModuleCatalog.AddModule(
new ModuleInfo()
{
ModuleName = module2.Name,
ModuleType = module2,
InitializationMode = InitializationMode.OnDemand
});
ModuleCatalog.AddModule(
new ModuleInfo()
{
ModuleName = module3.Name,
ModuleType = module3,
InitializationMode = InitializationMode.OnDemand
});
ModuleCatalog.AddModule(
new ModuleInfo()
{
ModuleName = module4.Name,
ModuleType = module4,
InitializationMode = InitializationMode.OnDemand
});
}
private async void MenuItemClick(object value)
{
if (value != null)
{
MenuDetail menuItem = (MenuDetail)value;
SelectedMenu = menuItem;
if (!string.IsNullOrEmpty(SelectedMenu.ModuleName))
{
_moduleManager.LoadModule(SelectedMenu.ModuleName);
}
await _navigationService.NavigateAsync(SelectedMenu.URL, navParams);
}
}
Module Page:
using Microsoft.Practices.Unity;
using Prism.Modularity;
using Prism.Unity;
using QNPL.Mobile.Module1.API;
using QNPL.Mobile.Module1.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace QNPL.Mobile.Module1
{
public class Module1 : IModule
{
private readonly IUnityContainer _unityContainer;
public Module1(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
}
public void Initialize()
{
_unityContainer.RegisterTypeForNavigation<Module1HomePage>();
_unityContainer.RegisterPopupNavigationService();
_unityContainer.RegisterType<IApiInterface, ApiInterface>();
}
}
}
Content Page in Module1:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="QNPL.Mobile.Module1.Views.Module1HomePage"
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:prismBehaviors="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
x:Name="Module1HomePage"
Title="Home Page"
prism:ViewModelLocator.AutowireViewModel="True">
<ContentPage.Content>
</ContentPage.Content>
</ContentPage>
ContentPage .cs file:
using Xamarin.Forms;
namespace QNPL.Mobile.Module1.Views
{
public partial class Module1HomePage : ContentPage
{
public Module1HomePage()
{
InitializeComponent();
}
}
}

iOS 11 large titles in Xamarin.Forms

How do I enable iOS 11 prefersLargeTitles throughout my Xamarin.Forms app?
I tried creating a custom renderer derived from PageRenderer for NavigationPage, setting:
ViewController.NavigationController.NavigationBar.PrefersLargeTitles = true;
This didn't have any effect, however.
Voila
[assembly: ExportRenderer(typeof(NavigationPage), typeof(NavBarRenderer))]
namespace LargeTitleSample.iOS
{
public class NavBarRenderer : NavigationRenderer
{
protected override void OnElementChanged(Xamarin.Forms.Platform.iOS.VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
NavigationBar.PrefersLargeTitles = true;
}
}
}
You have to create a custom renderer for the NavigationPage inheriting the NavigationRenderer. Then set the PrefersLargeTitles property on the NavigationBar to true.
It seems that when you add some scrollable control to the page, it will automatically have to 'big to small' effect when scrolling up, at least for a ListView.
Working example repo is here: https://github.com/jfversluis/LargeTitleSample
For XAML:
<NavigationPage Title="..." xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:NavigationPage.PrefersLargeTitles="true">

How to bind to a WPF dependency property to window?

i create a dependency property to close a view from view model,
dependencyProperty:
public static class WindowBehaviors
{
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.RegisterAttached("IsOpen"
, typeof(bool),
typeof(WindowBehaviors),
new UIPropertyMetadata(false, IsOpenChanged));
private static void IsOpenChanged(DependencyObject obj,DependencyPropertyChangedEventArgs args)
{
Window window = Window.GetWindow(obj);
if (window != null && ((bool)args.NewValue))
window.Close();
}
public static bool GetIsOpen(Window target)
{
return (bool)target.GetValue(IsOpenProperty);
}
public static void SetIsOpen(Window target, bool value)
{
target.SetValue(IsOpenProperty, value);
}
}
and use it in my xaml like this:
<window
...
Command:WindowBehaviors.IsOpen="True">
it work's fine,but when i want to bind it to a property in viewModel,it dosen't work,and i guess,it dosen't work because i define the resource later in xaml.
in xaml:
<Window.Resources>
<VVM:myVieModel x:Key="myVieModel"/>
</Window.Resources>
and i don't know what should i do,where should i put this:
Command:WindowBehaviors.IsOpen="{binding Isopen}"
public MainWindow()
{
InitializeComponent();
// DO THIS
this.DataContext = Resources["myVieModel"];
}
You need to bind the data context for the scope where your binding is in. Usually this is fairly high up in your XAML, usually the first element in your form or control.
In your case, the data context beeing a static resource the folllowing should work:
<grid DataContext="{StaticResource myVieModel}">
<!-- the code with the binding goß into here -->
</grid>
Actually this is the same as ebattulga suggests, just the XAML way (no code behind).
Thanks for your helps,i fixed it and here is my solution,
i used to use MVVMToolkit but now i'm useing MVVMlight and as you know in MVVMLight,we just define Application Resources Once in App.xaml.so we can bind all the window's properties simply,hope this can help some people who has the same problem!!
app.xaml
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
and in the window(view)
DataContext="{Binding DefaultSpecItemVM, Source={StaticResource Locator}}"
and it works perfect.:D

Resources