I have an already build Xamarin App but now I want to add the Sign Up / Login Page at the Start. When I open the App, first It open a Sign up Page.
I add a Blank Page with Title "Welcome to App" and Add a Button, but the Button doesn't work and only It works when it uses the Navigation of Other Pages, why? and how to add a sign up form on blank page?
IMAGE: It now looks like this.
I want to remove these Credentials, Accounts e.t.c from the top and just have an blank page of Sign Up.
Here is the Code, I hope it helps you to understand it better. If you need anything else, kindly ask for it.
Thanks :)
UPDATE: #Jason Comment : I removed Login Code from MainPage.xml, now the Navigation Tabs are no longer there but now the Button isn't working (when I click on it, nothing happens).
LoginPage.xml
<?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:converters="clr-namespace:Osma.Mobile.App.Converters;assembly=Osma.Mobile.App"
xmlns:behaviours="clr-namespace:Osma.Mobile.App.Behaviors;assembly=Osma.Mobile.App"
xmlns:components="clr-namespace:Osma.Mobile.App.Views.Components;assembly=Osma.Mobile.App"
x:Class="Osma.Mobile.App.Views.Login.LoginPage"
Title="Login"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
ios:Page.LargeTitleDisplay="Always"
>
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to App"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Button Text="Enter"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
Command="{Binding ButtonClickedCommand}"
/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
LoginPage.xml.cs
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Osma.Mobile.App.Views.Login
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
}
}
}
LoginViewMode.cs
using System;
using System.Reactive.Linq;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using System.Threading.Tasks;
using Acr.UserDialogs;
using Hyperledger.Aries.Contracts;
using Hyperledger.Aries.Features.DidExchange;
using Hyperledger.Aries.Utils;
using Osma.Mobile.App.Events;
using Osma.Mobile.App.Extensions;
using Osma.Mobile.App.Services;
using Osma.Mobile.App.Services.Interfaces;
using ReactiveUI;
using Xamarin.Forms;
using Xamarin.Essentials;
using Osma.Mobile.App.ViewModels.Credentials;
namespace Osma.Mobile.App.ViewModels.Login
{
public class LoginViewModel : ABaseViewModel
{
private readonly ICustomAgentContextProvider _agentContextProvider;
private readonly IConnectionService _connectionService;
public LoginViewModel(
IUserDialogs userDialogs,
INavigationService navigationService,
ICustomAgentContextProvider agentContextProvider,
IConnectionService defaultConnectionService) :
base("Login", userDialogs, navigationService)
{
_agentContextProvider = agentContextProvider;
_connectionService = defaultConnectionService;
}
public ICommand ButtonClickedCommand => new Command(async () => await NavigationService.NavigateToAsync<CredentialsViewModel>());
}
}
App.xaml.css
using System.Threading.Tasks;
using Autofac;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using Microsoft.AppCenter.Crashes;
using Osma.Mobile.App.Services.Interfaces;
using Osma.Mobile.App.Utilities;
using Osma.Mobile.App.ViewModels;
using Osma.Mobile.App.ViewModels.Login;
using Osma.Mobile.App.ViewModels.Account;
using Osma.Mobile.App.ViewModels.Connections;
using Osma.Mobile.App.ViewModels.CreateInvitation;
using Osma.Mobile.App.ViewModels.Credentials;
using Osma.Mobile.App.Views;
using Osma.Mobile.App.Views.Login;
using Osma.Mobile.App.Views.Account;
using Osma.Mobile.App.Views.Connections;
using Osma.Mobile.App.Views.CreateInvitation;
using Osma.Mobile.App.Views.Credentials;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;
using LoginPage = Osma.Mobile.App.Views.Login.LoginPage;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Osma.Mobile.App
{
public partial class App : Application
{
public new static App Current => Application.Current as App;
public Palette Colors;
private readonly INavigationService _navigationService;
private readonly ICustomAgentContextProvider _contextProvider;
public App(IContainer container)
{
InitializeComponent();
MainPage = new LoginPage();
Colors.Init();
_navigationService = container.Resolve<INavigationService>();
_contextProvider = container.Resolve<ICustomAgentContextProvider>();
InitializeTask = Initialize();
}
Task InitializeTask;
private async Task Initialize()
{
//Pages
_navigationService.AddPageViewModelBinding<MainViewModel, MainPage>();
_navigationService.AddPageViewModelBinding<LoginViewModel, LoginPage>();
_navigationService.AddPageViewModelBinding<ConnectionsViewModel, ConnectionsPage>();
_navigationService.AddPageViewModelBinding<ConnectionViewModel, ConnectionPage>();
_navigationService.AddPageViewModelBinding<RegisterViewModel, RegisterPage>();
_navigationService.AddPageViewModelBinding<AcceptInviteViewModel, AcceptInvitePage>();
_navigationService.AddPageViewModelBinding<CredentialsViewModel, CredentialsPage>();
_navigationService.AddPageViewModelBinding<CredentialViewModel, CredentialPage>();
_navigationService.AddPageViewModelBinding<AccountViewModel, AccountPage>();
_navigationService.AddPageViewModelBinding<CreateInvitationViewModel, CreateInvitationPage>();
if (_contextProvider.AgentExists())
{
await _navigationService.NavigateToAsync<LoginViewModel>();
}
else
{
await _navigationService.NavigateToAsync<LoginViewModel>();
}
}
protected override void OnStart()
{
#if !DEBUG
AppCenter.Start("ios=" + AppConstant.IosAnalyticsKey + ";" +
"android=" + AppConstant.AndroidAnalyticsKey + ";",
typeof(Analytics), typeof(Crashes));
#endif
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
MainPage.xml
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:connections="clr-namespace:Osma.Mobile.App.Views.Connections;assembly=Osma.Mobile.App"
xmlns:credentials="clr-namespace:Osma.Mobile.App.Views.Credentials;assembly=Osma.Mobile.App"
xmlns:account="clr-namespace:Osma.Mobile.App.Views.Account;assembly=Osma.Mobile.App"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
x:Class="Osma.Mobile.App.Views.MainPage"
CurrentPageChanged="CurrentPageChanged"
Appearing="Appearing"
>
<connections:ConnectionsPage
Icon="connection_icon.png"
ios:NavigationPage.PrefersLargeTitles="true"
BindingContext="{Binding Connections}">
</connections:ConnectionsPage>
<credentials:CredentialsPage
Icon="credentials_icon.png"
ios:NavigationPage.PrefersLargeTitles="true"
BindingContext="{Binding Credentials}">
</credentials:CredentialsPage>
<account:AccountPage
Icon="account_icon.png"
ios:NavigationPage.PrefersLargeTitles="true"
BindingContext="{Binding Account}">
</account:AccountPage>
</TabbedPage>
MainViewModel.cs
using System.Threading.Tasks;
using Acr.UserDialogs;
using Osma.Mobile.App.Services.Interfaces;
using Osma.Mobile.App.ViewModels.Login;
using Osma.Mobile.App.ViewModels.Account;
using Osma.Mobile.App.ViewModels.Connections;
using Osma.Mobile.App.ViewModels.CreateInvitation;
using Osma.Mobile.App.ViewModels.Credentials;
using ReactiveUI;
namespace Osma.Mobile.App.ViewModels
{
public class MainViewModel : ABaseViewModel
{
public MainViewModel(
IUserDialogs userDialogs,
INavigationService navigationService,
LoginViewModel loginViewModel,
ConnectionsViewModel connectionsViewModel,
CredentialsViewModel credentialsViewModel,
AccountViewModel accountViewModel,
CreateInvitationViewModel createInvitationViewModel)
: base(
nameof(MainViewModel),
userDialogs,
navigationService
)
{
Login = loginViewModel;
Connections = connectionsViewModel;
Credentials = credentialsViewModel;
Account = accountViewModel;
CreateInvitation = createInvitationViewModel;
}
public override async Task InitializeAsync(object navigationData)
{
await Login.InitializeAsync(null);
await Connections.InitializeAsync(null);
await Credentials.InitializeAsync(null);
await Account.InitializeAsync(null);
await CreateInvitation.InitializeAsync(null);
await base.InitializeAsync(navigationData);
}
#region Bindable Properties
private LoginViewModel _login;
public LoginViewModel Login
{
get => _login;
set => this.RaiseAndSetIfChanged(ref _login, value);
}
private ConnectionsViewModel _connections;
public ConnectionsViewModel Connections
{
get => _connections;
set => this.RaiseAndSetIfChanged(ref _connections, value);
}
private CredentialsViewModel _credentials;
public CredentialsViewModel Credentials
{
get => _credentials;
set => this.RaiseAndSetIfChanged(ref _credentials, value);
}
private AccountViewModel _account;
public AccountViewModel Account
{
get => _account;
set => this.RaiseAndSetIfChanged(ref _account, value);
}
private CreateInvitationViewModel _createInvitation;
public CreateInvitationViewModel CreateInvitation
{
get => _createInvitation;
set => this.RaiseAndSetIfChanged(ref _createInvitation, value);
}
#endregion
}
}
I do not see that you have a NavigationPage instance anywhere, so how does your NavigationService works?
NavigationService usually inserts a page to a stack of pages on a NaviagtionPage instance.
I see in App.xaml.cs you make the LoginPage your main page, but where do you switch it to a NavigationPage, or to your TabbedPage?
You need to check what your MainPage should really be.
Related
in my application I'm opening a Window for an Input form. In my App.xaml I have defined the following:
<DataTemplate DataType="{x:Type ViewModels:EditTicketViewModel}">
<Frame>
<Frame.Content>
<Views:EditTicketView></Views:EditTicketView>
</Frame.Content>
</Frame>
</DataTemplate>
My application also has a Window service for opening windows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DevPortal.Interfaces
{
public interface IWindowService
{
public void ShowWindow(object viewModel, bool showDialog);
}
}
the implementation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using DevPortal.Interfaces;
using Syncfusion.Windows.Shared;
using Syncfusion.SfSkinManager;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace DevPortal.Services
{
public class WindowService : IWindowService
{
public void ShowWindow(object viewModel, bool showDialog)
{
var window = new ChromelessWindow();
window.ResizeMode = ResizeMode.NoResize;
SfSkinManager.SetTheme(window, new Theme("FluentDark"));
window.Content = viewModel;
window.SizeToContent = SizeToContent.WidthAndHeight;
window.Title = viewModel.GetType().GetProperty("Title").GetValue(viewModel).ToString();
window.ShowIcon = false;
if (showDialog)
{
window.ShowDialog();
} else
{
window.Show();
}
}
}
}
How I open the window (from a viewmodel in the MainView)
[RelayCommand]
private void CreateTicket()
{
App.Current.ServiceProvider.GetService<IWindowService>().ShowWindow(new EditTicketViewModel(), true);
}
What would be the best way to close this window from the ViewModel? Previously i was used to directly create the view, and in the constructor of the view i would subscribe to a close-event in the viewmodel, but that's not really the MVVM-way I guess. Do I need to implement some kind of service? Thanks!
EDIT: I forgott to mention that the View is a page. So i Am creating a window with the viewmodel as content, and the datatemplate of the viewmodel is a Frame containing the page.
You could for example return an IWindow from your window service:
public class WindowService : IWindowService
{
public IWindow ShowWindow(object viewModel, bool showDialog)
{
var window = new ChromelessWindow();
...
return window;
}
}
...and then simply call Close() on this one in the view model.
The interface would be as simple as this:
public interface IWindow
{
void Close();
}
Your ChromelessWindow implements the interface:
public partial class ChromelessWindow : Window, IWindow { ... }
...and the view model only has a dependency on an interface. It still doesn't know anything about a view or actual window. IWindow is just a name. I can be called anything.
The cleanest way of closing a Window from it's ViewModel is using an attached property.
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
{
return;
}
if (view.IsModal())
{
view.DialogResult = args.NewValue as bool?;
}
else
{
view.Close();
}
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
In the ViewModel create a ViewClosed property
private bool? _viewClosed;
public bool? ViewClosed
{
get => _viewClosed;
set => Set(nameof(ViewClosed), ref _viewClosed, value);
}
then in the View, bind to it using the attached property
<Window
...
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}" >
Mode details on my take on MVVM navigation on my blog post.
I'm trying to open pdf with a webview but some pdf says "No Preview Avaliable", or you have to give it many clicks to open it or directly some crash the app.
Im using the web https://www.pdfpdf.com/samples.html and the first 5 pdfs work fine although sometimes I have to click them 5 times to open them. With those of the last sections, the app crashes when I click on one of them.
The following code can work if I open the pdf in WebView directly .
This is my code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="webviewproblema.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<StackLayout>
<WebView
x:Name="Browser"
HeightRequest="1000"
Navigating="Browser_Navigating"
Source="{Binding HTMLContent}"
WidthRequest="1000" />
</StackLayout>
</ContentPage>
public partial class MainPage : ContentPage
{
public string HTMLContent { get; set; }
public MainPage()
{
InitializeComponent();
Browser.Source = "https://www.pdfpdf.com/samples.html";
}
private void Browser_Navigating(object sender, WebNavigatingEventArgs e)
{
// With this,the app crash
//string url = "https://docs.google.com/gview?embedded=true&url=" + System.Net.WebUtility.UrlEncode(e.Url);
Browser.IsVisible = true;
string url = e.Url;
if ( e.Url.EndsWith(".pdf") || e.Url.EndsWith(".PDF") && !e.Url.Contains("drive.google.com"))
{
Browser.Source = "https://drive.google.com/viewerng/viewer?embedded=true&url=" + url;
}
}
}
You could use custom renderer to open external link in a website .
In forms
Create a custom webview
public class MyWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(MyWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
in Android project
using Android.Content;
using Android.Net.Http;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Webkit;
using Android.Widget;
using App32;
using App32.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(MyWebView), typeof(CustomWebViewRenderer))]
namespace App32.Droid
{
public class CustomWebViewRenderer : WebViewRenderer
{
Context _context;
public CustomWebViewRenderer(Context context) : base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Android.Webkit.WebView web_view = new Android.Webkit.WebView(_context);
web_view.LoadUrl(((MyWebView)Element).Uri);
web_view.SetWebViewClient(new MyWebViewClient());
SetNativeControl(web_view);
Control.Settings.JavaScriptEnabled = true;
}
}
}
public class MyWebViewClient : WebViewClient
{
public override void OnReceivedSslError(Android.Webkit.WebView view, SslErrorHandler handler, SslError error)
{
handler.Proceed();
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
var url = request.Url.ToString();
if (request.Url.ToString().EndsWith(".pdf") || request.Url.ToString().EndsWith(".PDF") && !request.Url.ToString().Contains("drive.google.com"))
{
view.LoadUrl("https://drive.google.com/viewerng/viewer?embedded=true&url=" + request.Url.ToString());
view.SetWebViewClient(new MyWebViewClient());
}
return true;
}
}
}
in iOS project
using System;
using App32;
using App32.iOS;
using Foundation;
using ObjCRuntime;
using UIKit;
using WebKit;
using Xamarin.Forms.Platform.iOS;
[assembly: Xamarin.Forms.ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))]
namespace App32.iOS
{
public class MyWebViewRenderer : ViewRenderer<MyWebView, WKWebView>
{
WKWebView _wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<MyWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
_wkWebView.NavigationDelegate = new MyDelegete();
SetNativeControl(_wkWebView);
}
if(e.NewElement!=null)
{
var webview = Element as MyWebView;
var url = webview.Uri;
if (url.EndsWith(".pdf") || url.EndsWith(".PDF") && !url.Contains("drive.google.com"))
{
Control.LoadRequest(new NSUrlRequest(new NSUrl("https://drive.google.com/viewerng/viewer?embedded=true&url=" + webview.Uri)));
}
else
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(webview.Uri)));
}
}
}
}
public class MyDelegete :WKNavigationDelegate
{
public override void DecidePolicy(WKWebView webView, WKNavigationAction navigationAction, WKWebpagePreferences preferences, Action<WKNavigationActionPolicy, WKWebpagePreferences> decisionHandler)
{
// base.DecidePolicy(webView, navigationAction, preferences, decisionHandler);
var url = navigationAction.Request.Url.ToString();
if (url.EndsWith(".pdf") || url.EndsWith(".PDF") && !url.Contains("drive.google.com"))
{
webView.LoadRequest(new NSUrlRequest(new NSUrl("https://drive.google.com/viewerng/viewer?embedded=true&url=" + url)));
}
decisionHandler.Invoke(WKNavigationActionPolicy.Allow, preferences);
}
}
}
in xaml
Now you can use it in xaml like
<StackLayout>
<local:MyWebView x:Name="Browser" Uri="https://xxx" HeightRequest="1000" WidthRequest="1000" />
</StackLayout>
I want to execute a method on TextChange event and for specific text, I want to do something and close the window using MVVM
for example on this part of my code I want to close my window:
if (text.Equals("12345"))
{
//Exit from window
}
I know how can I do it from a button using a command, As I have in the code of the next example.
But how can I pass the window to a running property that binds to a text of the textbox?
or there is another way to close form?
I want to do it on the ViewModel and not the code behind to write my code as MVVM pattern close that I can
my XAML code :
<Window x:Class="PulserTesterMultipleHeads.UserControls.TestWindows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PulserTesterMultipleHeads.UserControls"
mc:Ignorable="d"
Name="MainTestWindow"
Title="TestWindows" Height="450" Width="800">
<Grid>
<StackPanel>
<TextBox Text="{Binding Text,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button Command="{Binding EndTestExit}"
CommandParameter="{Binding ElementName=MainTestWindow}">Exit</Button>
</StackPanel>
</Grid>
</Window>
the code behind XAML :
using PulserTesterMultipleHeads.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace PulserTesterMultipleHeads.UserControls
{
/// <summary>
/// Interaction logic for TestWindows.xaml
/// </summary>
public partial class TestWindows : Window
{
public TestWindows()
{
InitializeComponent();
DataContext = new TestWindowsMV();
}
}
}
View Model code :
using PulserTester.ViewModel.Base;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace PulserTesterMultipleHeads.Classes
{
public class TestWindowsMV : INotifyPropertyChanged
{
private string text;
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
//Exit from window
}
}
}
/// <summary>
/// for the example
/// </summary>
private ICommand _EndTestExit;
public ICommand EndTestExit
{
get
{
if (_EndTestExit == null)
{
_EndTestExit = new GenericRelayCommand<Window>((window) => EndTestExitAction(window));
}
return _EndTestExit;
}
}
private void EndTestExitAction(Window window)
{
window.Close();
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
************************* Edit as the answer ****************************
The change in ModelView :
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
CloseAction();
}
}
}
The change in code behind:
public TestWindows()
{
InitializeComponent();
DataContext = new TestWindowsMV();
if (((TestWindowsMV)DataContext).CloseAction == null)
((TestWindowsMV)DataContext).CloseAction = new Action(this.Close);
}
It is kind of hard with MVVM but what you can do is create an Event inside of your ViewModel and attach a function that will close your window in code behind of your View. Then you will be able to call your event inside of ViewModel whenever you need to close the window (or do something else that is more convenient to do in View rather than in ViewModel)
The cleanest way to close a View from the ViewModel is to use an attached property
public static class perWindowHelper
{
public static readonly DependencyProperty CloseWindowProperty = DependencyProperty.RegisterAttached(
"CloseWindow",
typeof(bool?),
typeof(perWindowHelper),
new PropertyMetadata(null, OnCloseWindowChanged));
private static void OnCloseWindowChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
if (!(target is Window view))
return;
if (view.IsModal())
view.DialogResult = args.NewValue as bool?;
else
view.Close();
}
public static void SetCloseWindow(Window target, bool? value)
{
target.SetValue(CloseWindowProperty, value);
}
public static bool IsModal(this Window window)
{
var fieldInfo = typeof(Window).GetField("_showingAsDialog", BindingFlags.Instance | BindingFlags.NonPublic);
return fieldInfo != null && (bool)fieldInfo.GetValue(window);
}
}
which you can then bind to an appropriate property in your ViewModel
<Window
x:Class="...
vhelp:perWindowHelper.CloseWindow="{Binding ViewClosed}">
private bool? _viewClosed;
public bool? ViewClosed
{
get { return _viewClosed; }
set { Set(nameof(ViewClosed), ref _viewClosed, value); }
}
More details on my recent blog post.
You have almost everything you need already implemented. All you really need to do is call private void EndTestExitAction(Window window) from your setter and supply window its value during construction:
public string Text {
get {
return text;
} set {
text = value;
if (text.Equals("12345"))
{
EndTestExitAction(window)
}
}
}
where window is a property of your View Model.
I'm really trying to work my way through a Xamarian.Form app an I'm finding it difficult because the template out of the box doesn't seem to work.
Basically on the menu I'm trying to nav to the Browse or About page.
however MainPage RootPage { get => Application.Current.MainPage as MainPage; } is returning null.
I've made no changes to this template but I do understand C# so I roughly understand the issue but I'm not aware as to why this is returning null, certainly from the starting template.
MenuPage.xaml.xs
using ProspectGator.Models;
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ProspectGator.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MenuPage : ContentPage
{
MainPage RootPage { get => Application.Current.MainPage as MainPage; }
List<HomeMenuItem> menuItems;
public MenuPage()
{
InitializeComponent();
menuItems = new List<HomeMenuItem>
{
new HomeMenuItem {Id = MenuItemType.Browse, Title="Browse" },
new HomeMenuItem {Id = MenuItemType.About, Title="About" }
};
ListViewMenu.ItemsSource = menuItems;
ListViewMenu.SelectedItem = menuItems[0];
ListViewMenu.ItemSelected += async (sender, e) =>
{
if (e.SelectedItem == null)
return;
var id = (int)((HomeMenuItem)e.SelectedItem).Id;
await RootPage.NavigateFromMenu(id);
};
}
}
}
App.xaml.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using ProspectGator.Views;
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace ProspectGator
{
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage( new MainPage());
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Typically I would expect this property to be set or already set but I'm not sure. I do have a MainPage.xaml.
I have a form in Xamarin with two <Entry> (entry controls) one of them, when doing Tap it opens a modal window that contains a list and the other has a normal operation, everything works well until the user opens the normal Entry keyboard first and then call the modal, when this happens the keyboard is up and the list is shown blocking the view of the user as follows...
I would like to change this behavior, so I attach the XAML code of the view
MyView.XAML:
<Entry
Placeholder="Nombre Sustancia Química"
Margin="15,5,15,5"
HorizontalOptions="FillAndExpand"
Text="{Binding NombreSustancia, Mode=TwoWay}"
IsEnabled="{Binding EntryEnabled}">
</Entry>
<Entry
x:Name="Make"
Placeholder="Seleccione Fabricante"
Margin="15,5,15,5"
Focused="Entry_Focused"
HorizontalOptions="FillAndExpand"
Text="{Binding NombreFabricante, Mode=TwoWay}"
IsEnabled="{Binding EntryEnabled}">
</Entry>
MyView.XAML.CS:
public partial class FiltrosSisquimView : ContentPage
{
public ObservableCollection<Fabricante> Fabricantes { get; set; }
public FiltrosSisquimView ()
{
InitializeComponent();
}
private async void Entry_Focused(object sender, FocusEventArgs e)
{
//prevents the keyboard from opening when calling the modal
Make.Unfocus();
var mainViewModel = MainViewModel.GetInstance();
Fabricantes = mainViewModel.Filtros.Fabricantes;
mainViewModel.FabricantesModal = new FabricantesModalViewModel(Fabricantes);
await Application.Current.MainPage.Navigation.PushModalAsync(new FabricantesModalView());
}
}
I must say, that this problem only happens in iOS, and that Android has the expected behavior (when opening the modal the keyboard is automatically lowered), how could the keyboard go down when the user has already opened the list? Where should I control this event? on the modal page? in the codebehind? I am occupying MVVM as architectural pattern
any help for me?
It seems a native iOS design issue.I use the similar code on Xcode and get the same issue.So you can use the custom renderer and to solve it.
in Forms
using System;
using Xamarin.Forms;
namespace app1
{
public class MyEntry:Entry
{
public MyEntry()
{
}
}
}
in xaml
<Entry
Placeholder="Nombre Sustancia Química"
Margin="15,5,15,5"
HorizontalOptions="FillAndExpand"
>
</Entry>
<local:MyEntry
x:Name="Make"
Placeholder="Seleccione Fabricante"
Margin="15,5,15,5"
Focused="Entry_Focused"
HorizontalOptions="FillAndExpand"
>
</local:MyEntry>
in iOS project
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using app1;
using app1.iOS;
using UIKit;
using Foundation;
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace app1.iOS
{
public class MyEntryRenderer:EntryRenderer,IUITextFieldDelegate
{
public MyEntryRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Control.WeakDelegate = this;
}
}
[Export("textFieldShouldBeginEditing:")]
public bool ShouldBeginEditing(UITextField textField)
{
MessagingCenter.Send<Object>(this, "finish");
return false;
}
}
}
Now ,subscribe the message in your contentPage.
public partial class FiltrosSisquimView : ContentPage
{
public ObservableCollection<Fabricante> Fabricantes { get; set; }
public FiltrosSisquimView ()
{
InitializeComponent();
MessagingCenter.Subscribe<Object>(this, "finish", (obj) => {
var mainViewModel = MainViewModel.GetInstance();
Fabricantes = mainViewModel.Filtros.Fabricantes;
mainViewModel.FabricantesModal = new FabricantesModalViewModel(Fabricantes);
await Application.Current.MainPage.Navigation.PushModalAsync(new FabricantesModalView());
});
}
private async void Entry_Focused(object sender, FocusEventArgs e)
{
//prevents the keyboard from opening when calling the modal
Make.Unfocus();
var mainViewModel = MainViewModel.GetInstance();
Fabricantes = mainViewModel.Filtros.Fabricantes;
mainViewModel.FabricantesModal = new FabricantesModalViewModel(Fabricantes);
await Application.Current.MainPage.Navigation.PushModalAsync(new FabricantesModalView());
}
}