Customizing Xamarin.Forms layouts - xamarin.ios

I'm building an Android and iOS app using Xamarin Forms.
What I'm simply trying to do is set a background drawable on my Android app for my ListView items. The root view of my ListView items are StackLayout's:
var listView = new ListView
{
ItemsSource = items,
ItemTemplate = new DataTemplate(() =>
{
return new ViewCell
{
View = new StackLayout(...)
};
}
};
I know I can access the native element by using a custom renderer:
public class MyEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (e.OldElement == null) {
var nativeEditText = (EditText)Control;
...
}
}
}
But I'm not sure how this would work for a StackLayout (or any other layout for that matter).
I first extended StackLayout:
public class ListViewItem : StackLayout
{
}
And I read somewhere that layouts use the VisualElementRenderer, so I tried the following:
public class ListViewItemRenderer : VisualElementRenderer<StackLayout>
{
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
// any way to access the native element?
}
}
But VisualElementRenderer does not seem to give me access to the native element.
So is there any way I can access the native elements of Layout elements? Or maybe there is a different way to simply set a background drawable on layouts within my Android app?

Even though I still don't know how to access the native element of a layout, the VisualElementRenderer has a method for setting the background drawable on Android (which was exactly what I needed). So I ended up with the following:
protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
{
base.OnElementChanged(e);
SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.listViewItem));
}

I understand you want to hook into an existing Layout renderer and extending it to access the native element with extra capabilities like background image.
Eventually the support for background-image will be supported just like background-colour is, I imagine, across the Layout controls. It may be worth while waiting for this as I can't see why they wouldn't implement these in a later release.
In the mean time you would need something that would work and is quite easy to implement?
Creating the background drawable via inheriting the renderer from a Layout may not be the simplest of solutions therefore, although does have its advantages as you can then re-use easily with the extra functionality across all Layouts for an application.
In your code for ListViewItemRenderer, however, it is inheriting from a Xamarin.Forms control (you specified StackLayout) and have not specified a native, platform dependent, control to be the base for the layout control that would have to match the Xamarin.Forms platform dependent control used.
Each Renderer is tied to a native element. Layout controls will be no different than other custom native control renderers.
For a custom control, you will write a renderer something like the following (note I haven't specified a layout renderer as I haven't had a need to do this yet and am just going from past experience - but similar rules should apply to implementing a renderer for a layout as opposed to a custom control):-
// System.Windows.Controls.Grid in this case is the root native control for a WindowsPhone renderer of MyControl
public class MyControlRenderer : ViewRenderer<MyControlView, System.Windows.Controls.Grid>
There is a simpler approach, however to achieve what you want to do:-
The simpler approach would be instead of inheriting from the Stack Layout control, it would be better to inherit from Grid as the root of the control.
Then you can add an Image control to the Grid and also a Stack Layout for the same Grid Row and Column.
By doing the above you will be able to achieve a background-image across the entire listview item row.

Related

Xamarin.Forms.DatePicker Text color

For Xamarin.Forms.DatePicker It is not obvious that the date field is "tapable" I want to change the text color of it to blue so the user will think it is clickable?
I see a background color property but not forecolor/text color?
Let me know how I can do that.
I did it just creating a class like that on my Android Project and making no changes on my Forms Pages:
using Xamarin.Forms;
using xxxx.Droid;
[assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(MyDatePickerRederer))]
namespace xxxx.Droid
{
public class MyDatePickerRederer : Xamarin.Forms.Platform.Android.DatePickerRenderer
{
protected override void OnElementChanged(Xamarin.Forms.Platform.Android.ElementChangedEventArgs<Xamarin.Forms.DatePicker> e)
{
base.OnElementChanged(e);
this.Control.SetTextColor(Android.Graphics.Color.Black);
}
}
}
I don't think that the forecolor/text color is accessible at this time and I'm not sure if it will be in the future or not. Typically what you would do in this scenario is create a Custom Renderer to get down into the native implementation of the UIDatePicker control for iOS and change it's properties there. The problem with that in this case is that if you look through the iOS SDK documentation as well, I don't believe there is a way to customize the text on the UIDatePicker picker control. This is why you can't do it in Xamarin.Forms either.
At this point you will probably have to create your own custom control/renderer to make such a small change. Frustrating, I know, but unfortunately that this point I don't think you can actually accomplish the simple thing you are looking to do. :-(

MVVMCross changing selected tab bar item from within nested view controller

