Large title for an View - android

I have xamarin. form app of few views, one among those views has a title of 42 characters. Is there any way to get that displayed on view without missing any character. When I try this renderer I am getting font size applicable for every view but I need to display that for the only specific view which has a title of 42 characters.
[assembly: ExportRenderer(typeof(CustomNavigationPageControl), typeof(CustomNavigationPageRenderer))]
namespace ALCInspection.Droid.Dependecies
{
public class CustomNavigationPageRenderer : NavigationPageRenderer
{
public CustomNavigationPageRenderer(Context context) : base(context)
{
}
private Android.Support.V7.Widget.Toolbar _toolbar;
private Android.Support.V7.Widget.Toolbar toolbar;
public override void OnViewAdded(Android.Views.View child)
{
base.OnViewAdded(child);
if (child.GetType() == typeof(Android.Support.V7.Widget.Toolbar))
{
toolbar = child as Android.Support.V7.Widget.Toolbar;
toolbar.ChildViewAdded += Toolbar_ChildViewAdded;
var a = toolbar.ChildCount;
}
}
void Toolbar_ChildViewAdded(object sender, ChildViewAddedEventArgs e)
{
var view = e.Child.GetType();
if (e.Child.GetType() == typeof(Android.Support.V7.Widget.AppCompatTextView))
{
var textView = e.Child as Android.Support.V7.Widget.AppCompatTextView;
textView.TextSize = 16;
toolbar.ChildViewAdded -= Toolbar_ChildViewAdded;
}
}
}
}
public class CustomNavigationPageControl : NavigationPage
{
public CustomNavigationPageControl(Page root) : base(root)
{
}
}
public static async Task NavigateToAsyncToSmallTitleView(Page firstPageToNavigate, INavigation navigation = null)
{
try
{
if (navigation == null)
{
navigation = ((CustomNavigationPageControl)Application.Current.MainPage).Navigation;
}
await navigation.PushAsync(firstPageToNavigate, false);
}
catch(Exception exception)//exception specified cast is not valid
{
}
}
and i am calling it as
await Helper.NavigateToAsyncWithSmallTitle(new OtherViwq());
I come with above code on searching but it is throwing specified cast exception.

According to this question, you need to use a custom renderer.
Have a look also at here, duplicate question here and here.
Hope it helps..

Related

Xamarin Forms Shell how to customize tab with custom renderers

