how to display the result of a DateTimePicker in a label - xamarin.forms

good morning
I have the following code for a DateTimePicker, and I would like that when I press on the button, the date is shown in the Label (result), could you help me?
DateTimePicker works inherit a xamarin ContentView instead of just an Entry, xamarin and then creates the Stacklayout which xamarin add the Entry and the date and time xamarin pickers to content.
public class DateTimePicker2 : ContentView, INotifyPropertyChanged
{
public Entry Fecha { get; private set; } = new Entry() { TextColor = Color.Black, HorizontalTextAlignment=TextAlignment.Center };
public DatePicker DatePicker { get; private set; } = new DatePicker() { MaximumDate = DateTime.Today, IsVisible = false };
public TimePicker TimePicker { get; private set; } = new TimePicker() { IsVisible = false };
string stringFormat { get; set; }
public string StringFormat { get { return stringFormat ?? "dd/MM/yyyy HH:mm"; } set { stringFormat = value; } }
public DateTime DateTimed
{
get { return (DateTime)GetValue(DateTimeProperty); }
set { SetValue(DateTimeProperty, value); OnPropertyChanged("DateTime"); }
}
private TimeSpan Time
{
get
{
return TimeSpan.FromTicks(DateTimed.Ticks);
}
set
{
DateTimed = new DateTime(DateTimed.Date.Ticks).AddTicks(value.Ticks);
}
}
private DateTime date
{
get
{
return DateTimed.Date;
}
set
{
DateTimed = new DateTime(DateTimed.TimeOfDay.Ticks).AddTicks(value.Ticks);
}
}
BindableProperty DateTimeProperty = BindableProperty.Create("DateTime", typeof(DateTime), typeof(DateTimePicker2), DateTime.Now, BindingMode.TwoWay, propertyChanged: DTPropertyChanged);
public DateTimePicker2()
{
BindingContext = this;
Content = new StackLayout()
{
Children =
{
DatePicker,
TimePicker,
Fecha
}
};
TimePicker.Unfocused += (sender, args) => Time = TimePicker.Time;
DatePicker.Focused += (s, a) => UpdateEntryText();
GestureRecognizers.Add(new TapGestureRecognizer() { Command = new Command(() => DatePicker.Focus()) });
Fecha.Focused += (sender, args) => { Device.BeginInvokeOnMainThread(() => DatePicker.Focus()); };
DatePicker.Unfocused += (sender, args) => { Device.BeginInvokeOnMainThread(() => { TimePicker.Focus(); date = DatePicker.Date; UpdateEntryText();}); };
}
private void UpdateEntryText()
{
Fecha.Text = DateTimed.ToString(StringFormat);
}
static void DTPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var timePicker = (bindable as DateTimePicker2);
timePicker.UpdateEntryText();
}
}
in Xaml it is used like this
<local:DateTimePicker2 ></local:DateTimePicker2>
<Button Clicked="Button_Clicked"> </Button>
<Label x:Name="result"></Label>

assign a name to your control
<local:DateTimePicker2 x:Name="dt2" />
<Button Clicked="Button_Clicked" />
<Label x:Name="result" />
then in your handler
protected void Button_Clicked(object sender, EventArgs e)
{
result.Text = dt2.Datetimed.ToString();
}

Related

xamarin.forms changing property of observablecollection does not update UI