We're using MVVMCross within our application and I've come up against something that I'm not sure I've solved in the best way possible.
One of our ViewModels contains 3 other view models - a dashboard and 2 lists. In iOS this is presented using a MvxTabBarViewController which works great. Android and WP present this view in a similar manner. An example of the object model is below:
public class ProjectViewModel : MvxViewModel
{
public DashboardViewModel Dashboard {get;set;}
public FirstListViewModel FirstList {get;set;}
public SecondListViewModel SecondList {get;set;}
}
We're now in the situation where if a certain action happens within the DashboardViewModel we would like to instruct the navigation to change the tab in iOS and the same thing to happen on the other platforms.
The only way I've been able to get the tab to change on iOS is to use this.SelectedIndex = 1; from within the iOS ProjectView.
At the moment also the only way I've managed to trigger this change is to fire an event from the DashboardViewModel and then the ProjectViewModel subscribes to this and fires another event which is subscribed to by the ProjectView to instruct it to change the tab in whatever device specific way it needs to. I can't help but think there is a better way to do this.
I've tried taking a look at a custom ViewPresenter for iOS and calling ShowViewModel FirstListViewModel from within the DashboardViewModel but the presenter doesn't appear to be getting used so we just transition normally. My idea was I could get in the middle, cancel the navigation request and then flip the active tab on the ProjectView.
Any suggestions would be appreciated on how we could do this in a better cross platform way using MVVMCross to handle the change if at all possible.
You should be able to do this in any of several ways:
using a custom presenter with overridden Show as you suggest
using a custom presenter with overridden ChangePresentation - and using a custom hint
using a custom binding or a binding to a property within the ProjectView to drive the transition
using a custom IMvxInteraction property
using a custom event from VM to View
using a messenger to send a message from the ViewModels to the Views.
Ultimately lots of these could work and which of these I might choose would depend on which one worked and which one the team are happy with - shipping the working app is always the ultimate goal.
Given where I am with MvvmCross experience, I'd probably opt today for trying the approach of trying a custom IMvxInteraction property. But this might not be for everyone... it certainly might be overkill for this sample...
However, to do this, I would try:
add a public enum Display { Dash, First, Second } to the Core project
add a ProjectViewModel property:
private MvxInteraction<Display> _display = new MvxInteraction< Display >();
public IMvxInteraction<Display> DisplayChange { get { return _display; } }
whenever this ViewModel wants to fire the change it can fire it using e.g. _display.Raise(Display.First)
the ProjectView could then bind Display to its own property which might be implemented like:
private IDisposable _subscription;
private IMvxInteraction<Display> _displayInteraction;
public IMvxInteraction<Display> ChangeDisplay
{
get { return _displayInteraction; }
set
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
}
_displayInteraction = value;
if (_displayInteraction != null)
{
_subscription = _displayInteraction.WeakSubscribe(DoDisplayChange);
}
}
}
private void DoDisplayChange(Display which)
{
// change the tab display here
}
the binding would be added in ViewDidLoad like:
set.Bind(this).For(v => v.ChangeDisplay).To(vm => vm.DisplayChange);

Swap RootViewController with MVVMCross

I need to implement a login/logout using MVVMCross, iOS only to start. After the user logs in, I want to close the view and make the "real" first view the root controller. For logout, I want to do the same in reverse. Whenever the LoginViewModel is requested, clear the root and replace it.
This Remove ViewController from stack indicates there is a ClearTop parameter, but it looks like it is gone in v3?
I then found this What is the best way to handle GoBack for the different MvvmCross (v3) platforms and I implemented this Presenter:
public override void Close(IMvxViewModel toClose)
{
if (toClose is LoginViewModel)
{
ClearBackStack();
Show(new MvxViewModelRequest() { ViewModelType = typeof(FirstViewModel)});
return;
}
base.Close(toClose);
}
public override void Show(MvxViewModelRequest request)
{
if (request.ViewModelType == typeof (LoginViewModel))
{
ClearBackStack();
}
base.Show(request);
}
Is this the correct way to handle this? Is there an easier mechanism (pre-v3 like)? Should I be overriding ChangePresentation instead?
Also, is there a mechanism to call ShowViewModel from a View? Do I need to resolve the IMvxViewDispatcher or is there a more straight forward method?
Yes, if you want to do custom presentation techniques then the easiest way is to implement your own view presenter.
For an introduction and some links on this, see How can I implement SplitView in another view in MvvmCross?
You are free to write code directly in your views, including navigation logic using resolved IoC objects. However, mvvmCross tries to encourage you to put this logic in the viewmodels - especially so that the 'logic' is more easily shared between platforms.