I'd like to achieve this design (highlight the selected tab with a gradient):
I've been trying to achieve this on android at first using ShellTabLayoutAppearanceTracker and a custom ShellRenderer but I can't even change the background color of any tab in the tablayout.
Also even though I have 4 tabs in my tab bar, tabLayout.TabCount only returns 1. Clearly, there's something I don't understand in all this.
How would you go about it?
Bonus points for an iOS solution as well.
Here's a my code so far:
[assembly: ExportRenderer(typeof(***.App.AppShell), typeof(***.App.Droid.CustomShellRenderer))]
namespace ***.App.Droid
{
public class CustomShellRenderer : ShellRenderer
{
public CustomShellRenderer(Context context) : base(context) {}
protected override IShellTabLayoutAppearanceTracker CreateTabLayoutAppearanceTracker(ShellSection shellSection)
{
return new CustomShellTabLayoutAppearanceTracker(this);
}
}
public class CustomShellTabLayoutAppearanceTracker : ShellTabLayoutAppearanceTracker
{
public CustomShellTabLayoutAppearanceTracker(IShellContext shellContext) : base(shellContext) { }
public override void SetAppearance(TabLayout tabLayout, ShellAppearance appearance)
{
base.SetAppearance(tabLayout, appearance);
for (var i = 0; i < tabLayout.TabCount; i++)
{
var tab = tabLayout.GetTabAt(i);
if (tab.IsSelected)
{
tab.View.Background = new GradientDrawable(/* ... */);
}
else
{
tab.View.SetBackgroundColor(appearance.BackgroundColor.ToAndroid());
}
}
}
}
}
In your custom ShellRenderer try overriding the CreateBottomNavViewAppearanceTracker method i.e.
protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
{
return new BottomNavView(this, shellItem);
}
And in the custom BottomNavView you can then do like:
public class BottomNavView : ShellBottomNavViewAppearanceTracker
{
public BottomNavView(IShellContext context, ShellItem shellItem) : base(context, shellItem) { }
public override void SetAppearance(Google.Android.Material.BottomNavigation.BottomNavigationView bottomView, IShellAppearanceElement appearance)
{
base.SetAppearance(bottomView, appearance);
BottomNavigationMenuView bottomNavigationView = bottomView.GetChildAt(0) as BottomNavigationMenuView;
var firstItem = bottomNavigationView.GetChildAt(0);
firstItem.Background = new GradientDrawable(GradientDrawable.Orientation.TopBottom, new int[] { Color.Red.ToAndroid(), Color.White.ToAndroid(), Color.Blue.ToAndroid() });
}
}
And with that you can draw your gradients i.e. in this case:
I just find a way to change Xamarin.Shell select Tab backgroud in ios, like this:
[assembly: ExportRenderer(typeof(AppShell), typeof(MyShellRenderer))]
namespace App434.iOS
{
public class MyShellRenderer : ShellRenderer
{
protected override IShellSectionRenderer CreateShellSectionRenderer(ShellSection shellSection)
{
var renderer = base.CreateShellSectionRenderer(shellSection);
if (renderer != null)
{
(renderer as ShellSectionRenderer).NavigationBar.SetBackgroundImage(UIImage.FromFile("monkey.png"), UIBarMetrics.Default);
}
return renderer;
}
protected override IShellTabBarAppearanceTracker CreateTabBarAppearanceTracker()
{
return new MyOtherTabBarAppearanceTracker();
}
}
public class MyOtherTabBarAppearanceTracker : ShellTabBarAppearanceTracker, IShellTabBarAppearanceTracker
{
void IShellTabBarAppearanceTracker.SetAppearance(UITabBarController controller, ShellAppearance appearance)
{
base.SetAppearance(controller, appearance);
UITabBar tabBar = controller.TabBar;
CGSize size = new CGSize(tabBar.Frame.Width / 2, tabBar.Frame.Height);
//Background Color
UITabBar.Appearance.SelectionIndicatorImage = imageWithColor(size);
}
public UIImage imageWithColor(CGSize size)
{
CGRect rect = new CGRect(0, 0, size.Width, size.Height);
UIGraphics.BeginImageContext(size);
using (CGContext context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(UIColor.Red.CGColor);
context.FillRect(rect);
}
UIImage image = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return image;
}
}
}
You can take a look the following thread:
Background color of selected TabBarItem in Xamarin on iOS
But I don't find the way to change selected tab background in Android, I will update it if I find this.

How to change navigation page back button in xamarin forms

