How to disable the Connect button after the button clicked? - viewmodel

My OutPut is this Here,
I'm using the 'connect' button in MainActivity and 'disconnect' button in MainFragment and create the Function for connect and disconnect in MainViewModel.
Whenever I click the 'connect button', it should moves to fragment using databinding to open the Fragment page.(i.e., MainActivity.kt--->MainViewmodel.kt--->MainFragment.kt).
Note that: If I click 'connect' button should be is invisible in MainFragment, once return to MainActivity, then it should in visible. Not-need to declare the "android:visibility="#{viewModel.connectButtonVisible ? View.VISIBLE : View.GONE}" " in .xml files because it getting error in dataBindingImplemention. I want this visibility in Koltin files as code
Whenever I click the 'Disconnect button', it should get back to MainActivity using databinding.
(i.e.,MainFragment.kt --->MainViewmodel.kt--->MainActivity.kt)
4.Don't use the navigation_graph.xml because databinding is not sufficient for navigation only viewbinding is ok. But I'm using DataBinding only.
I give my sample code.
MainViewModel.kt
class MainViewModel : ViewModel() {
val connectionState = MutableLiveData<Boolean>()
fun connect(){
connectionState.value = true
}
fun disconnect(){
connectionState.value = false
}
}
Activity_Main.xml
<!-- layout for MainActivity -->
<layout>
<data>
<variable
name="viewModel"
type="com.example.myapp.MainViewModel" />
</data>
<LinearLayout>
<!-- other layout elements -->
<Button
android:id="#+id/connect_button"
android:onClick="#{() -> viewModel.connectionState}"
android:text="Connect" />
<!-- other layout elements -->
</LinearLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
binding.viewModel = viewModel
viewModel.connectionState.observe(this, Observer { isConnected ->
if (isConnected) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, MainFragment())
.addToBackStack(null)
.commit()
}
})
}
}
Fragment_main.xml
<!-- layout for MainFragment -->
<layout>
<data>
<variable
name="viewModel"
type="com.example.myapp.MainViewModel" />
</data>
<LinearLayout>
<!-- other layout elements -->
<Button
android:id="#+id/disconnect_button"
android:onClick="#{() -> viewModel.connectionState}"
android:text="Disconnect" />
<!-- other layout elements -->
</LinearLayout>
</layout>
MainFragment.kt
class MainFragment : Fragment() {
private lateinit var binding: FragmentMainBinding
private lateinit var viewModel: MainViewModel
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_main, container, false)
viewModel = ViewModelProvider(activity!!).get(MainViewModel::class.java)
binding.viewModel = viewModel
viewModel.connectionState.observe(viewLifecycleOwner, Observer { isConnected ->
if (!isConnected) {
activity!!.supportFragmentManager.popBackStack()
}
})
return binding.root
}
}
My problem is Once the Click the Connect Button it should be disabled and it show the Fragment_main.xml. Again When I Click the Disconnect Button is return back to Activity_main.xml, in Connect will be visible.
Note that: Without using navagation_graph.xml and android:visibility="#{viewModel.connectButtonVisible ? View.VISIBLE : View.GONE}".

Related

Pass data child->parent only when parent presses button