I have an observrable collection in my class that contains checkboxes. I implemented a button to check all checkboxes at once. I tried just cycling through all elements and checking the box via binding:
void selectAll_clicked(System.Object sender, System.EventArgs e)
{
var x = sender as Button;
if (!allSelected)
{
allSelected = true;
x.Text = AppResources.DeselectAll;
foreach (var elem in contactList)
elem.isChecked = true;
}
else
{
allSelected = false;
x.Text = AppResources.SelectAll;
foreach (var elem in contactList)
elem.isChecked = false;
}
}
}
I am sure this effects the list, but the UI isnt updated at all.
How can I make sure the observablecollection "updates" visibly?
I also tried adding propertychanged handler:
private void SetList()
{
listview_contacts.ItemsSource = contactList;
contactList.CollectionChanged += items_CollectionChanged;
}
static void items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (INotifyPropertyChanged item in e.OldItems)
item.PropertyChanged -= item_PropertyChanged;
}
if (e.NewItems != null)
{
foreach (INotifyPropertyChanged item in e.NewItems)
item.PropertyChanged += item_PropertyChanged;
}
}
static void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
BUt this just says that the cast isnt valid...
Thank you
I was able to achieve that by altering my type like so:
public class ContactType : INotifyPropertyChanged
{
private string _name;
private bool _isChecked;
public string name
{
get => _name; set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(name)));
}
}
public string phone { get; set; }
public string initials { get; set; }
public bool isChecked
{
get => _isChecked; set
{
_isChecked = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(isChecked)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Xamarin Firebase and Syncfusion DataGrid. How do I listen for Firebase changes?

I'm having trouble with the coding to properly listen for Firebase add or update events. My attempt below has the data loading into the Syncfusion Datagrid, but there is a weird glitch where when I click the mouse on the Datagrid and pull-down, the first record in my 4 record set gets added to the bottom of the Datagrid, showing a 5th record... if I update an element in the Datagrid, the change is not reflected in Firebase. If I add or change a value in firebase, it does not update in Datagrid. Any help to steer me in the right direction to get this to work would be appreciated. Here's the code:
the VisualStudio 2019
CookPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Chart_sample"
xmlns:gauge="clr-namespace:Syncfusion.SfGauge.XForms;assembly=Syncfusion.SfGauge.XForms"
xmlns:Syncfusion="clr-namespace:Syncfusion.SfDataGrid.XForms;assembly=Syncfusion.SfDataGrid.XForms"
mc:Ignorable="d"
x:Class="Chart_sample.Views.CookPage">
<StackLayout>
<Syncfusion:SfDataGrid x:Name="sfGrid">
</Syncfusion:SfDataGrid>
</StackLayout>
</ContentPage>
CookPage.xaml.cs
using Chart_sample.Services;
using Syncfusion.SfDataGrid.XForms;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace Chart_sample.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CookPage : ContentPage
{
FirebaseHelper firebaseHelper = new FirebaseHelper();
public CookPage()
{
InitializeComponent();
// for Syncfusion DataGrid
firebaseHelper.listenForEvents();
sfGrid.ItemsSource = ViewProgramModel._returnedEvents;
sfGrid.ColumnSizer = ColumnSizer.Star;
sfGrid.AllowEditing = true;
sfGrid.NavigationMode = NavigationMode.Cell;
sfGrid.SelectionMode = Syncfusion.SfDataGrid.XForms.SelectionMode.Single;
}
}
}
FirebaseHelper.cs
using Firebase.Database;
using Firebase.Database.Query;
using System;
using System.Linq;
namespace Chart_sample.Services
{
public class FirebaseHelper
{
internal ViewProgramModel ViewProgramModel { get; set; }
FirebaseClient firebase = new FirebaseClient("https://pelletpirate.firebaseio.com/");
private readonly string ChildProgram = "ControllerData/Pellet_Pirate_1/Program";
public static IDisposable returnedEvents;
public async void listenForEvents()
{
ViewProgramModel._returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
ViewProgramModel._returnedEvents.Add(programs.ElementAt(i).Object);
}
returnedEvents = firebase.Child(ChildProgram).OrderByKey().AsObservable<ViewProgramModel>()
.Subscribe(eventReceived =>
{
if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
{
var found = ViewProgramModel._returnedEvents.FirstOrDefault(i => i._KEY == eventReceived.Key);
if (found == null)
{
// not in observable collection, add it
ViewProgramModel._returnedEvents.Add(eventReceived.Object);
}
else
{
// event was updated
int tempIndex = ViewProgramModel._returnedEvents.IndexOf(found);
ViewProgramModel._returnedEvents[tempIndex] = eventReceived.Object;
}
}
});
}
}
}
ViewProgrammodel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Chart_sample
{
public class ViewProgramModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _KEy;
private string MOde;
private int TArget;
private string TRigger;
private int TRiggerVAlue;
public string _KEY
{
get { return _KEy; }
set
{
this._KEy = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("_KEY"));
}
}
public string MODE
{
get { return MOde; }
set
{
this.MOde = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MODE"));
}
}
public int TARGET
{
get { return TArget; }
set
{
this.TArget = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TARGET"));
}
}
public string TRIGGER
{
get { return TRigger; }
set
{
this.TRigger = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGER"));
}
}
public int TRIGGERVALUE
{
get { return TRiggerVAlue; }
set
{
this.TRiggerVAlue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGERVALUE"));
}
}
public static ObservableCollection<ViewProgramModel> _returnedEvents = new ObservableCollection<ViewProgramModel>();
}
}
I edit your demo, I achieve the update, Add, delete function.
Here is running GIF.
I change your ViewProgramModel like following code. Just move the _returnedEvents to the FirebaseHelper.cs
namespace Chart_sample
{
public class ViewProgramModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _KEy;
private string MOde;
private int TArget;
private string TRigger;
private int TRiggerVAlue;
public string _KEY
{
get { return _KEy; }
set
{
this._KEy = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("_KEY"));
}
}
public string MODE
{
get { return MOde; }
set
{
this.MOde = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MODE"));
}
}
public int TARGET
{
get { return TArget; }
set
{
this.TArget = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TARGET"));
}
}
public string TRIGGER
{
get { return TRigger; }
set
{
this.TRigger = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGER"));
}
}
public int TRIGGERVALUE
{
get { return TRiggerVAlue; }
set
{
this.TRiggerVAlue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TRIGGERVALUE"));
}
}
}
Here is FirebaseHelper.cs, Note: I achieve the update function just for the TARGET Column, I suggest your to add a Primary-key(Auto-increase) for every record in your database to achieve your search function.
public class FirebaseHelper
{
public ObservableCollection<ViewProgramModel> _returnedEvents { get; set; }
public FirebaseHelper()
{
_returnedEvents = new ObservableCollection<ViewProgramModel>();
}
// internal ViewProgramModel MyViewProgramModel { get; set; }
FirebaseClient firebase = new FirebaseClient("https://xxxxxxxxxx.firebaseio.com/");
private readonly string ChildProgram = "ControllerData/xxxxxx_Pirate_1/Program";
public static IDisposable returnedEvents;
public async Task AddViewProgramModel()
{
//new ViewProgramModel() { MODE="test", TARGET=122, TRIGGER="122", TRIGGERVALUE=333, }
await firebase
.Child(ChildProgram)
.PostAsync( new ViewProgramModel() { MODE = "test", TARGET = 122, TRIGGER = "122", TRIGGERVALUE = 333, });
GetAllData();
}
public async Task UpdateViewProgramModel(ViewProgramModel viewProgramModel , string oldValue)
{
var toUpdatePerson = (await firebase
.Child(ChildProgram)
.OnceAsync<ViewProgramModel>()).FirstOrDefault(a => a.Object.TARGET == Convert.ToInt32( oldValue));
await firebase
.Child(ChildProgram)
.Child(toUpdatePerson.Key)
.PutAsync(viewProgramModel);
GetAllData();
}
public async Task DeleteViewProgramModel(string mode)
{
var toDeletePerson = (await firebase
.Child(ChildProgram)
.OnceAsync<ViewProgramModel>()).FirstOrDefault(a => a.Object.MODE == mode);
await firebase.Child(ChildProgram).Child(toDeletePerson.Key).DeleteAsync();
GetAllData();
}
public async void GetAllData()
{
_returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
_returnedEvents.Add(programs.ElementAt(i).Object);
}
}
public async void listenForEvents()
{
_returnedEvents.Clear();
var programs = await firebase.Child(ChildProgram).OnceAsync<ViewProgramModel>();
for (int i = 0; i < programs.Count; i++)
{
_returnedEvents.Add(programs.ElementAt(i).Object);
}
//returnedEvents = firebase.Child(ChildProgram).OrderByKey().AsObservable<ViewProgramModel>()
// .Subscribe(eventReceived =>
// {
// if (eventReceived.EventType == Firebase.Database.Streaming.FirebaseEventType.InsertOrUpdate)
// {
// var found = _returnedEvents.FirstOrDefault(i => i._KEY == eventReceived.Key);
// if (found == null)
// {
// // not in observable collection, add it
// _returnedEvents.Add(eventReceived.Object);
// }
// else
// {
// // event was updated
// int tempIndex = _returnedEvents.IndexOf(found);
// _returnedEvents[tempIndex] = eventReceived.Object;
// }
// }
//});
}
}
}
Here is CookPage.xaml
<StackLayout>
<Button Text="add" Clicked="Button_Clicked"></Button>
<Button Text="delete" Clicked="Button_Clicked_1"></Button>
<Syncfusion:SfDataGrid x:Name="sfGrid" ItemsSource="{Binding _returnedEvents, Mode=TwoWay} " >
</Syncfusion:SfDataGrid>
</StackLayout>
Here is code about CookPage.cs.
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CookPage : ContentPage
{
FirebaseHelper firebaseHelper = new FirebaseHelper();
public CookPage()
{
InitializeComponent();
// for Syncfusion DataGrid
firebaseHelper.listenForEvents();
//sfGrid.ItemsSource = ViewProgramModel._returnedEvents;
BindingContext= firebaseHelper;
sfGrid.ColumnSizer = ColumnSizer.Star;
sfGrid.AllowEditing = true;
sfGrid.NavigationMode = NavigationMode.Cell;
sfGrid.AllowLoadMore = true;
sfGrid.AutoGenerateColumns = true;
//sfGrid.AutoGenerateColumnsMode= AutoGenerateColumnsMode.
sfGrid.SelectionMode = Syncfusion.SfDataGrid.XForms.SelectionMode.Single;
sfGrid.AllowPullToRefresh = true;
sfGrid.CurrentCellEndEdit += SfGrid_CurrentCellEndEdit; ;
}
private async void SfGrid_CurrentCellEndEdit(object sender, GridCurrentCellEndEditEventArgs e)
{
//throw new System.NotImplementedException();
var selectObj = sender as SfDataGrid;
RowColumnIndex index = e.RowColumnIndex;
int selectColumnIndex = index.ColumnIndex; //2
int selectRowIndex = index.RowIndex; //3
var ob=firebaseHelper._returnedEvents;
ViewProgramModel selectObject =ob[selectRowIndex-1];
var newVale = e.NewValue.ToString();
var oldeValue = e.OldValue.ToString();
//Here just judge TARGET Column, you should judge all Columns
if (selectColumnIndex == 2)
{
selectObject.TARGET = Convert.ToInt32(newVale);
}
//If you want to achieve the all Grid change function, you should judge the selectRowIndex for every change
//if (selectRowIndex==1)
//{
// selectObject.MODE = newVale;
//}else if (selectRowIndex==2)
//{
// selectObject.TARGET = Convert.ToInt32( newVale);
//}else if (selectRowIndex == 3)
//{
// selectObject.TRIGGER = newVale;
//}else if (selectRowIndex == 4)
//{
// selectObject.TRIGGERVALUE = Convert.ToInt32(newVale);
//}
await firebaseHelper.UpdateViewProgramModel(selectObject, oldeValue);
}
private async void Button_Clicked(object sender, System.EventArgs e)
{
await firebaseHelper.AddViewProgramModel();
}
private async void Button_Clicked_1(object sender, System.EventArgs e)
{
await firebaseHelper.DeleteViewProgramModel("test");
}
}