I am trying to change back arrow image in navigation page. For this in android app i created navigation page renderer and then using method toolbar.SetNavigationIcon and its not working, but when i use toolbar.SetLogo its working fine.
protected override void OnElementChanged(ElementChangedEventArgs<NavigationPage> e)
{
base.OnElementChanged(e);
toolbar.SetNavigationIcon(Resource.Drawable.arrow);
toolbar.SetLogo(Resource.Drawable.arrow);
}
public override void OnViewAdded(Android.Views.View child)
{
base.OnViewAdded(child);
if (child.GetType() == typeof(Android.Support.V7.Widget.Toolbar))
{
toolbar = (Android.Support.V7.Widget.Toolbar)child;
toolbar.ChildViewAdded += Toolbar_ChildViewAdded;
}
}
I also tried add image to app:navigationIcon in toolbar.axml, and it shows great in designer
my arrow
But, when i starting my app i have the same standart arrow icon
enter image description here
If your MainActivity is FormsApplicationActivity, you could refer to this example :
https://github.com/jessejiang0214/ChangeBackIconInXF/tree/master/Droid
If your MainActivity type is FormsAppCompatActivity, you could custom a PageRenderer and change the Toolbar's NavigationIcon.
For example :
[assembly: ExportRenderer(typeof(ContentPage), typeof(NavigationPageRendererDroid))]
...
public class NavigationPageRendererDroid : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);
var context = (Activity)Xamarin.Forms.Forms.Context;
var toolbar = context.FindViewById<Android.Support.V7.Widget.Toolbar>(Droid.Resource.Id.toolbar);
toolbar.NavigationIcon = Android.Support.V4.Content.ContextCompat.GetDrawable(context, Resource.Drawable.Back);
}
}
Usage :
MainPage = new NavigationPage(new MainPage());
...
//When click a button in MainPage, navigate to another page
private async void Button_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new TestPage());
}
Effect.
Update :
When you use Navigation.PushAsync() method navigate to another page, the system will automatically update the Toolbar's icon, you could find in the source code :
protected virtual Task<bool> OnPushAsync(Page view, bool animated)
{
return SwitchContentAsync(view, animated);
}
Task<bool> SwitchContentAsync(Page page, bool animated, bool removed = false, bool popToRoot = false)
{
...
UpdateToolbar();
...
}
void UpdateToolbar()
{
...
bool isNavigated = ((INavigationPageController)Element).StackDepth > 1;
if (isNavigated)
{
...
if (NavigationPage.GetHasBackButton(Element.CurrentPage))
{
//Set the navigation back icon < =================== !!! =-=
var icon = new DrawerArrowDrawable(activity.SupportActionBar.ThemedContext);
icon.Progress = 1;
bar.NavigationIcon = icon;
}
}
...
}
Solution :
So we have no choice but to custom a NavigationPageRenderer, override the OnPushAsync method to set the Toolbar's icon.
using AToolbar = Android.Support.V7.Widget.Toolbar;
[assembly: ExportRenderer(typeof(CustomNavigationPage), typeof(NavigationPageRendererDroid))] // APPCOMP
...
public class NavigationPageRendererDroid : Xamarin.Forms.Platform.Android.AppCompat.NavigationPageRenderer // APPCOMP
{
public AToolbar toolbar;
public Activity context;
protected override Task<bool> OnPushAsync(Page view, bool animated)
{
var retVal = base.OnPushAsync(view, animated);
context = (Activity)Xamarin.Forms.Forms.Context;
toolbar = context.FindViewById<Android.Support.V7.Widget.Toolbar>(Droid.Resource.Id.toolbar);
if (toolbar != null)
{
if (toolbar.NavigationIcon != null)
{
toolbar.NavigationIcon = Android.Support.V4.Content.ContextCompat.GetDrawable(context, Resource.Drawable.Back);
//toolbar.SetNavigationIcon(Resource.Drawable.Back);
}
}
return retVal;
}
}
The CustomNavigationPage are defined in PCL :
public class CustomNavigationPage : NavigationPage
{
public CustomNavigationPage(Page startupPage) : base(startupPage)
{
}
}
Usage :
public App()
{
InitializeComponent();
MainPage = new CustomNavigationPage(new MainPage());
}
...
// In MainPage
private async void Button_Clicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new TestPage());
}
I solved this the next way:
In my MainActivity i am added static toolbar property and identified it in OnCreateOptionsMenu
public static Toolbar ToolBar { get; private set; }
public override bool OnCreateOptionsMenu(IMenu menu)
{
ToolBar = FindViewById<Toolbar>(Resource.Id.toolbar);
ToolBar.SetNavigationIcon(Resource.Drawable.arrow);
return base.OnCreateOptionsMenu(menu);
}
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
....
}
Then in PageRenderer:
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
MainActivity.ToolBar?.SetNavigationIcon(Resource.Drawable.arrow);
}
But!! From 2 ways i have bad effect with redrawing
Usage:
async void tapImage_Tapped(object sender, EventArgs e)
{
await Navigation.PushAsync(new ChooseGenrePage(_listGenres));
}

Xamarin TimePicker propertychanged event fired on load

