How to disable Master page in MasterDetailPage of Xamarin Forms - c#

I am working with Xamarin forms and I need to disable the Master page that I use as context menu depending on whether user is logged in or not. I have both Master and Detail pages as separate XAML pages.
<MasterDetailPage.Master>
<view:MenuPage/>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<view:MainViewPage
x:Name="MainView"/>
</MasterDetailPage.Detail>
As you might have guessed, I am trying to incorporate MVVM here, so I tried binding visibility (IsVisible) and enabled (IsEnabled) properties of the Master page, however, I still get the undesired black fade effect when pushing navigation button to access my menu. Instead, I need to completely eat up the button press action.

Should your pages be visible whenever the user is connected or not ?
Or do you have a login page at the start of the application for example ?
If you don't have pages that are visible by both connected users or not, you could implement the login page or another page by defining it as ContentPage. It will take all the screen space and hide the navigationBar.
Then after user connect you call a page as MasterDetailPage and then you will have your navigationBar, ...
Don't know if that's what you're looking for but i hope i was able to help you.

This can be achieved with a custom NavigationRenderer, by overriding the Click event of the drawer icon with your custom logic.
[assembly: ExportRenderer(typeof(NavigationPage), typeof(CustomNavigationPageRenderer))]
namespace RTW.Mobile.App.Droid.Renderers
{
public class CustomNavigationPageRenderer : NavigationPageRenderer, IMessageSender
{
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
base.OnLayout(changed, l, t, r, b);
var toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
for (var i = 0; i < toolbar.ChildCount; i++)
{
var imageButton = toolbar.GetChildAt(i) as ImageButton;
var drawerArrow = imageButton?.Drawable as DrawerArrowDrawable;
if (drawerArrow == null)
continue;
//ensure only one handler is registered
imageButton.Click -= imageButton_Click;
imageButton.Click += imageButton_Click;
}
}
private void imageButton_Click(object sender, EventArgs e)
{
if (!App.IsBlockingConditionTrue)
{
MessagingCenter.Send<IMessageSender>(this, "ToggleMasterIsPresented");
}
}
}
}
Then just subscribe to the message with
MessagingCenter.Subscribe<IMessageSender>(this, "ToggleMasterIsPresented", OnToggleMasterIsPresented);
and handle it.
private void OnToggleMasterIsPresented(IMessageSender obj)
{
_masterDetailPage.IsPresented = !_masterDetailPage.IsPresented;
}

Related

Xamarin forms TabbedPage Navigation between children

I am creating TabbedPage navigation in my project and I have button in page 1, when you click the button it must be go to page 2. It will slide on page 2 of the tabbedpage.
This is how i design my TabbedPage XAML
<TabbedPage
android:TabbedPage.IsSmoothScrollEnabled="True" >
<local:Home Title="Home"/>
<local:MapsLocation Title="Map"/>
<local:FeedbackPage Title="Feedback"/>
<local:Profile Title="Profile"/>
</TabbedPage>
This is my code in my code in button command
public ICommand GotoCommand { get; }
private async void GotoExecute(object sender)
{
var MyObject = (Shop)sender;
await Navigation.PushAsync(new MapsLocation(MyObject.Address));
}
Page 1 when you click the button, It should be slide into the Page 2
Page 2, it should go here when you click the button in the page 1
It should be sliding into child 2 of TabbedPage
How do i achieve this?
If you want to slide to other Tabbedpage, you can use the code below to achieve it. Note that the index of the tab.Children[index] starts from 0 and it should < the count of child tabbedpage or it'll throw the error Index was out of range.
private void Button_Clicked(object sender, EventArgs e)
{
    var tab = this.Parent as TabbedPage;
    tab.CurrentPage = tab.Children[1];
}

ASP.Net Page Lifecycle and dynamic controls