I have a UWP app using MVVM Toolkit. I have a parent ViewModel and a child UserControl.
Everyone says I should pass data from child to parent using Observer pattern. Which is good. MVVM Toolkit provides some classes and methods Send/Receive.
Question
How do I pass data from child user control to parent? The child has 10 text boxes and the parent needs that data after clicking a button 'Create File'. I cannot use Observer Send method after each key press in a text box.
MainPage.xaml
<Grid>...
<uc:MetadataUserControl>
</uc:MetadataUserControl>
<Button Content="Create final"></Button>
</Grid>
MainPageViewModel.cs
private MainPageModel _mainPageModel = null; // Business layer model
public MainPageModel MainPageModel
{
get { return _mainPageModel; }
set { SetProperty(ref _mainPageModel, value); }
}
void Receive() { .. } // Receive data from user controls
MainPageModel.cs
public MetadataModel {get; set;}
MetadataViewModel.cs
private MetadataModel _metadataModel = null; // Business layer model
public MetadataModel MetadataModel
{
get { return _metadataModel; }
set { SetProperty(ref _metadataModel, value); }
}
void Send() {} // Notify parents subscribed to this user control
MetadataUserControl.xaml
<Grid>
<!-- First textbox -->
<TextBlock Text="First textbox"></TextBlock>
<TextBox Text="{x:Bind Path=ViewModel.MetadataModel.FirstText}"></TextBox>
<!-- Second textbox -->
<TextBlock Text="Second textbox"></TextBlock>
<TextBox Text="{x:Bind Path=ViewModel.MetadataModel.SecondText}"></TextBox>
...
</Grid>
SecondMainPage.xaml
<Grid>...
<uc:MetadataUserControl>
</uc:MetadataUserControl>
<Button Content="Create final"></Button>
</Grid>
Call WeakReferenceMessenger.Default.Send(new NotifyParentMessage(new YourInfoClass())); in your Send() method in the child's view model and make sure that the YourInfoClass class contains all information that the parent view model needs about the child, i.e. the values of all properties that are bound to the textboxes in the view.
Managed to do it using Messenger's request feature. When the parent sends an event, the children replies with the object required.
MainViewModel.cs
private void CreateFile()
{
// Request data from children user controls
var metadataRequested = WeakReferenceMessenger.Default.Send<MetadataRequestMessage>();
}
MetadataViewModel.cs constructor
WeakReferenceMessenger.Default.Register<MetadataViewModel, MetadataRequestMessage>(this, (r, m) =>
{
m.Reply(r.MetadataModel);
});
..
public class MetadataRequestMessage : RequestMessage<MetadataModel>
{
}

Xamarin Android WebView not opening links in browser once SetWebViewClient is called

I have a simple webview that is loading some locally stored HTML. There are plain vanilla links in the HTML, like <a href="https://google.com"/>. When a user taps on one of these links, I want the device browser to open.
This is the default behaviour for an Android webview and seems to work fine, however as soon as I call the webView.SetWebViewClient(webViewClient) method, this default behaviour stops.
I've tried overriding the ShouldOverrideUrlLoading() method(s) in my implementation of the WebViewClient class to open the browser myself, but these methods are never getting called.
To summarise, like this, links in my HTML will open in the browser:
webView = new WebView(Activity);
//var webViewClient = new WebViewClient();
//webView.SetWebViewClient(webViewClient);
webView.LoadData(myHTML, "text/html", null);
But as soon as I uncomment those two lines, the links will open within the WebView.
I'm not sure why your ShouldOverrideUrlLoading method is not being called in your class as I don't have all your code to look at. So, I put together a fully working example where you can control how the links are opened--whether in your webview view or another Android browser activity. I hope it helps!
WebView.axml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/WebView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/transparent">
<WebView
android:id="#+id/webviewMain"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF" />
</LinearLayout>
testwebview.html ("Assets/html/testwebview.html" in project)
<html>
<head>
<title>WebView Testing</title>
</head>
<body>
<h1>Select a link...</h1>
<br />
Google
<br />
Stack Overflow
</body>
</html>
WebViewCustomOverrideActivity.cs
using Android.App;
using Android.OS;
using Android.Webkit;
using Android.Content;
namespace XamdroidMaster.Activities {
[Activity(Label = "Custom WebView Testing", MainLauncher = true)]
public class WebViewCustomOverrideActivity : Activity {
protected override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.WebView);
WebView wv = FindViewById<WebView>(Resource.Id.webviewMain);
MyWebViewClient myWebViewClient = new MyWebViewClient();
wv.SetWebViewClient(myWebViewClient);
wv.LoadUrl("file:///android_asset/html/testwebview.html"); // "Assets/html/testwebview.html" in project
}
}
public class MyWebViewClient : WebViewClient {
public override bool ShouldOverrideUrlLoading(WebView view, string url) {
if (url.ToLower().Contains("google.com")) {
// Let my WebView load the page
return false;
}
// For all other links, launch another Activity that handles URLs
var intent = new Intent(Intent.ActionView, Android.Net.Uri.Parse(url));
view.Context.StartActivity(intent);
return true;
}
}
}

Add Page on Navigate Drawer Menu and navigate to respective page on menu click Xamarin Android