Set background colour of HVScrollView item on click

I have implemented the HVScrollView custom renderer in Xamarin.Forms as below and am trying to set the background colour of the item selected on tapped/clicked but can't figure out how to do that.
I can't figure out whether I need to add something to the custom renderer to make it show the selected item or whether it should be set in my markup.
Please help
public class HVScrollGridView : Grid
{
private ICommand _innerSelectedCommand;
private readonly ScrollView _scrollView;
private readonly StackLayout _itemsStackLayout;
public event EventHandler SelectedItemChanged;
public StackOrientation ListOrientation { get; set; }
public double Spacing { get; set; }
public static readonly BindableProperty SelectedCommandProperty =
BindableProperty.Create("SelectedCommand", typeof(ICommand), typeof(HVScrollGridView), null);
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create("ItemsSource", typeof(IEnumerable), typeof(HVScrollGridView), default(IEnumerable<object>), BindingMode.TwoWay, propertyChanged: ItemsSourceChanged);
public static readonly BindableProperty SelectedItemProperty =
BindableProperty.Create("SelectedItem", typeof(object), typeof(HVScrollGridView), null, BindingMode.TwoWay, propertyChanged: OnSelectedItemChanged);
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create("ItemTemplate", typeof(DataTemplate), typeof(HVScrollGridView), default(DataTemplate));
public ICommand SelectedCommand
{
get { return (ICommand)GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void ItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
{
var itemsLayout = (HVScrollGridView)bindable;
itemsLayout.SetItems();
}
public HVScrollGridView()
{
_scrollView = new ScrollView();
_itemsStackLayout = new StackLayout
{
BackgroundColor = BackgroundColor,
Padding = Padding,
Spacing = Spacing,
HorizontalOptions = LayoutOptions.FillAndExpand
};
_scrollView.BackgroundColor = BackgroundColor;
_scrollView.Content = _itemsStackLayout;
Children.Add(_scrollView);
}
protected virtual void SetItems()
{
_itemsStackLayout.Children.Clear();
_itemsStackLayout.Spacing = Spacing;
_innerSelectedCommand = new Command<View>(view =>
{
SelectedItem = view.BindingContext;
SelectedItem = null; // Allowing item second time selection
});
_itemsStackLayout.Orientation = ListOrientation;
_scrollView.Orientation = ListOrientation == StackOrientation.Horizontal
? ScrollOrientation.Horizontal
: ScrollOrientation.Vertical;
if (ItemsSource == null)
{
return;
}
foreach (var item in ItemsSource)
{
_itemsStackLayout.Children.Add(GetItemView(item));
}
_itemsStackLayout.BackgroundColor = BackgroundColor;
SelectedItem = null;
}
protected virtual View GetItemView(object item)
{
var content = ItemTemplate.CreateContent();
var view = content as View;
if (view == null)
{
return null;
}
view.BindingContext = item;
var gesture = new TapGestureRecognizer
{
Command = _innerSelectedCommand,
CommandParameter = view
};
AddGesture(view, gesture);
return view;
}
private void AddGesture(View view, TapGestureRecognizer gesture)
{
view.GestureRecognizers.Add(gesture);
var layout = view as Layout<View>;
if (layout == null)
{
return;
}
foreach (var child in layout.Children)
{
AddGesture(child, gesture);
}
}
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
{
var itemsView = (HVScrollGridView)bindable;
if (newValue == oldValue && newValue != null)
{
return;
}
itemsView.SelectedItemChanged?.Invoke(itemsView, EventArgs.Empty);
if (itemsView.SelectedCommand?.CanExecute(newValue) ?? false)
{
itemsView.SelectedCommand?.Execute(newValue);
}
}
public static explicit operator ListView(HVScrollGridView v)
{
throw new NotImplementedException();
}
}

autocomplete for TKCustomMap from CustomList

I took from a GitHub TkCustomMap project TK.CustomMap, and I want to call my custom service, to fill my custom list, and on ItemSelected event, from autocomplete searchBar to MapCenter it with Coordinates from my list model.
I've tried and I stopped at binding and on ItemSelect event.
Here is my MapPage:
using Xamarin.Forms;
using Xamarin.Forms.Maps;
namespace TK.CustomMap.Sample
{
public partial class SamplePage : ContentPage
{
public SamplePage()
{
//InitializeComponent();
this.CreateView();
this.BindingContext = new SampleViewModel();
}
private async void CreateView()
{
var autoComplete = new PlacesAutoComplete { ApiToUse = PlacesAutoComplete.PlacesApi.CustomList };
autoComplete.SetBinding(PlacesAutoComplete.MapSelectedCommandProperty, "MapCenter");
var newYork = new Position(40.7142700, -74.0059700);
var mapView = new TKCustomMap(MapSpan.FromCenterAndRadius(newYork, Distance.FromKilometers(2)));
mapView.IsShowingUser = true;
mapView.SetBinding(TKCustomMap.CustomPinsProperty, "Pins");
mapView.SetBinding(TKCustomMap.MapClickedCommandProperty, "MapClickedCommand");
mapView.SetBinding(TKCustomMap.MapLongPressCommandProperty, "MapLongPressCommand");
mapView.SetBinding(TKCustomMap.MapCenterProperty, "MapCenter");
mapView.SetBinding(TKCustomMap.PinSelectedCommandProperty, "PinSelectedCommand");
mapView.SetBinding(TKCustomMap.SelectedPinProperty, "SelectedPin");
mapView.SetBinding(TKCustomMap.RoutesProperty, "Routes");
mapView.SetBinding(TKCustomMap.PinDragEndCommandProperty, "DragEndCommand");
mapView.SetBinding(TKCustomMap.CirclesProperty, "Circles");
mapView.SetBinding(TKCustomMap.CalloutClickedCommandProperty, "CalloutClickedCommand");
mapView.SetBinding(TKCustomMap.PolylinesProperty, "Lines");
mapView.SetBinding(TKCustomMap.PolygonsProperty, "Polygons");
mapView.SetBinding(TKCustomMap.MapRegionProperty, "MapRegion");
mapView.SetBinding(TKCustomMap.RouteClickedCommandProperty, "RouteClickedCommand");
mapView.SetBinding(TKCustomMap.RouteCalculationFinishedCommandProperty, "RouteCalculationFinishedCommand");
mapView.SetBinding(TKCustomMap.TilesUrlOptionsProperty, "TilesUrlOptions");
mapView.SetBinding(TKCustomMap.MapFunctionsProperty, "MapFunctions");
mapView.IsRegionChangeAnimated = true;
autoComplete.SetBinding(PlacesAutoComplete.BoundsProperty, "MapRegion");
RelativeLayout _baseLayout = new RelativeLayout();
_baseLayout.Children.Add(
mapView,
Constraint.RelativeToView(autoComplete, (r, v) => v.X),
Constraint.RelativeToView(autoComplete, (r, v) => autoComplete.HeightOfSearchBar),
heightConstraint: Constraint.RelativeToParent((r) => r.Height - autoComplete.HeightOfSearchBar),
widthConstraint: Constraint.RelativeToView(autoComplete, (r, v) => v.Width));
_baseLayout.Children.Add(
autoComplete,
Constraint.Constant(0),
Constraint.Constant(0));
Content = _baseLayout;
}
}
}
Here is my PlacesAutoComplete class:
using System;
using System.Collections.Generic;
using System.Linq;
using TK.CustomMap.Api;
using TK.CustomMap.Api.Google;
using TK.CustomMap.Api.OSM;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using static TK.CustomMap.Sample.SearchBarModel;
namespace TK.CustomMap.Sample
{
public class SearchBarModel : IPlaceResult
{
public string Subtitle { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public class PlacesAutoComplete : RelativeLayout
{
public static BindableProperty BoundsProperty = BindableProperty.Create<PlacesAutoComplete, MapSpan>(
p => p.Bounds,
default(MapSpan));
// TODO: SUMMARIES
public enum PlacesApi
{
//Google,
//Osm,
//Native,
//THIS IS MY LIST!
CustomList
}
private readonly bool _useSearchBar;
private bool _textChangeItemSelected;
private SearchBar _searchBar;
private Entry _entry;
private ListView _autoCompleteListView;
private IEnumerable<IPlaceResult> _predictions;
public PlacesApi ApiToUse { get; set; }
public static readonly BindableProperty MapSelectedCommandProperty =
BindableProperty.Create<PlacesAutoComplete, Command<Position>>(
p => p.SetMap, null);
public List<SearchBarModel> myList = new List<SearchBarModel>();
public Command<Position> SetMap
{
get { return (Command<Position>)this.GetValue(MapSelectedCommandProperty); }
set { this.SetValue(MapSelectedCommandProperty, value); }
}
public double HeightOfSearchBar
{
get
{
return this._useSearchBar ? this._searchBar.Height : this._entry.Height;
}
}
private string SearchText
{
get
{
return this._useSearchBar ? this._searchBar.Text : this._entry.Text;
}
set
{
if (this._useSearchBar)
this._searchBar.Text = value;
else
this._entry.Text = value;
}
}
public new MapSpan Bounds
{
get { return (MapSpan)this.GetValue(BoundsProperty); }
set { this.SetValue(BoundsProperty, value); }
}
public PlacesAutoComplete(bool useSearchBar)
{
this._useSearchBar = useSearchBar;
this.Init();
}
public string Placeholder
{
get { return this._useSearchBar ? this._searchBar.Placeholder : this._entry.Placeholder; }
set
{
if (this._useSearchBar)
this._searchBar.Placeholder = value;
else
this._entry.Placeholder = value;
}
}
public PlacesAutoComplete()
{
this._useSearchBar = true;
this.Init();
}
private void Init()
{
OsmNominatim.Instance.CountryCodes.Add("de");
this._autoCompleteListView = new ListView
{
IsVisible = false,
RowHeight = 40,
HeightRequest = 0,
BackgroundColor = Color.White
};
this._autoCompleteListView.ItemTemplate = new DataTemplate(typeof(MapSearchCell));
View searchView;
if (this._useSearchBar)
{
this._searchBar = new SearchBar
{
Placeholder = "Search for address..."
};
this._searchBar.TextChanged += SearchTextChanged;
this._searchBar.SearchButtonPressed += SearchButtonPressed;
searchView = this._searchBar;
}
else
{
this._entry = new Entry
{
Placeholder = "Sarch for address"
};
this._entry.TextChanged += SearchTextChanged;
searchView = this._entry;
}
this.Children.Add(searchView,
Constraint.Constant(0),
Constraint.Constant(0),
widthConstraint: Constraint.RelativeToParent(l => l.Width));
this.Children.Add(
this._autoCompleteListView,
Constraint.Constant(0),
Constraint.RelativeToView(searchView, (r, v) => v.Y + v.Height));
this._autoCompleteListView.ItemSelected += ItemSelected;
this._textChangeItemSelected = false;
}
private void SearchButtonPressed(object sender, EventArgs e)
{
if (this._predictions != null && this._predictions.Any())
this.HandleItemSelected(this._predictions.First());
else
this.Reset();
}
private void SearchTextChanged(object sender, TextChangedEventArgs e)
{
if (this._textChangeItemSelected)
{
this._textChangeItemSelected = false;
return;
}
this.SearchPlaces();
}
private async void SearchPlaces()
{
try
{
if (string.IsNullOrEmpty(this.SearchText))
{
this._autoCompleteListView.ItemsSource = null;
this._autoCompleteListView.IsVisible = false;
this._autoCompleteListView.HeightRequest = 0;
return;
}
IEnumerable<IPlaceResult> result = null;
if (this.ApiToUse == PlacesApi.CustomList)
{
myList.Add(new SearchBarModel
{
Name = "Test1",
Description = "On item select, show me on map!",
Longitude = 20.4680701,
Latitude = 44.8152658
});
myList.Add(new SearchBarModel
{
Name = "Test2",
Description = "On item select, show me on map!",
Longitude = 20.4233035,
Latitude = 44.805651,
});
myList.Add(new SearchBarModel
{
Name = "Test3",
Description = "On item select, show me on map!",
Longitude = 20.456054,
Latitude = 44.8839925
});
myList.Add(new SearchBarModel
{
Name = "Test4",
Description = "On item select, show me on map!",
Longitude = 20.4328035,
Latitude = 44.8071928,
});
result = myList.OfType<IPlaceResult>().ToList<IPlaceResult>();
}
else
{
result = await OsmNominatim.Instance.GetPredictions(this.SearchText);
}
if (result != null && result.Any())
{
this._predictions = result;
this._autoCompleteListView.HeightRequest = result.Count() * 40;
this._autoCompleteListView.IsVisible = true;
this._autoCompleteListView.ItemsSource = this._predictions;
}
else
{
this._autoCompleteListView.HeightRequest = 0;
this._autoCompleteListView.IsVisible = false;
}
}
catch (Exception x)
{
// TODO
}
}
//From here my code partially works
private void ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) return;
var prediction = (IPlaceResult)e.SelectedItem;
this.HandleItemSelected(prediction);
}
private void HandleItemSelected(IPlaceResult prediction)
{
if (this.SetMap != null && this.SetMap.CanExecute(this))
{
this.SetMap.Execute(prediction);
}
this._textChangeItemSelected = true;
this.SearchText = prediction.Description;
this._autoCompleteListView.SelectedItem = null;
this.Reset();
}
private void Reset()
{
this._autoCompleteListView.ItemsSource = null;
this._autoCompleteListView.IsVisible = false;
this._autoCompleteListView.HeightRequest = 0;
if (this._useSearchBar)
this._searchBar.Unfocus();
else
this._entry.Unfocus();
}
}
}

xamarin design time binding cannot resolve property in data context

i'm currently in the process of modifying an ItemsView according to my needs. I noticed on flaw in my implementation however:
Unlike ListView i don't get intellisense according to my current iteration element. Does anyone know how to make that happen?
Here's my control implementation:
// http://adventuresinxamarinforms.com/2015/04/29/creating-a-xamarin-forms-accordion-control-without-custom-renders/
public class ItemsView : Grid
{
protected ScrollView ScrollView;
protected readonly StackLayout ItemsStackLayout;
public ItemsView()
{
ScrollView = new ScrollView();
ScrollView.SetBinding(ScrollOrientationProperty, new Binding(nameof(ScrollOrientation), mode: BindingMode.OneWay, source: this));
ItemsStackLayout = new StackLayout
{
Padding = new Thickness(0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand
};
ItemsStackLayout.SetBinding(StackOrientationProperty, new Binding(nameof(ItemsStackLayout), mode: BindingMode.OneWay, source: this));
ScrollView.Content = ItemsStackLayout;
Children.Add(ScrollView);
SelectedCommand = new Command<object>(item =>
{
var selectable = item as ISelectable;
if (selectable == null)
return;
SetSelected(selectable);
SelectedItem = selectable.IsSelected ? selectable : null;
});
}
protected virtual void SetSelected(ISelectable selectable)
{
selectable.IsSelected = true;
}
public bool ScrollToStartOnSelected { get; set; }
#region SelectedCommand
public static BindableProperty SelectedCommandProperty = BindableProperty.Create<ItemsView, ICommand>(d => d.SelectedCommand, default(ICommand));
public ICommand SelectedCommand
{
get { return (ICommand) GetValue(SelectedCommandProperty); }
set { SetValue(SelectedCommandProperty, value); }
}
#endregion SelectedCommand
#region ScrollOrientation
public static BindableProperty ScrollOrientationProperty = BindableProperty.Create<ItemsView, ScrollOrientation>(d => d.ScrollOrientation, ScrollOrientation.Vertical);
public ScrollOrientation ScrollOrientation
{
get { return (ScrollOrientation) GetValue(ScrollOrientationProperty); }
set { SetValue(ScrollOrientationProperty, value); }
}
#endregion ScrollOrientation
#region StackOrientation
public static BindableProperty StackOrientationProperty = BindableProperty.Create<ItemsView, StackOrientation>(d => d.StackOrientation, StackOrientation.Vertical);
public StackOrientation StackOrientation
{
get { return (StackOrientation) GetValue(StackOrientationProperty); }
set { SetValue(StackOrientationProperty, value); }
}
#endregion StackOrientation
public event EventHandler SelectedItemChanged;
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<ItemsView, IEnumerable>(p => p.ItemsSource, default(IEnumerable<object>), BindingMode.OneWay, null, ItemsSourceChanged);
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty SelectedItemProperty =
BindableProperty.Create<ItemsView, object>(p => p.SelectedItem, default(object), BindingMode.TwoWay, null, OnSelectedItemChanged);
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly BindableProperty ItemTemplateProperty =
BindableProperty.Create<ItemsView, DataTemplate>(p => p.ItemTemplate, default(DataTemplate));
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
private static void ItemsSourceChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue)
{
var itemsLayout = (ItemsView)bindable;
itemsLayout.SetItems();
var newObservableCasted = newValue as INotifyCollectionChanged;
var oldObservableCasted = oldValue as INotifyCollectionChanged;
if (newObservableCasted != null)
newObservableCasted.CollectionChanged += itemsLayout.ItemsSourceCollectionChanged;
if (oldObservableCasted != null)
oldObservableCasted.CollectionChanged -= itemsLayout.ItemsSourceCollectionChanged;
}
private void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
this.SetItems();
}
protected virtual void SetItems()
{
ItemsStackLayout.Children.Clear();
if (ItemsSource == null)
return;
foreach (var item in ItemsSource)
{
var itemView = GetItemView(item);
if (itemView == null)
{
ItemsStackLayout.Children.Add(new Label()
{
Text = "ItemTemplate missing."
});
break;
}
ItemsStackLayout.Children.Add(itemView);
}
SelectedItem = ItemsSource.OfType<ISelectable>().FirstOrDefault(x => x.IsSelected);
}
protected virtual View GetItemView(object item)
{
if (ItemTemplate == null)
return null;
ItemTemplate.SetValue(BindingContextProperty, item);
var content = ItemTemplate.CreateContent();
var view = content as View;
if (view == null)
return null;
var gesture = new TapGestureRecognizer
{
CommandParameter = item
};
gesture.SetBinding(TapGestureRecognizer.CommandProperty, (ItemsView v) => v.SelectedCommand, BindingMode.OneWay);
AddGesture(view, gesture);
return view;
}
protected void AddGesture(View view, TapGestureRecognizer gesture)
{
view.GestureRecognizers.Add(gesture);
var layout = view as Layout<View>;
if (layout == null)
return;
foreach (var child in layout.Children)
AddGesture(child, gesture);
}
private static void OnSelectedItemChanged(BindableObject bindable, object oldValue, object newValue)
{
var itemsView = (ItemsView)bindable;
if (newValue == oldValue)
return;
var selectable = newValue as ISelectable;
itemsView.SetSelectedItem(selectable ?? oldValue as ISelectable);
}
protected virtual void SetSelectedItem(ISelectable selectedItem)
{
var items = ItemsSource;
foreach (var item in items.OfType<ISelectable>())
item.IsSelected = selectedItem != null && item == selectedItem && selectedItem.IsSelected;
var handler = SelectedItemChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
my viewmodels:
public class InfoFieldsViewModel : HeaderedViewModel
{
public override Task NavigatedToAsync(object state)
{
base.NavigatedToAsync(state);
var casted = state as SiteSelectionEntry;
if (casted != null)
{
HeaderText = $" < {casted.Name}";
}
Groups.IsEventNotificationEnabled = false;
var group = new InfoFieldGroupViewModel("Gruppe 1");
group.Items.Add(new InfoFieldDetailViewModel("Label1"));
group.Items.Add(new InfoFieldDetailViewModel("Label2"));
group.Items.Add(new InfoFieldDetailViewModel("Label3"));
Groups.Add(group);
group = new InfoFieldGroupViewModel("Gruppe 2");
group.Items.Add(new InfoFieldDetailViewModel("Label4"));
group.Items.Add(new InfoFieldDetailViewModel("Label5"));
group.Items.Add(new InfoFieldDetailViewModel("Label6"));
Groups.Add(group);
Groups.IsEventNotificationEnabled = true;
Groups.RaiseUpdate();
return Done;
}
private ExtendedObservableCollection<InfoFieldGroupViewModel> _groups = new ExtendedObservableCollection<InfoFieldGroupViewModel>();
public ExtendedObservableCollection<InfoFieldGroupViewModel> Groups
{
get { return GetValue(ref _groups); }
set { SetValue(ref _groups, value); }
}
}
public class EditableViewModel : ViewModelBase
{
private bool _isInEditMode = new bool();
public bool IsInEditMode
{
get { return GetValue(ref _isInEditMode); }
set { SetValue(ref _isInEditMode, value); }
}
}
public class InfoFieldGroupViewModel : ViewModelBase
{
public InfoFieldGroupViewModel()
{
IsExpanded = true;
}
public InfoFieldGroupViewModel(string groupName)
{
_groupName = groupName;
}
private bool _isExpanded = new bool();
public bool IsExpanded
{
get { return GetValue(ref _isExpanded); }
set { SetValue(ref _isExpanded, value); }
}
private string _groupName;
public string GroupName
{
get { return _groupName; }
set { SetValue(ref _groupName, value); }
}
private ExtendedObservableCollection<InfoFieldDetailViewModel> _items = new ExtendedObservableCollection<InfoFieldDetailViewModel>();
public ExtendedObservableCollection<InfoFieldDetailViewModel> Items
{
get { return GetValue(ref _items); }
set { SetValue(ref _items, value); }
}
}
public class InfoFieldDetailViewModel : EditableViewModel
{
public InfoFieldDetailViewModel()
{
}
public InfoFieldDetailViewModel(string label)
{
_label = label;
}
private string _label;
public string Label
{
get { return _label; }
set { SetValue(ref _label, value); }
}
}
The view which uses the controls:
<Grid BackgroundColor="{x:Static resources:Colors.DefaultBackground}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<custom:ApplicationHeader
HeaderText="{Binding HeaderText}"
HeaderTapCommand="{Binding NavigatorBackCommand}"
HomeButtonCommand="{Binding NavigatorBackCommand}"/>
<Grid Row="1" custom:GridExtensions.IsBusy="{Binding IsBusy}">
<custom:ItemsView ItemsSource="{Binding Groups}">
<custom:ItemsView.ItemTemplate>
<DataTemplate>
<Label MinimumHeightRequest="30" Text="{Binding GroupName}"></Label>
</DataTemplate>
</custom:ItemsView.ItemTemplate>
</custom:ItemsView>
</Grid>
</Grid>
Screenshot of designtime error:
Oddly enough an ordinary xamarin.forms listview seems to have no trouble getting the design time correct here and mapping the child datacontext within the item template.
Is there some attribute i'm missing out on to make it work? Or am i doing something wrong in my implementation which makes this fail? The template itself renders just fine. So it's just the design time getting it wrong here.
Any ideas welcome. So far none of my binding context redirects worked successfully.
For me, it was a design-time DataType specification added on page level:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:**;assembly=**"
xmlns:bindingConverters="clr-namespace:**;assembly=**"
x:DataType="viewModels:WelcomeViewModel" <!-- HERE-->
x:Class="**.WelcomePage"
Title="{Binding Title}">
<ContentPage.Content >
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Name}"/> <!-- this binding was looking for property 'Name' on root level, which is 'WelcomeViewModel' -->
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage.Content>
</ContentPage>
So, I've just removed x:DataType="viewModels:WelcomeViewModel" and it started working.

Resources