I am developing a Xamarin app for android using Xamarin forms to create my layout. I have come across an issue with my time picker firing on load of my view cell.
What I am doing is creating a list view and then setting the item template to my custom view cell template. In my view cell template I am creating a TimePicker and binding a PropertyChanged event to it. I am also setting the TimePicker.Time property with information retrieved from the database. What seems to happen at this point is that my PropertyChanged event is fired for each item that will be displayed in the list view. This leads to multiple database calls that are not needed.
Is there a way to stop the PropertyChanged event being called until a property has actually been changed?
My code is below.
public class MyCell : ViewCell
{
private readonly TimePicker _myTimePicker;
public MyCell()
{
_myTimePicker = new TimePicker()
{
HorizontalOptions = LayoutOptions.EndAndExpand
};
_myTimePicker.SetBinding(TimePicker.TimeProperty, "StartTime");
_myTimePicker.PropertyChanged += MyTimePicker_PropertyChanged;
var viewLayout = new StackLayout()
{
HorizontalOptions = LayoutOptions.StartAndExpand,
Orientation = StackOrientation.Horizontal,
Padding = new Thickness(20),
Children = { _myTimePicker }
};
View = viewLayout;
}
private void MyTimePicker_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == TimePicker.TimeProperty.PropertyName && _myTimePicker.Time != TimeSpan.MinValue)
{
var dataAccess = new DataAccessLayer();
dataAccess.Update(myTimePicker.Time);
}
}
}
I'm not sure why the propertychanged event is fired multiple times and how to stop it firing until I actually pick a time. Any help would be appreciated.
Below is my code for the form to display the list view. All my xaml is defined in code. 'MyCell' uses the values returned from '_dataAccess.GetTimes()'.
public class TimeDetails : ContentPage
{
private ListView _listView;
private readonly DataAccessLayer _dataAccess = new DataAccessLayer();
protected override void OnAppearing()
{
_listView = new ListView
{
RowHeight = 80,
SeparatorColor = Color.Blue,
SeparatorVisibility = SeparatorVisibility.Default
};
_listView.ItemsSource = _dataAccess.GetTimes();
_listView.ItemTemplate = new DataTemplate(typeof(MyCell));
_listView.ItemSelected += ListView_ItemSelected;
Content = new StackLayout
{
VerticalOptions = LayoutOptions.FillAndExpand,
Children = { _listView }
};
}
}
The return type of _dataAcess.GetTimes() is List of TaskTime. The TaskTime model is shown below.
public class TaskTime
{
public int Id { get; set; }
public string Task { get; set; }
public TimeSpan StartTime { get; set; }
}
class MyCell : ViewCell
{
private readonly TimePicker _myTimePicker;
public MyCell()
{
_myTimePicker = new TimePicker()
{
HorizontalOptions = LayoutOptions.EndAndExpand
};
_myTimePicker.SetBinding(TimePicker.TimeProperty, "StartTime");
_myTimePicker.PropertyChanged += MyTimePicker_PropertyChanged;
_myTimePicker.Focused += _myTimePicker_Focused;
var viewLayout = new StackLayout()
{
HorizontalOptions = LayoutOptions.StartAndExpand,
Orientation = StackOrientation.Horizontal,
Padding = new Thickness(20),
Children = { _myTimePicker }
};
View = viewLayout;
}
bool myTimePickerSelected;
private void _myTimePicker_Focused(object sender, FocusEventArgs e)
{
myTimePickerSelected = true;
}
private void MyTimePicker_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == TimePicker.TimeProperty.PropertyName && myTimePickerSelected)
{
//var dataAccess = new DataAccessLayer();
//dataAccess.Update(myTimePicker.Time);
}
}
}
A simpler solution using focused & unfocused event is :
public class CustomTimePicker : TimePicker
{
public event EventHandler TimeChanged;
private TimeSpan StartValue { get; set; }
public CustomTimePicker ()
{
this.Focused += OnFoused;
this.Unfocused += OnUnfocused;
}
private void OnFoused(object sender, FocusEventArgs e)
{
StartValue = this.Time;
}
private void OnUnfocused(object sender, FocusEventArgs e)
{
if (StartValue != this.Time)
{
TimeChanged?.Invoke(this, e);
}
}
}
what i have seen is that OnAppearing() is finished when all initialization is done. Maybe you could introduce a boolean variable that you then use in PropertyChanged() method. It is a trick because you have to do it from ContentPage, and pass a value to your control when initialization is finished.
I just saw, ther is OnAppearing() method in CellView, this way everything stays in your control class.

Changing keyboard's ImeOptions of Xamarin.Forms.Entry in custom renderer not working on Android

