I am trying to implement a custom ImageRenderer in iOS subclassing the native UIImageView, but I am having some problems with CreateNativeControl.
In the older Xamarin.Forms version (like 4.2) the custom native class that I initialized with protected override UIImageView CreateNativeControl() { return new NativeImage(); } looks like it never get called (the message I log in the constructor is not shown). The Custom Renderer is correctly initialized (the right message is logged).
In the latest stable version (like 4.4) in overriding of CreateNativeControl the return type it is said that has to be a FormsUIImageView, never heard of it, anyway I also tried to subclass that but same problem as before, it seems it never get called as the constructor message is not logged. The Custom Renderer is correctly initialized (the right message is logged).
Here the code I used:
public class IOSImageView : ImageRenderer
{
public IOSImageView()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if(Control == null)
{
return;
}
Console.WriteLine("PIPPO created from Custom Renderer"); //this message is correctly logged
}
protected override UIImageView CreateNativeControl() //FormsUIImageView in XF 4.4
{
return new NativeImage();
}
}
public class NativeImage : UIImageView //FormsUIImageView in XF 4.4
{
public NativeImage() : base()
{
Console.WriteLine("PIPPO created from native IOS"); //this message is NOT logged
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
Console.WriteLine("PIPPO touched"); //this (of course because no NativeImage is shown and there is no image to touch) is NOT logged
}
}
FormsUIImageView is new after XF 4.4 which you can check Xamarin.Forms release notes
In your case , you seems want to set the Image Renderer as your custom ImageView, right?
You should invoked SetNativeControl()
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
SetNativeControl(new NativeImage());
}
}
public class NativeImage : FormsUIImageView
{
public NativeImage() : base()
{
this.UserInteractionEnabled = true;
}
public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);
Console.WriteLine("PIPPO touched"); //this (of course because no NativeImage is shown and there is no image to touch) is NOT logged
}
}
Related
[assembly: ExportRenderer(typeof(AppShell), typeof(CustomShellRenderer))]
namespace App4.Droid
{
public class CustomShellRenderer : ShellRenderer
{
public CustomShellRenderer(Context context) : base(context)
{
}
protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
{
return new CustomShellBottomNavViewAppearanceTracker();
}
}
public class CustomShellBottomNavViewAppearanceTracker : IShellBottomNavViewAppearanceTracker
{
public void Dispose()
{
}
public void ResetAppearance(BottomNavigationView bottomView)
{
}
public void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
{
(bottomView.Parent as LinearLayout)?.SetBackgroundColor(Color.Transparent.ToAndroid());
bottomView.SetBackgroundColor(Color.Transparent.ToAndroid());
bottomView.LabelVisibilityMode = LabelVisibilityMode.LabelVisibilityUnlabeled;
}
}
}
shell customrenderer in android
I want to overlap the content part and bottom navigation part
I know how to implement purely XAML-only without shell and CustomRender, but I don't want to. Because my project is highly dependent on Shell
ex)
https://github.com/naweed/MauiPlanets
above app is overlap bottom navigation and contentpage
in my case remove tabbar background and remain icons
Simply put, I would like to lower the height of the content page overlapping with the bottom navigation.
Full screen should be able to meet your need, if I understand correctly.
Add this to your MainActivity.cs:
protected override void OnCreate(Bundle savedInstanceState)
{
...
IWindowInsetsController wicController = Window.InsetsController;
Window.SetDecorFitsSystemWindows(false);
Window.SetFlags(WindowManagerFlags.Fullscreen, WindowManagerFlags.Fullscreen);
if (wicController != null)
{
wicController.Hide(WindowInsets.Type.NavigationBars());
}
}
I see this often:
protected override void OnAppearing()
{
base.OnAppearing();
Why do people add base.OnAppearing()
Also:
protected override void OnStart()
{
base.OnStart();
Is it needed to override OnStart and is that also a similar lifecycle event?
OnAppearing is a virtual method defined in the Xamarin.Forms.Page class
namespace Xamarin.Forms
{
[RenderWith(typeof(_PageRenderer))]
public class Page : VisualElement, ILayout, IPageController, IElementConfiguration<Page>, IPaddingElement
{
// ...
protected virtual void OnAppearing()
{
}
// ...
}
}
and gets also called from the base class
[EditorBrowsable(EditorBrowsableState.Never)]
public void SendAppearing()
{
if (_hasAppeared)
return;
_hasAppeared = true;
if (IsBusy)
{
if (IsPlatformEnabled)
MessagingCenter.Send(this, BusySetSignalName, true);
else
_pendingActions.Add(() => MessagingCenter.Send(this, BusySetSignalName, true));
}
OnAppearing(); // <--- here
Appearing?.Invoke(this, EventArgs.Empty);
var pageContainer = this as IPageContainer<Page>;
pageContainer?.CurrentPage?.SendAppearing();
FindApplication(this)?.OnPageAppearing(this);
}
It is very common to call the base method within an overridden method.
I am trying to prevent the user of my app from pressing the hardware back button.
I have found this code snippet that is in the code behind the xaml file:
protected override bool OnBackButtonPressed()
{
return true;
}
I have tried variations of this including using Boolean instead of Bool and returning base.functionname nothing seems to fire this method.
Here is the bigger context:
Code behind:
namespace Watson.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StartScan : ContentPage
{
public StartScan()
{
InitializeComponent();
}
protected override bool OnBackButtonPressed()
{
return true;
}
}
}
This is the second page in the stack and disabling the button only needs to happen on this page only, no where else.
Any help would be appreciated.
This is working for me, I tried in Android and iOS platforms using Xamarin.Forms.
Hope you can resolve with this piece of code.
namespace Test
{
public partial class TestPage2 : ContentPage
{
public TestPage2()
{
NavigationPage.SetHasBackButton(this, false);
InitializeComponent();
}
protected override bool OnBackButtonPressed()
{
//return base.OnBackButtonPressed();
return true;
}
}
}
Thanks,
You can also add NavigationPage.SetHasBackButton property in the XAML
NavigationPage.HasBackButton="True"
In the Content Page
You can do this way:
namespace Watson.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class StartScan : ContentPage
{
public StartScan()
{
NavigationPage.SetHasBackButton(this, false);
InitializeComponent();
}
protected override bool OnBackButtonPressed()
{
// you can put some actions here (for example, an dialog prompt, ...)
return true;
}
}
}
For me it is working with the following code (Samsung Android)
protected override bool OnBackButtonPressed()
{
//base.OnBackButtonPressed();
browser.GoBack();
return true;
}
OnBackButtonPressed firing when you click the back button on your device, not from your navigation page, do your logic in OnDisappearing!
I'm building custom control by extending ScrollableControl.
Problem is that my custom control acts as container - I can drag controls into it:
My question is how can I disable container functionality in class that extends ScrollableControl
Below are two test controls, one extends Control, second ScrollableControl
public class ControlBasedControl : Control
{
protected override Size DefaultSize
{
get { return new Size(100, 100); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LightCoral, ClientRectangle);
}
}
public class ScrollableControlBasedControl : ScrollableControl
{
public ScrollableControlBasedControl()
{
AutoScrollMinSize = new Size(200, 200);
}
protected override Size DefaultSize
{
get { return new Size(100, 100); }
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.LawnGreen, ClientRectangle);
}
}
You get "acts-like-a-container" behavior at design time from the [Designer] attribute. Copy-pasting from the Reference Source:
[
ComVisible(true),
ClassInterface(ClassInterfaceType.AutoDispatch),
Designer("System.Windows.Forms.Design.ScrollableControlDesigner, " + AssemblyRef.SystemDesign)
]
public class ScrollableControl : Control, IArrangedElement {
// etc...
}
It is ScrollableControlDesigner that gets the job done. Doesn't do much by itself, but derived from ParentControlDesigner, the designer that permits a control to act as a parent for child controls and gives it container-like behavior at design time.
Fix is easy, you just have to use your own [Designer] attribute to select another designer. Add a reference to System.Design and make it look like this:
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms.Design; // Add reference to System.Design
[Designer(typeof(ControlDesigner))]
public class ScrollableControlBasedControl : ScrollableControl {
// etc...
}
There is probably more than one way to accomplish this, but here is what I would do...
First create a read-only version of ControlCollection
public class ReadOnlyControlCollection : Control.ControlCollection
{
public ReadOnlyControlCollection(Control owner)
: base(owner)
{
}
public override bool IsReadOnly
{
get { return true; }
}
public override void Add(Control control)
{
throw new ArgumentException("control");
}
}
Then make your ScrollableControlBasedControl create an instance of ReadOnlyControlCollection in stead of the default ControlCollection
public class ScrollableControlBasedControl : ScrollableControl
{
protected override Control.ControlCollection CreateControlsInstance()
{
return new ReadOnlyControlCollection(this);
}
// The rest of your class goes here...
}
I use Visual Studio 2010 and when I drop a control on an ScrollableControlBasedControl the control is magically moved back to where it came from, as if the action was cancelled.
I have a little problem, which I can't solve..
Well, I built a BaseActivity.cs Class:
public class BaseActivity<T> : MvxBindingTabActivityView<T> where T : class, IMvxViewModel
{
protected override void OnViewModelSet()
{ }
public override bool OnCreateOptionsMenu(IMenu menu)
{
// GroupId, ItemId, OrderId
menu.Add(0, 0, 0, "Einstellungen").SetIcon(Android.Resource.Drawable.IcMenuManage);
menu.Add(0, 1, 1, "Info").SetIcon(Android.Resource.Drawable.IcMenuInfoDetails);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
var id = item.ItemId + 1; // (Id is zero-based :)
if (id == 1) // First Item
{
StartActivity(typeof(SettingsShowActivity));
}
else if (id == 2) // Second Item
{
Android.App.AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog ad = builder.Create();
ad.SetTitle("Information");
ad.SetIcon(Android.Resource.Drawable.IcDialogAlert);
ad.SetMessage("Version: 0.1");
ad.SetButton("OK", (s, e) => { Console.WriteLine("OK Button clicked, alert dismissed"); });
ad.Show();
}
return true;
}
}
The goal of this class is, that I can put things in that I will use in every other Activity, just like here, the OptionsMenu, which is more or less on all Activities..
Then my other two Activities which are inheriting from BaseActivity.cs:
the MainScreenActivity.cs:
[Activity]
public class MainScreenActivity : BaseActivity<MainScreenViewModel>
{
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.MainScreenLayout);
TabHost.TabSpec spec;
Intent intent;
intent = base.CreateIntentFor<AddressesSearchViewModel>();
intent.AddFlags(ActivityFlags.NewTask);
spec = TabHost.NewTabSpec("adressen");
spec.SetIndicator("Adressen");
spec.SetContent(intent);
TabHost.AddTab(spec);
intent = base.CreateIntentFor<ContactsSearchViewModel>();
intent.AddFlags(ActivityFlags.NewTask);
spec = TabHost.NewTabSpec("kontaktpersonen");
spec.SetIndicator("Kontaktpersonen");
spec.SetContent(intent);
TabHost.AddTab(spec);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
}
and the LoginActivity.cs:
[Activity]
public class LoginActivity : BaseActivity<LoginViewModel>
{
protected override void OnResume()
{
base.OnResume();
App.IsLoggedIn = false;
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.Login);
//App.MessageHub.Subscribe<ErrorMessage>((m) => { ErrorMessageAlert(m.Message, m.Title); });
}
}
Its compiling fine, but the app crashes when I start it, and thats the errormessage I get: Your content must have a TabHost whose id attribute is 'android.R.id.tabhost' . I suggest, that it is because I "needed" to implement the abstract interface into the BaseActivity.cs :
protected override void OnViewModelSet()
{ }
So maybe he walks into the 'false' OnViewModelSet(), (In the empty one instead of the one which is building the Tabhost).. but I'm actually not sure.. btw this comes from: MvxBindingTabActivityView..
Hmm any help would be appreciated
I think this is a quite simple problem...
MvxBindingTabActivityView inherits from TabActivity (see source) and it's this class that requires the content - Your content must have a TabHost whose id attribute is 'android.R.id.tabhost'
If you don't want to use Tabs, then just inherit from MvxBindingActivityView instead - this is what the conference sample does - https://github.com/slodge/MvvmCross/blob/vnext/Sample%20-%20CirriousConference/Cirrious.Conference.UI.Droid/Views/BaseView.cs
If one of your activities needs to do tabs, but the other doesn't then they need to inherit using different inheritance trees. If you want to share code between the two base classes, then the best way to do this in C# seems to be using extension methods - e.g. see BaseViewExtensionMethods.cs shared between BaseView.cs, BaseTabbedView.cs and BaseMapView.cs in the conference sample.