JavaFX 2.1 TableView that includes WebView cells

I want to create a JavaFX table that, in the cells of one column, allows the user to edit XHTML text. I only need very basic formatting capabilities like bold, italic, striketrough.
I have already managed to implement this by using my own subclass of TableCell and using a WebView for each cell (HTMLEditor would of course have been another choice, but my guess is that for my requirements, WebView should be sufficient).
However, to make editing comfortable for the user, I need the following features:
1. The cell height needs to resize if the user enters multi-line text.
2. A context menu (or, if not possible, some other menu or button) should allow formatting parts of the text in a cell as described above (bold, italic..)
Has anybody been successful in implementing something similar ? I have seen suggestions on the web, but they rarely included code samples.
I have succedded doing something similar.
I figured I can share some of the basic clues that allowed me to achieve it.
Resize the whole WebView. For that, the whole WebView must be an editable html page. You achive that by setting contenteditable to true:
<body contenteditable='true' id='content'></body>
You can have a context menu over a webview. But it is something tricky, as you must first disable the original context menu associated to it.
WebView editView;
...
EventDispatcher originalDispatcher = editView.getEventDispatcher();
editView.setEventDispatcher(new WebmenuEventDispatcher(originalDispatcher));
And this is the event dispatcher class:
public class WebmenuEventDispatcher implements EventDispatcher {
private EventDispatcher originalDispatcher;
public WebmenuEventDispatcher(EventDispatcher originalDispatcher) {
this.originalDispatcher = originalDispatcher;
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
if (MouseButton.SECONDARY == mouseEvent.getButton()) {
mouseEvent.consume();
// Show our own menu
cmEdit.show(editView.getScene().getWindow(), mouseEvent.getScreenX(), mouseEvent.getScreenY());
}
}
return originalDispatcher.dispatchEvent(event, tail);
}
}
Now, for setting the font from within that menu, you need a bidirectional Java<->javascript bridge and use some javascript in the webview side.

Want to create a custom component that mirror existing component functionalities, but using different renderer

The JSF component SelectOneRadio layout is very limited so I wrote a custom Renderer for it, and it works great. However, there are times when I want to use the standard SelectOneRadio layout as well. So I decide to make my new component that utilize the custom Renderer I create, but I want this new component to mirror the functionality of SelectOneRadio, and the only different is that it will use my Renderer. Do I need to create both custom tag and custom component to go with my custom renderer in this case? What class should I extends to obtain all functionalities from SelectOneRadio? I would greatly appreciated if you can provided some codes.
EDIT
#BalusC: I like your idea about detecting the value of layout to delegate the correct renderer. So if I have layout="div_layout", then it works great, but if it is pageDirection or lineDirection and nothing show up. What I did is: I create a class that extends MenuRenderer and I override encodeEnd method, so in there I did this
String layout = (String) component.getAttributes().get("layout");
if(layout != null){
if(layout.equals(PAGE_DIRECTION) || layout.equals(LINE_DIRECTION)){
super.encodeEnd(context, component);
return;
} else if (!layout.equals(DIV_LAYOUT)){
//Throw error message
}
}
//Continue with my own renderer code
EDIT2
Above when I said nothing show up, I was wrong. super.encodeEnd(context, component); did render, but instead of render the radio, it render select option tag. So it seems that I delegate to the wrong renderer. I need to use RadioRenderer instead of MenuRenderer.
If it's specific to your own web application, then you could replace just alone the renderer. Easiest is to extend the implementation specific renderer and then depending on the value of one of the standard attributes (layout is the best choice) either delegate to the implementation specific renderer, or do your own custom rendering job.
I case of Mojarra, you'd like to extend com.sun.faces.renderkit.html_basic.RadioRenderer and then register it as follows
<renderkit>
<renderer>
<component-family>javax.faces.SelectOne</component-family>
<renderer-type>javax.faces.Radio</renderer-type>
<renderer-class>com.example.ExtendedRadioRenderer</renderer-class>
</renderer>
</renderkit>
If you wish to be implementation independent, then you'd need to write the entire renderer implementation yourself.
If you wish to have a custom component for it, then you'd need to write it yourself as well.

Resources