What I have:
I have a custom class MyEntry derived from Xamarin.Forms.Entry and custom renderer classes MyEntryRenderer for Android and iOS.
What I want:
I want to change the keyboard's "enter"-button to a "search"-button by changing ImeOptions on Android and ReturnKeyType on iOS (see sample code). When I press the altered "search"-button, the MyEntry.Completed event should be called (like before when I pressed the un-altered "enter"-button.
What really happens:
On iOS the code works like expected. But on Android nothing happens. The event doesn't get called.
My question:
How can I achieve what I described above on Android?
Sample code:
App.cs:
namespace CustomEntry
{
public class App
{
public static Page GetMainPage()
{
MyEntry entry = new MyEntry {
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Placeholder = "Enter some text"
};
entry.Completed += delegate {
Console.WriteLine("Completed");
};
return new ContentPage {
Content = entry,
};
}
}
}
MyEntry.cs:
namespace CustomEntry
{
public class MyEntry:Entry
{
}
}
MyEntryRenderer.cs (Android):
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace CustomEntry.Android
{
public class MyEntryRenderer:EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null) {
Control.ImeOptions = global::Android.Views.InputMethods.ImeAction.Search;
}
}
}
}
MyEntryRenderer.cs (iOS):
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace CustomEntry.iOS
{
public class MyEntryRenderer:EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null) {
Control.ReturnKeyType = UIReturnKeyType.Search;
}
}
}
}
I found a workaround for my problem myself:
First I added an Action to my custom entry to be called when I press my "search"-button.
MyEntry.cs
namespace CustomEntry
{
public class MyEntry:Entry
{
public Action SearchPressed = delegate {
};
}
}
Second I "listen" for ImeAction.Search like this and call the Action I added to my custom entry.
MyEntryRenderer.cs (Android):
[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace CustomEntry.Android
{
public class MyEntryRenderer:EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null) {
Control.ImeOptions = ImeAction.Search;
Control.EditorAction += (sender, args) => {
if (args.ActionId == ImeAction.Search) {
var entry = (AddressEntry)Element;
entry.SearchPressed();
}
};
}
}
}
}
In a third class where I use MyEntry I can run some code when the "search"-button is pressed like this:
var myEntry = new MyEntry();
myEntry.SearchPressed += SomeMethod;
public class EntryExtensionRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
if ((Element as EntryExtension).NoSuggestionsKey)
{
Control.AutocorrectionType = UITextAutocorrectionType.No;
}
if ((Element as EntryExtension).ReturnKeyType.Equals("Done"))
{
this.AddDoneButton("Done", (EntryExtension)Element);
}
else if ((Element as EntryExtension).ReturnKeyType.Equals("Next"))
{
this.AddDoneButton("Next", (EntryExtension)Element);
}
}
protected void AddDoneButton(string button, EntryExtension entry)
{
UIToolbar toolbar = new UIToolbar(new RectangleF(0.0f, 0.0f, 50.0f, 44.0f));
var doneButton = new UIBarButtonItem();
if (button.Equals("Done")) {
doneButton = new UIBarButtonItem(UIBarButtonSystemItem.Done, delegate
{
entry.KeyPressedEnter();
});
}
if (button.Equals("Next"))
{
doneButton = new UIBarButtonItem("Next", UIBarButtonItemStyle.Done, delegate
{
entry.KeyPressedEnter();
});
}
toolbar.Items = new UIBarButtonItem[] {
new UIBarButtonItem (UIBarButtonSystemItem.FlexibleSpace),
doneButton
};
this.Control.InputAccessoryView = toolbar;
}
}

MVVMCross Get SelectedItem from a MvxBindableListView