I'm am a little bit stuck in the ASP.Net's page lifecycle. This is my first ASP.Net project after many years of doing React so I might be missing something;)
Simplified code:
protected void Page_Load(object sender, EventArgs e)
{
BuildView();
}
private void BuildView()
{
switch (pageViewMode.Value)
{
case "Overview": BuildOverview(); break;
case "Runs": BuildRunsOverview(); break;
}
}
private void BuildOverview()
{
var tilesContainer = new TilesContainer();
tilesContainer.OnTileClicked += (InfoTile targetTile) =>
{
pageViewMode.Value = targetTile.Value;
BuildView();
};
rootElement.Controls.Add(tilesContainer);
}
The problem is that the "OnTileClicked" event works only on the first load and not after the postback. I believe it has something to do with the page lifecycle and registering the events after the Control events ( https://learn.microsoft.com/en-us/previous-versions/aspnet/ms178472(v=vs.100)?redirectedfrom=MSDN ).
If it is really the case, how do I then dynamically build pages from the code behind? Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?
'Should I really create all the controls ( BuildOverview() and BuildRunsOverview()) and then conditionally show or hide them?'
Answer is: yes.
You don't dynamically build pages from code behind - at least its not that well supported in asp.net pages.
In your case you need the TilesContainer on every postback and attach the event handler to it, else the event won't be called. So it would be easier to put all your controls in the markup (.aspx) and just set them to Visible = false/true depending on your code. Controls you set to Visible = false won't be rendered on the client side, so at least no overhead there.
If you use custom-controls (I assume your TilesContainer is a custom-control), then you need to implement the Visible-property the right way, e.g. if your TilesContainers main control is a Panel, override Visible and set the value there:
public override bool Visible
{
get { return base.Visible; }
// set all neccessary controls visibility here
set { this.pnlMain.Visible = base.Visible = value; }
}

Always get a System.NotSupportedException when quitting a view after subscribing to OnPreDrawListener

I'm in the process of developing an app to display booking information via a client's API in Xamarin.Forms. They wanted a section to display several blocks of information, and that information is often styled with HTML tags (we have no control over how this data is returned). As a quick way to throw a view together to display this information, I created a view which consists of a series of individual webviews to display this information inside a scrollview.
To make this page look less horrendous, I have made a custom renderer for the webview that, when content has been loaded, will resize the view to encapsulate all of the content without scrolling.
Now the actual problem:
To achieve this, inside the custom renderer, in the OnAttachedToWindow() callback, get the webview's ViewTreeObserver and call the AddOnPreDrawListener() method to make use of the OnPreDraw() callback;
The resize is performed in that callback, if the ContentHeight is greater than 0 then it resized the page and returns true.
Then in the OnDetachedFromWindow() callback, I check that the VTO is alive, and unsubscribe from the OnPreDraw listener.
Simple, Right?
The page works exactly as expected when navigating to it, however when trying to navigate away, It ALWAYS throws this error:
System.NotSupportedException: Unable to activate instance of type ExtendedWebViewRenderer from native handle 0xbe9f6eac (key_handle 0xf883b13).
The error does not occur if I remove all the ViewTreeObserver event subscription stuff, but if I do that, then the page will not resize accordingly.
Could someone please point me in the right direction? I have included the brief source code for this. I would appreciate any help on this:
Source
public class ExtendedWebViewRenderer : WebViewRenderer, ViewTreeObserver.IOnPreDrawListener
{
ExtendedWebView _xwebView = null;
WebView _webView;
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.WebView> e)
{
base.OnElementChanged (e);
_xwebView = e.NewElement as ExtendedWebView;
_webView = Control;
}
protected override void OnAttachedToWindow()
{
base.OnAttachedToWindow();
// Here assign a ViewTreeObserver to monitor when the view's children need to be resized.
var _vto = _webView.ViewTreeObserver;
_vto.AddOnPreDrawListener(this);
}
protected override void OnDetachedFromWindow()
{
var _vto = _webView.ViewTreeObserver;
if (_vto.IsAlive)
{
_vto.RemoveOnPreDrawListener(this);
}
this.Control.ClearCache(true);
this.Control.ClearAnimation();
this.Control.StopLoading();
base.OnDetachedFromWindow();
}
public bool OnPreDraw()
{
if (_webView != null)
{
// We get the view's content height and apply that to the parent view.
int contentHeight = _webView.ContentHeight;
if (contentHeight != 0 && contentHeight != _xwebView.HeightRequest)
{
int desiredHeight = contentHeight + _webView.PaddingBottom + _webView.PaddingTop;
_xwebView.HeightRequest = desiredHeight;
}
}
return true;
}
}