I have made a sample with Navigate Drawer Menu from Left side. I have fixed 4 menu items as (Home, About Us, Enquiry, Contact Us).
Now I need to add pages to my layout, By default it should show Home Page with respective content. Then when I would click on About Us menu item then should display About Us Page with respective content...so on for other menu links too.
I didn't have an idea to how to add page and how should I make menu clickable and to track which menu item should have been clicked and which page should have to open and how.
I have searched a lot for this on google, on YouTube and on several tutorials, but didn't found any proper guidance. Therefore kindly help me regarding this please....
below it the code of MainActivity.cs
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Support.V7.App;
using Android.Support.V4.Widget;
using V7Toolbar = Android.Support.V7.Widget.Toolbar;
using Android.Support.Design.Widget;
namespace NavigationDrawerLayout
{
[Activity(Label = "J&K Tour and Travel", Theme = "#style/Theme.DesignDemo", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : AppCompatActivity
{
DrawerLayout drawerLayout;
NavigationView navigationView;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawer_layout);
// Create ActionBarDrawerToggle button and add it to the toolbar
var toolbar = FindViewById<V7Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
var drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, Resource.String.drawer_open, Resource.String.drawer_close);
drawerLayout.SetDrawerListener(drawerToggle);
drawerToggle.SyncState();
navigationView = FindViewById<NavigationView>(Resource.Id.nav_view);
setupDrawerContent(navigationView);
}
void setupDrawerContent(NavigationView navigationView)
{
navigationView.NavigationItemSelected += (sender, e) => {
//e.MenuItem.SetChecked(true);
drawerLayout.CloseDrawers();
};
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
navigationView.InflateMenu(Resource.Menu.nav_menu);
return true;
}
}
}
Also i am sharing a link of .rar file of my whole sample project below. I have made this project using VS 2017 Community Edition using c#:
https://drive.google.com/open?id=0B584mT-OF6vJZzdFem4tMG9jV1U
It is recommended to use NavigationDrawer with Fragment to show different pages.
In your layout you can place a FrameLayout for the container of fragments for example:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<FrameLayout android:id="#+id/framelayout"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<android.support.design.widget.NavigationView
android:layout_width="wrap_content"
android:layout_gravity="start"
android:layout_height="match_parent"
android:id="#+id/nav_view"
app:menu="#menu/nav_menu"
app:headerLayout="#layout/drawer_header" />
</android.support.v4.widget.DrawerLayout>
Then in the OnCreate method first commit your home page like this:
FragmentTransaction transaction = this.FragmentManager.BeginTransaction();
HomeFragment home = new HomeFragment();
transaction.Add(Resource.Id.framelayout, home).Commit();
Then in the NavigationItemSelected event:
var naviview = FindViewById<NavigationView>(Resource.Id.nav_view);
naviview.NavigationItemSelected += (sender, e) =>
{
e.MenuItem.SetChecked(true);
FragmentTransaction transaction1 = this.FragmentManager.BeginTransaction();
switch (e.MenuItem.TitleFormatted.ToString())
{
case "Home":
HomeFragment home = new HomeFragment();
transaction1.Replace(Resource.Id.framelayout, home).AddToBackStack(null).Commit();
break;
case "About Us":
VideoFragment video = new VideoFragment();
transaction1.Replace(Resource.Id.framelayout, video).AddToBackStack(null).Commit();
break;
}
drawerLayout.CloseDrawers();
};
Since it is Fragment, then for example, your Home Page should be something like this:
public class HomeFragment : Fragment
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.homelayout, container, false);
return view;
}
}

Navigation on ViewModel in MVVM Light in Xamarin.Forms

I have a Xamarin.froms app in MVVM Light, i want to open another page on button click, but i only want to use this method of Page class
page.Navigation.PushModalAsync(new MyPage())
How to send "Page" class as reference on ViewModel class.
There are variety of ways to do this.
Easy way:
Pass the Page as a constructor parameter of View Model.
The useful way:
Write a Navigation Service. This can extend from a Content Page like this:
public class NavigationService : ContentPage
{
public static INavigation Navigation
{
get {
return Application.Current.MainPage.Navigation;
}
}
public static IReadOnlyList<Page> NavigationStack () {
return Navigation.NavigationStack;
}
}
Now you can use this Service in your ViewModel like this:
Page lastPage = Navigation.NavigationStack.Last;
XAML code
<Button Text="Help" Command="{Binding NavigateHelpPage}"/>
MVVM Hierarchical page navigation in Xamarin.forms
public ICommand NavigateHelpPage { get; }
private async Task NavigateToHelpPage()
{
await Application.Current.MainPage.Navigation.PushAsync(new HelpPage());
}
Inside Constructor:
NavigateHelpPage = new Command(async () => await NavigateToHelpPage());
MainPage you can set like :
Application.Current.MainPage = new NavigationPage(new Views.Dashboard.DashboardPage());

