How to access code behind from Tablet/Phone template? - xaml

I am taking over a project with a DashboardPage and a DashboardPageViewModel that are linked through DI's ViewModelLocator. The Dashboard page have the following code that separates the Xaml/Code behinds into two separate templates.
private void SetContent()
{
Debug.WriteLine("Dashboardpage setContent");
switch(Device.Idiom)
{
case TargetIdiom.Phone:
Content = new PrimaryPhoneLayout
{
RegionContent = RegionContent
};
break;
case TargetIdiom.Tablet:
Content = new PrimaryTabletLayout(deviceDisplay)
{
RegionContent = RegionContent
};
break;
default:
throw new NotSupportedException($"{Device.Idiom} is not a supported idom");
}
}
I want to add a button in both Phone/Tablet xaml and handle the logic within DashboardPage. How can I make a reference to Dashboard page when these XAML files are linked to their individual code behind and not Dashboard Page?
To elaborate further, DashboardPage derives from MenuContainerPage that allows me to slide in/out of my slide menu. I want to handle this logic through a button that I implemented in both Tablet/Phone layout.

This is how I would do it.
I would start by creating an interface with the events I want to expose from my ContentView
For the sample, I will call this interface as IMenuOptionHandler and it would look like this
public interface IMenuOptionHandler
{
event EventHandler OnSlideIn;
event EventHandler OnSlideOut;
}
Here we have two events that will be invoked from our ContentViews. You can add as many as you wish.
Then we need to make our ContentViews to implement this interface:
public partial class PrimaryPhoneLayouts : ContentView, IMenuOptionHandler
{
//...
#region "IMenuOptionHandler implementation"
public event EventHandler OnSlideIn;
public event EventHandler OnSlideOut;
#endregion
void OnSlideInButtonClicked(object sender, EventArgs e)
{
OnSlideIn?.Invoke(this, EventArgs.Empty);
}
void OnSlideOutButtonClicked(object sender, EventArgs e)
{
OnSlideOut?.Invoke(this, EventArgs.Empty);
}
}
public partial class PrimaryTabletLayout : ContentView, IMenuOptionHandler
{
// ...
#region "IMenuOptionHandler implementation"
public event EventHandler OnSlideIn;
public event EventHandler OnSlideOut;
#endregion
void OnSlideInButtonClicked(object sender, EventArgs e)
{
OnSlideIn?.Invoke(this, EventArgs.Empty);
}
void OnSlideOutButtonClicked(object sender, EventArgs e)
{
OnSlideOut?.Invoke(this, EventArgs.Empty);
}
As you can see both classes are implementing our interface.
Also, I added two sets of methods which are the methods that you will hook to the Buttons on the XAML.
Let's imagine that your XAML looks like this:
<ContentView.Content>
<StackLayout Orientation="Vertical"
HorizontalOptions="FillAndExpand">
<Button Text="SlideIn"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
Clicked="OnSlideInButtonClicked" />
<Button Text="SlideOut"
VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"
Clicked="OnSlideOutButtonClicked" />
</StackLayout>
</ContentView.Content>
Both XAML should have the buttons and the Clicked events wired to our methods in the Code behind classes.
These two methods the only purpose (as of now) is to invoke the events and notify anyone that it's subscribed to them that an event happened.
Now in your DashboardPage
you will add this global property for simplicity
IMenuOptionHandler MenuOptionHandler => Content as IMenuOptionHandler;
This will cast the Content of the Page, whatever it's the value, to IMenuOptionHandler. Any class that implements this interface will allow this cast to happen.
The last part to add on the same DashboardPage is the subscription to the events. These are gonna happen in the OnAppearing method and we will be unsubscribing on the OnDisappearing.
protected override void OnAppearing()
{
base.OnAppearing();
if (MenuOptionHandler != null)
{
MenuOptionHandler.OnSlideIn += MenuOptionHandler_OnSlideIn;
MenuOptionHandler.OnSlideOut += MenuOptionHandler_OnSlideOut;
}
}
protected override void OnDisappearing()
{
base.OnDisappearing();
if (MenuOptionHandler != null)
{
MenuOptionHandler.OnSlideIn -= MenuOptionHandler_OnSlideIn;
MenuOptionHandler.OnSlideOut -= MenuOptionHandler_OnSlideOut;
}
}
void MenuOptionHandler_OnSlideIn(object sender, EventArgs e)
{
//Logic to handle the SlideIn
Debug.WriteLine("MenuOptionHandler_OnSlideIn");
}
void MenuOptionHandler_OnSlideOut(object sender, EventArgs e)
{
//Logic to handle the SlideOut
Debug.WriteLine("MenuOptionHandler_OnSlideOut");
}
Now, whenever one of the Buttons on the ContentView (iPhone or Tablet) is clicked, the Dashboard ContentPage will be notified about this and you will be able to perform any task you wish.
Hope this helps.-

Assume you have a button in Page1, first give a name to the Button in Xaml:
<Button x:Name="btnInPage1" Text="Welcome to Xamarin.Forms!" />
In the code behind of Page1, create a public static property of button, and set the btnPageOne = btnInPage1:
public partial class Page1 : ContentPage
{
public static Button btnPageOne;
public Page1 ()
{
InitializeComponent ();
btnPageOne = btnInPage1;
}
}
Then in your DashboardPage, you can access the button by using Page1.btnPageOne, and handle the logic with:
Page1.btnPageOne.Clicked += delegate {
Console.WriteLine("Page1 btn clicked");
};
The same if you have Page2, Page3...

Related

Using Rg Plugins Popup with Xamarin Forms

I am very new to Xamarin Forms development and I need a popup dialog. I found exactly what I am looking for in https://github.com/rotorgames/Rg.Plugins.Popup, but I cannot for the life of me figure out how to use it. Could someone point me to a working example or provide some direction on use? The README.md on the site is not helping me much.
I want the the popup dialog to appear when a info button is clicked in the top navigation bar. All the popup needs is 1-2 buttons (and labels) for setting user settings.
This is for Xamarin.Forms: iOS and Android.
In simple steps:
Install the plugin in all the projects
Add the PopUp in your
Xaml
Use the methods they provide on the documentacion for Show/Hide the PopUp:
Task PushAsync(PopupPage page, bool animate = true)
Task PopAllAsync(bool animate = true)
They also provide a demo, check it:
https://github.com/rotorgames/Rg.Plugins.Popup/tree/master/src/Demo
Add a reference to the library, i.e. from nuget, to all projects.
Within your Android project, add this Rg.Plugins.Popup.Popup.Init(this, savedInstanceState); inside the MainActivity.cs OnCreate method, before Xamarin Forms Inits.
And the same for the iOS project, inside AppDelegate.cs FinishedLaunching method()
//Android
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Rg.Plugins.Popup.Popup.Init(this, savedInstanceState); /*Must add before the other Xamarin Inits*/
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
Xamarin.Forms.Forms.Init(this, savedInstanceState);
}
//iOS
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
Rg.Plugins.Popup.Popup.Init(); /* place at the top*/
....
}
Add a new ContentPage (.xaml) to your Views directory.
<?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="MyProjectName.Views.MyContentPageName">
<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>
<StackLayout HorizontalAlignment="FillAndExpand" VerticalAlignment="FillAndExpand">
<!-- place your layout content here ....fx a close popup button -->
<Button Clicked="CloseBtn_Clicked" Text="Close" />
</StackLayout>
</pages:PopupPage>
In the ContentPage (PopupPage) code behind file, add using Rg.Plugins.Popup.Services; and inherit from the following
using Rg.Plugins.Popup.Services;
using System;
using System.Threading.Tasks;
using Xamarin.Forms;
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MyContentPageName: Rg.Plugins.Popup.Pages.PopupPage
{
public MyContentPageName()
{
InitializeComponent();
}
public void OnAnimationStarted(bool isPopAnimation)
{
// optional code here
}
public void OnAnimationFinished(bool isPopAnimation)
{
// optional code here
}
protected override bool OnBackButtonPressed()
{
// Return true if you don't want to close this popup page when a back button is pressed
return true;
}
// Invoked when background is clicked
protected override bool OnBackgroundClicked()
{
// Return false if you don't want to close this popup page when a background of the popup page is clicked
return false;
}
private async void CloseBtn_Clicked(object sender, EventArgs e)
{
await PopupNavigation.Instance.PopAsync(true);
}
}
From the .xaml.cs page, where you would like to open the popup, add this:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Rg.Plugins.Popup.Contracts;
using Rg.Plugins.Popup.Services;
public partial class MyOtherPage : ContentPage
{
private IPopupNavigation _popup { get; set; }
private MyContentPageName _modalPage;
public MyOtherPage()
{
_popup = PopupNavigation.Instance;
_modalPage = new MyContentPageName();
}
protected override void OnAppearing()
{
base.OnAppearing();
_popup.Popped += Popup_Popped;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_popup.Popped -= Popup_Popped;
}
private async void Tapped_OpenModal(object sender, EventArgs e)
{
await _popup.PushAsync(_modalPage);
}
/// <summary> Triggered when the MyContentPageName popup is closed "PopAsync()" </summary>
private async void Popup_Popped(object sender, Rg.Plugins.Popup.Events.PopupNavigationEventArgs e)
{
/* add your logic here, if necessary */
}
}
*Note: If your modal simply displays static content, there is no need for a _popped event delegate within the OnAppearing()/OnDisappearing().