Getting frame from custom WPF user control

I am developing a WinRT 8.1 application and I have a MenuFlyout within my custom control. Essentially, when a user clicks an Item within the MenuFlyout, the user is navigated to a different page. My dilemma is that I cannot access the Page element within my user control. Is there any work-around for this? I have looked at many similar SO questions, but none of them worked for me.
public sealed partial class BottomAppBar : UserControl {
public BottomAppBar() {
this.InitializeComponent();
//we are forced to manually add items as flyout does not support command
foreach (Vault v in User.Instance.Vaults) {
MenuFlyoutItem vault = new MenuFlyoutItem();
vault.Text = v.Name;
vault.Click += switchUser;
flyoutVault.Items.Add(vault);
}
}
private void switchUser(object sender, object e) {
//This line results in an error
this.Frame.Navigate(typeof(LoginPage));
/** Does not work as well
var parent = VisualTreeHelper.GetParent(this);
while (!(parent is Page)) {
parent = VisualTreeHelper.GetParent(parent);
}
(parent as Page).Frame.Navigate(typeof(LoginPage));
*/
}
The design-patterned solution is to create a navigation service passing the app frame to it and then use something like dependency injection to pass the navigation service to whomever might need it.
The simple solution is to store the reference to the Frame in your App class and access it through the app object/static property.

How do I get a control which looks like a TabControl with no tabs?

We have a form which displays media items in tab pages of a tab control, and I'm implementing a feature which allows users to 'pop out' the tab pages into their own forms.
However, when I add the media player to a form rather than a TabPage, the background switches from the gradient fill of a tab page to the plain SystemColors.Control background of the parent form. I need to add the the media player to a control which has the same background as a TabControl, but which doesn't display a tab at the top. I tried adding the media player to the TabControl's control collection, but that just throws an exception.
How do I get a control which looks like a TabControl with no tabs? Should I keep trying to add the media player to a TabControl, or should I try to write a Panel with a custom-drawn background? If the latter, how do I make sure that works with all possible themes?
The questions seems to be about the UseVisbleBackgroundStyle. AFAIK only buttons and TabPages have this property.
The following is a very dirty hack, just to get you started:
1) derive a customControl from Panel and add "using System.Windows.Forms.VisualStyles;"
2) Add the following code
//warning: incomplete, add error checking etc
private readonly VisualStyleElement element = VisualStyleElement.Tab.Body.Normal;
public bool UseVisbleBackgroundStyle { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
if (UseVisbleBackgroundStyle)
{
var x = new VisualStyleRenderer(element);
x.DrawBackground(pe.Graphics, this.ClientRectangle);
}
else
{
base.OnPaint(pe);
}
}
Thanks to Henk - I eventually went with:
protected override void OnPaintBackground(PaintEventArgs e)
{
if (TabRenderer.IsSupported && Application.RenderWithVisualStyles)
{
TabRenderer.DrawTabPage(pe.Graphics, this.ClientRectangle);
}
else
{
base.OnPaintBackground(pe);
ControlPaint.DrawBorder3D(pe.Graphics, this.ClientRectangle, Border3DStyle.Raised);
}
}
Try creating your own customer UserControl
This answer is modified from another answer site. It does the trick rather cleanly.
In the load event for the window containing the tab control, try:
// TabControl is the name of the tab control in this window.
TabControl.Appearance = TabAppearance.FlatButtons;
TabControl.Multiline = false;
TabControl.SizeMode = TabSizeMode.Fixed;
TabControl.ItemSize = new Size(0,1);
// The tabs are now gone. Select the panel you want to display
TabControl.SelectTab("InProgressTab");

Categories

Resources