Custom Popup in Xamarin.Forms

Is there any way to create custom popup dialog with Editor inside it using Xamarin Forms. Targeted to iOS platform.
I want a pop up with a Title Label ,Text box for accepting input and error Label for displaying error message, with OK and Cancel button.
I want to accept pin number from input pop up and have to validate pin. If validation fails I have to show a Error message inside pop up.
Thanks,
This is a good popup for XF that includes the ability to add an editor to the popup.
Popup Page Plugin for Xamarin Forms
// Use these methods in PopupNavigation globally or Navigation in your pages
// Open new PopupPage
Task PushAsync(PopupPage page, bool animate = true) // Navigation.PushPopupAsync
// Hide last PopupPage
Task PopAsync(bool animate = true) // Navigation.PopPopupAsync
// Hide all PopupPage with animations
Task PopAllAsync(bool animate = true) // Navigation.PopAllPopupAsync
// Remove one popup page in stack
Task RemovePageAsync(PopupPage page, bool animate = true) // Navigation.RemovePopupPageAsync
XAML POPUP PAGE
<?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="Demo.Pages.MyPopupPage">
<!--Animations use example-->
<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>
<!-- Content -->
</pages:PopupPage>
POPUP PAGE
public partial class MyPopupPage : PopupPage
{
public SecondPopupPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
// Method for animation child in PopupPage
// Invoced after custom animation end
protected virtual Task OnAppearingAnimationEnd()
{
return Content.FadeTo(0.5);
}
// Method for animation child in PopupPage
// Invoked before custom animation begin
protected virtual Task OnDisappearingAnimationBegin()
{
return Content.FadeTo(1);;
}
protected override bool OnBackButtonPressed()
{
// Prevent hide popup
//return base.OnBackButtonPressed();
return true;
}
// Invoced when background is clicked
protected override bool OnBackgroundClicked()
{
// Return default value - CloseWhenBackgroundIsClicked
return base.OnBackgroundClicked();
}
}
MAINPAGE
// Main Page
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
// Button Click
private async void OnOpenPupup(object sender, EventArgs e)
{
var page = new MyPopupPage();
await Navigation.PushPopupAsync(page);
// or
await PopupNavigation.PushAsync(page);
}
}
Have a look at ACR User Dialogs. https://github.com/aritchie/userdialogs
Nuget Package: https://www.nuget.org/packages/Acr.UserDialogs/
Then have a look at the Prompt Examples: https://github.com/aritchie/userdialogs/blob/master/src/Samples/Samples/ViewModels/StandardViewModel.cs#L97
void Prompt()
{
UserDialogs.Instance.ActionSheet(new ActionSheetConfig()
.SetTitle("Choose Type")
.Add("Default", () => this.PromptCommand(InputType.Default))
.Add("E-Mail", () => this.PromptCommand(InputType.Email))
.Add("Name", () => this.PromptCommand(InputType.Name))
.Add("Number", () => this.PromptCommand(InputType.Number))
.Add("Number with Decimal", () => this.PromptCommand(InputType.DecimalNumber))
.Add("Password", () => this.PromptCommand(InputType.Password))
.Add("Numeric Password (PIN)", () => this.PromptCommand(InputType.NumericPassword))
.Add("Phone", () => this.PromptCommand(InputType.Phone))
.Add("Url", () => this.PromptCommand(InputType.Url))
);
}
Yes, a custom popup can be added in Xamarin forms. Please follow these steps:
Create one stacklayout.
Add the fields in to the stacklayout.
Create one new Frame object.
Make the Frame object background with transaprent color.
Add the Stacklayout object into frame.
Add the frame to main content page.
If you need code, I will update.

Resources