OnAppearing event firing twice .with tabbed page

I am facing issue being a beginner for Xamarin forms and MVVM . I have tabbedpage and 2 pages are under tag . Here is code.
Issue is local:ActiveOrderViewPage page OnAppearing() event is firing twice when tabbedPage is loading and execute twice code under OnAppearing() event .
Please help me to find this why this is happening ?
Here Is code Tabbed Page
tabbedpage.xaml
<TabbedPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Title="Orders">
<TabbedPage.Children>
<local:ActiveOrderViewPage />
<local:SavedOrderViewPage />
</TabbedPage.Children>
tabbedpage.xaml.cs
public partial class OrderTabViewPage : TabbedPage
{
public OrderViewModel ViewModel { get { return BindingContext as OrderViewModel; } }
public OrderTabViewPage()
{
InitializeComponent();
this.BindingContext = ViewModelLocator.OrderVModel;
}
public OrderTabViewPage(params object[] arg) : this()
{
if (arg != null)
{
ViewModel.AccountID = Convert.ToInt32(arg[0]);
}
}
Here is active order .cs
public partial class ActiveOrderViewPage : ContentPage
{
public OrderViewModel ViewModel { get { return BindingContext as OrderViewModel; } }
public ActiveOrderViewPage()
{
InitializeComponent();
this.BindingContext = ViewModelLocator.OrderVModel;
}
//public OrderViewPage() : this()
//{
// ViewModel.AccountID = accuntId;
//}
protected override void OnAppearing()
{
base.OnAppearing();
if (ViewModelLocator.OrderVModel.ActiveOrderItems == null || ViewModelLocator.OrderVModel.ActiveOrderItems.List.Count == 0)
{
ViewModelLocator.OrderVModel.ActiveOrderCommand.Execute(null);
}
}
Thanks in advance ...
Having had this problem for a long long time, before realising, I know how frustrating this is. The event OnAppearing() fires twice because of the way that the tabbed page renders all of the individual pages. It initially renders the page, then in your case will render the other page, which causes the OnDisappearing() to fire. The first page then gets focus, causing the OnAppearing() to fire again.
If you only want the code to fire once, after adding the child pages, set the currentpage property to null, which should stop the OnAppearing() from firing again
In my case OnAppearing called twice while i had in app.xaml the follow code:
MainPage = new NavigationPage(new MainPage());
after i change the code to:
MainPage =new MainPage();
The OnAppearing is no more calling twice.

Is it possible to remove or change default flyout transition animation?

When you add a flyout for a button control, for example, after you tap on that button the flyout is displayed with a transition animation which depends on the Placement property of the flyout. If the Placement is set to Top the animation looks like drop down from that button. In my case I would like to either remove the animation altogether or make it expanding by x and y from that button. How to do that?
There is a similar case:How to change ContentDialog transition.
As Rob Caplan said,You cannot override the transitions in the ContentDialog, etc. They are designed to be easy ways to get standard behaviour and will always use the PopupThemeTransition.
So if you want non-standard behaviour then you can write a custom control which uses your own TransitionCollection or without Transition.
For example:
<UserControl
x:Class="Is_it_possible_to_remove_or_change_default_flyou.MyUserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Is_it_possible_to_remove_or_change_default_flyou"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
LostFocus="UserControl_LostFocus">
<UserControl.Transitions>
<TransitionCollection>
<!--Add the Transition that you want-->
</TransitionCollection>
</UserControl.Transitions>
<Grid Name="MyGrid" BorderBrush="Gray" BorderThickness="1" Background="LightGray">
<StackPanel>
<TextBox Name="MyText" Text="Hello"></TextBox>
<TextBox Text="!"></TextBox>
</StackPanel>
</Grid>
</UserControl>
The code behind:
public sealed partial class MyUserControl1 : UserControl
{
public Popup hostPopup;
public double actHei;
public double actWei;
public MyUserControl1()
{
this.InitializeComponent();
hostPopup = new Popup();
hostPopup.Child = this;
}
public void Show()
{
hostPopup.IsOpen = true;
actHei = MyGrid.ActualHeight;
actWei = MyGrid.ActualWidth;
MyText.Focus(FocusState.Pointer);
}
private void UserControl_LostFocus(object sender, RoutedEventArgs e)
{
hostPopup.IsOpen = false;
}
}
In MainPage code behind:
public sealed partial class MainPage : Page
{
public MyUserControl1 popup;
public MainPage()
{
this.InitializeComponent();
popup = new MyUserControl1();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (popup.hostPopup.IsOpen != true)
{
var ttv = MyButton.TransformToVisual(Window.Current.Content);
Point screenCoords = ttv.TransformPoint(new Point(0, 0));
popup.hostPopup.VerticalOffset = screenCoords.Y - 100;
popup.hostPopup.HorizontalOffset = screenCoords.X;
popup.Show();
}
}
}
Contrary to what Rob Caplan and Jayden statet it is actually possible to override the default Transition of a ContentDialog.
You can create a class extending the ContentDialog and subscribe to to Loading event as shown in the code below.
There you can override the Transitions of the newly created popup for the ContentDialog.
I know this is a late answer and this might be a bit of a hacky solution but since I was looking for it I hope I can help somebody.
Example Code (no xaml):
public class CustomContentDialog : ContentDialog {
public CustomContentDialog() {
Loading += (sender, args) => {
// gets the background rectangle and the popup containing the ContentDialog
var popups = VisualTreeHelper.GetOpenPopups(Window.Current);
// remove all transitions and childtransitions from the popups
foreach (var p in popups) {
p.ChildTransitions = new TransitionCollection {
// your transitions
};
p.Transitions = new TransitionCollection {
// your transitions
};
}
};
}
}

XAML Binding - App.cs

Question about binding in XAML with WP8.
In my App.cs I declare a public property for class Setting. In other xaml pages I need to access that propery and pass that property to a ConverterParameter. I can't say I've found a clean way of doing this. Below is my current method of how I accomplish this, but it just feels dirty. Any other ways out there?
So what's happening with code below? In app the settings data gets loaded. Any time the settings gets loaded or the a setting changes it Removes/Adds App.Current.Resource. This then allows me to data bind it {StaticResource {resourceName}}
Again, this works 100%...but is there a better/another way to accomplish this?
App.cs
private static Settings _settings = null;
public static Settings Settings
{
get { return _settings; }
private set { _settings = value; }
}
private async void Application_Launching(object sender, LaunchingEventArgs e)
{
if (Settings == null)
Settings = await FlightPath.Core.Data.LoadSettingsAsync();
App.Current.Resources.Add("Settings", App.Settings);
Settings.SettingsChanged += Settings_SettingsChanged;
}
private void Settings_SettingsChanged(object sender, EventArgs e)
{
if (App.Current.Resources["Settings"] == null)
App.Current.Resources.Add("Settings", App.Settings);
else
{
App.Current.Resources.Remove("Settings");
App.Current.Resources.Add("Settings", App.Settings);
}
}
Application Page XAML using Converter / ConverterParameter
<TextBlock Text="{Binding observation_time,
Converter={StaticResource ZuluToLocalTimeConverter},
ConverterParameter={StaticResource Settings}}"
Style="{StaticResource PhoneTextNormalStyle}"
Margin="-4,0,0,0"/>
if you are using MVVM you can Create a SettingManager class which having a Singleton instance. Then declare its propert in ViewModelBase class. Finally use it into your xaml code
XAML
C#
class ViewModelBaseClass: InotifyPropertyChanged
{
public SettingManager Settings{get{return SettingManager.Instance;}}
}
class SettingManager
{
public static Instance{get{...}}
public string this[string sName]
{
return "whatever you need";
}
}
class MYViewModel: ViewModelBase
{
}

How to fire a button Click event in code behind in user control?

is it possible to fire a button Click event in code behind in user control?
In C# events can only be invoked from the class that declares them. In case of the Button there is a method called OnClick which raises the ClickEvent but it is protected. So you need to declare class that inherits from Button and change the visibility of OnClick method (or declare some over method that calls base.OnClick)
public class MyButton : Button
{
public new void OnClick()
{
base.OnClick();
}
}
Example of XAML
<StackPanel Background="White" >
<my:MyButton x:Name="TestButton" Click="HandleClick" Content="Test" />
<TextBlock x:Name="Result" />
</StackPanel>
And code behind:
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
new Timer(TimerCall,null,0,1000);
}
private void TimerCall(object state)
{
Dispatcher.BeginInvoke(()=>TestButton.OnClick());
}
private void HandleClick(object sender, RoutedEventArgs e)
{
Result.Text = String.Format("Clicked on {0:HH:mm:ss}",DateTime.Now);
}
}
Though it is always easier to call event handler directly.
HandleClick(this,null)
Then there will be no need for extra plumbing.