Little problem with my Android application and I don't know how to solve it with MVVM Cross.
Here is my ViewModel:
public class AddressesShowViewModel : MvxViewModel
{
public List<Address> Addresses { get; set; }
public AddressesShowViewModel(string addressesForListView)
{
Addresses = JsonConvert.DeserializeObject<List<Address>>(addressesForListView);
}
public IMvxCommand ShowItemCommand
{
get
{
//return new MvxRelayCommand<Type>((type) => this.RequestNavigate(type));
return new MvxRelayCommand(DoShowContact);
}
}
private Address selectedItem;
public Address SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; FirePropertyChanged(() => SelectedItem); }
}
private void DoShowContact()
{
RequestNavigate<AddressShowViewModel>();
}
}
My AddressesShow.axml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res/INMobileCRM4Android.INMobileCRM4Android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Mvx.MvxBindableListView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'Addresses'},'ItemClick':{'Path':'ShowItemCommand'}, 'SelectedItem':{'Path':'SelectedItem'}}"
local:MvxItemTemplate="#layout/addresslistitem"
/>
</FrameLayout>
I would like to know, how I can get the SelectedItem from the ListView in AddressesShow.axml.. I tried to create a Property 'SelectedItem'.. But its getting called at the beginning, when the ViewModel is created (and is obviously returning null), not when the Item is clicked.. Its btw a type of Address, not just a String or something.. Maybe any suggestions?
The lack of SelectedItem in Droid was identified as an issue last week during preparation for Daniel's talk at Build.
To workaround it, there were a couple of quick answers:
1 There is SelectedItemPosition you can use for binding - this is an int
2 You can use a Click ICommand/IMvxCommand binding instead of using SelectedItem - in your example, this would be the same axml but
public IMvxCommand ShowItemCommand
{
get
{
return new MvxRelayCommand<Address>(address => DoShowContact(address));
}
}
To be clear this Click option above is what I would use.
If SelectedItem really is needed...
Then for a complete answer, Daniel and I prototyped a new binding. This binding was registered using:
registry.RegisterFactory(new MvxCustomBindingFactory<MvxBindableListView>("SelectedItem", adapterView => new MvxAdapterViewSelectedItemTargetBinding(adapterView)));
and contained the logic:
using System;
using Android.Widget;
using Cirrious.MvvmCross.Binding.Droid.Views;
using Cirrious.MvvmCross.Binding.Interfaces;
using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics;
namespace Cirrious.MvvmCross.Binding.Droid.Target
{
#warning This needs to be redone for all adapterviews not just list view!
#warning The use of ItemClick instead of ItemSelected needs to be reinvestigated here!
public class MvxAdapterViewSelectedItemTargetBinding : MvxBaseAndroidTargetBinding
{
private readonly MvxBindableListView _view;
private object _currentValue;
public MvxAdapterViewSelectedItemTargetBinding(MvxBindableListView view)
{
_view = view;
((ListView)_view).ItemClick += OnItemClick;
}
private void OnItemClick(object sender, AdapterView.ItemClickEventArgs itemClickEventArgs)
{
var container = (_view.GetItemAtPosition(itemClickEventArgs.Position) as MvxJavaContainer);
if (container == null)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Missing MvxJavaContainer in MvxAdapterViewSelectedItemTargetBinding");
return;
}
var newValue = container.Object;
if (!newValue.Equals(_currentValue))
{
_currentValue = newValue;
FireValueChanged(newValue);
}
}
public override void SetValue(object value)
{
#warning Sort out Equals test here
if (value != null && value != _currentValue)
{
var index = _view.Adapter.GetPosition(value);
if (index < 0)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning, "Value not found for spinner {0}", value.ToString());
return;
}
_currentValue = value;
_view.SetSelection(index);
}
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
public override Type TargetType
{
get { return typeof(object); }
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
((ListView)_view).ItemClick -= OnItemClick;
}
base.Dispose(isDisposing);
}
}
}
To test this worked, I used the Tutorial PullToRefresh code adapted using:
<Mvx.MvxBindableListView android:id="#android:id/list" android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="{'ItemsSource':{'Path':'Emails'},'ItemClick':{'Path':'ShowItemCommand'},'SelectedItem':{'Path':'TheSelectedEmail'}}"
local:MvxItemTemplate="#layout/listitem_email"
/>
and:
public class SimpleEmail
{
public string From { get; set; }
public string Header { get; set; }
public string Message { get; set; }
}
private ObservableCollection<SimpleEmail> _emails;
public ObservableCollection<SimpleEmail> Emails
{
get { return _emails; }
private set { _emails = value; RaisePropertyChanged(() => Emails); }
}
private SimpleEmail _email;
public SimpleEmail TheSelectedEmail
{
get { return _email; }
set
{
_email = value;
MvxTrace.Trace(MvxTraceLevel.Error, "HELLO {0} ", value == null ? "null" : value.From);
}
}
One thing to be careful about in all this work is that a listview selected item in Android is slightly different to a listbox selected item in Silverlight/wp - e.g. it can be quite hard to get a listview in android to highlight the current selection and it can be quite hard to get the listview to generate selection changed events.
Note: I've logged an issue on Droid SelectedItem to https://github.com/slodge/MvvmCross/issues/52 - I'll make sure the binding is added to the core library in the near future

Categories

Resources