I am trying to output a value from my object to two different entries. Both entries are on the same view but in different ContentPages as follows:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.Views.ViewTabs.ViewHome"
xmlns:localTabs="clr-namespace:myApp.Views.ViewTabs"
xmlns:localObjPages="clr-namespace:myApp.Objects"
>
<ContentPage Title="PageOne">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryOne" Text="{Binding BananaCount}"/>
<Entry
x:Name="EntryTwo" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ContentPage.BindingContext>
<localObjPages:PagesObj/>
</ContentPage.BindingContext>
<ScrollView>
<StackLayout>
<Entry
x:Name="EntryThree" Text="{Binding BananaCount}"/>
</StackLayout>
</ScrollView>
</ContentPage>
</TabbedPage>
My Model:
public string BananaCount
{
get { return _bananaCount; }
set
{
if (_bananaCount != value)
{
_bananaCount = value;
NotifyPropertyChanged("BananaCount");
}
}
}
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly? Thank you.
The object is updated and returned in EntryOne or in EntryTwo when I change it either in EntryOne or in EntryTwo. However, it is not updated in EntryThree. Why is this? Am I Binding this correctly?
Do one sample about TabbedPage, assign datasource for TabbedPage, not contentpage, that you can take a look:
<TabbedPage
x:Class="FormsSample.tabbedpage.TabbedPage6"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:FormsSample.tabbedpage">
<!--Pages can be added as references or inline-->
<ContentPage Title="PageOne">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryOne" Text="{Binding str}" />
<Entry x:Name="EntryTwo" Text="{Binding str}" />
<Button
x:Name="btn1"
Clicked="btn1_Clicked"
Text="change data" />
</StackLayout>
</ScrollView>
</ContentPage>
<ContentPage Title="PageTwo">
<ScrollView>
<StackLayout>
<Entry x:Name="EntryThree" Text="{Binding str}" />
</StackLayout>
</ScrollView>
</ContentPage>
public partial class TabbedPage6 : TabbedPage
{
public tabclass tabc { get; set; }
public TabbedPage6()
{
InitializeComponent();
tabc = new tabclass();
this.BindingContext = tabc;
}
private void btn1_Clicked(object sender, EventArgs e)
{
tabc.str = "this is test!";
}
}
public class tabclass:ViewModelBase
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
}
The ViewModel is one class that implement INotifyPropertyChanged.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
Trying to fire a command inside a stacklayout with itemssource. I wonder why the NavigateToProductListViewShopTappedCommand is not getting fired.
Tried multiple command approaches:
1)
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
None are not working
Code:
<?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:controls="clr-namespace:BoerPlaza.Controls.Shop"
xmlns:local="clr-namespace:BoerPlaza.ViewModels"
xmlns:behaviors="clr-namespace:BoerPlaza.Behaviors"
x:Class="BoerPlaza.Views.Shop.MyShopsPage"
x:Name="Page"
Title="Mijn winkels">
<ContentPage.Content>
<ScrollView>
<StackLayout Margin="{StaticResource margin-side-std}"
Padding="{StaticResource padding-top-bottom-std}"
BindableLayout.ItemsSource="{Binding Shops}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:ShopCardTemplateView Shop="{Binding .}"
ControlTemplate="{StaticResource ShopCardTemplateView}">
<controls:ShopCardTemplateView.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:MyShopsListViewModel}}, Path=NavigateToProductListViewShopTappedCommand}"
CommandParameter="{Binding .}">
<!--Command="{Binding BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"-->
</TapGestureRecognizer>
</controls:ShopCardTemplateView.GestureRecognizers>
</controls:ShopCardTemplateView>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Code behind
public partial class MyShopsPage : ContentPage
{
private readonly MyShopsListViewModel _viewModel;
public MyShopsPage()
{
InitializeComponent();
BindingContext = _viewModel = new MyShopsListViewModel(App.ShopDataStore, App.DialogService);
_viewModel.LoadShopsOnUserIdCommand.Execute("B22698B8-42A2-4115-9631-1C2D1E2AC5F7");
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
ViewModel:
[QueryProperty(nameof(UserId), nameof(UserId))]
public class MyShopsListViewModel : BaseViewModel
{
private string _userId;
private ObservableCollection<ShopDbViewModel> _shops;
private readonly IShopDataStore _shopDataStore;
private readonly IDialogService _dialogService;
public ObservableCollection<ShopDbViewModel> Shops
{
get
{
return _shops;
}
set
{
_shops = value;
OnPropertyChanged(nameof(Shops));
}
}
public void OnAppearing()
{
IsBusy = true;
}
public ICommand LoadShopsOnUserIdCommand { get; set; }
public ICommand NavigateToProductListViewShopTappedCommand { get; set; }
public MyShopsListViewModel(IShopDataStore shopDataStore, IDialogService dialogService)
{
this._shopDataStore = shopDataStore;
this._dialogService = dialogService;
Shops = new ObservableCollection<ShopDbViewModel>();
LoadShopsOnUserIdCommand = new Command<string>(async (string userId) => await ExecuteLoadShopsOnUserId(userId));
NavigateToProductListViewShopTappedCommand = new Command<ShopDbViewModel>(async (ShopDbViewModel shop) => await ExecuteNavigateToProductListViewShopTappedCommandAsync(shop));
}
private async Task ExecuteNavigateToProductListViewShopTappedCommandAsync(ShopDbViewModel shop)
{
if (shop == null)
return;
await Shell.Current.GoToAsync($"{nameof(ProductsPage)}?{nameof(MyProductsListViewModel.ShopId)}={shop.Id}");
}
public string UserId
{
get
{
return _userId;
}
set
{
_userId = value;
LoadShopsOnUserIdCommand.Execute(value);
}
}
private async Task ExecuteLoadShopsOnUserId(string userId)
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
try
{
Shops.Clear();
var shops = await _shopDataStore.GetShopOnUserIdAsync(userId);
foreach(var shop in shops)
{
Shops.Add(shop);
}
}
catch (Exception ex)
{
await _dialogService.ShowDialog(ex.Message, "An error has occurred", "OK");
}
finally
{
IsBusy = false;
}
}
else
{
await _dialogService.ShowDialog("No active internet connection", "Connection error", "OK");
IsBusy = false;
}
}
}
If you define the ICommand in the ViewModel directly , you could set the binding path like following
Command="{Binding Path=BindingContext.NavigateToProductListViewShopTappedCommand, Source={x:Reference Page}}"
I've found the problem
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ffimage="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:customcontrols="clr-namespace:BoerPlaza.Controls"
x:Class="BoerPlaza.Controls.Shop.ShopCardTemplateView">
<ContentView.Resources>
<ControlTemplate x:Key="ShopCardTemplateView">
<!-- Card Header -->
<!-- for displaying products and categories on homepage -->
<StackLayout Spacing="1"
HorizontalOptions="FillAndExpand"
Margin="{StaticResource margin-card}">
<!-- On click - shows the product detail view page -->
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" />
</StackLayout.GestureRecognizers>
<!-- Image frame -->
<Frame BackgroundColor="{StaticResource image-box-color}"
CornerRadius="0"
HasShadow="False"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
HeightRequest="100">
<!-- Product Image -->
....
As you can see this is the control template I'm using for the MyShopsPage. This is a shop card. Inside this shop card I already had an StackLayout.GestureRecognizers. Somehow when I was clicking on the control template, I was actually clicking on this.
I always thought everything flows from top to bottom in events, but this seems different. Something that is on top on something else does not mean anything in xaml.
I have a resuable control like this to display a loading spinner:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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"
mc:Ignorable="d"
x:Class="Framework.Controls.Loading" x:Name="LoadingControl" IsVisible="{Binding LoadingIndicator}"
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<ContentView.Content>
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" Color="DarkBlue"
IsVisible="{Binding LoadingIndicator}"
IsRunning="{Binding LoadingIndicator}">
</ActivityIndicator>
</ContentView.Content>
</ContentView>
I am trying to consume it on a page like this:
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
However, the loading spinner fails to appear on-screen.
When I set the LoadingIndicator property to true, it appears just fine:
<controls:Loading LoadingIndicator="true"></controls:Loading>
My 'IsLoading' binding is definitely working properly, because if I place the following code directly in my XAML page it also works fine:
<ActivityIndicator HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
Color="DarkBlue" IsVisible="{Binding IsLoading}" IsRunning="{Binding IsLoading}">
</ActivityIndicator>
Therefore, what is it about this that's wrong?
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
The 'IsLoading' property gets set on each of my pages from my view model. Here is a snippet from the view model:
public ICommand OnSave => new Command(async () =>
{
IsLoading = true;
await CreateItem();
IsLoading = false;
});
The code-behind for my control looks like this:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(
propertyName: nameof(LoadingIndicator), typeof(bool),
typeof(Loading), default(string), BindingMode.OneWayToSource);
public bool LoadingIndicator
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public Loading()
{
InitializeComponent();
BindingContext = this;
}
}
Do I need to write code to handle the change if the IsLoading binding gets updated?
This is the full code for the page where I am using the control:
ItemCreatePage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage Title="{Binding PageTitle}"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:userControls="clr-namespace:Framework.UserControls"
xmlns:converters="clr-namespace:Framework.ValueConverters"
xmlns:controls="clr-namespace:Framework.Controls;assembly=Framework.Android"
x:Class="Framework.Views.Item.ItemCreatePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:DoubleConverter x:Key="DoubleConverter"></converters:DoubleConverter>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<Grid>
<ScrollView>
<Grid RowSpacing="0" VerticalOptions="Start">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackLayout Grid.Row="1" Padding="20,20,20,0" VerticalOptions="Start">
<Label Text="Category" />
<userControls:BindablePicker
ItemsSource="{Binding Categories}"
SelectedItem="{Binding Path=Item.CategoryName, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.CategoryId, Mode=TwoWay}"/>
<Label Text="Description" />
<Editor Text="{Binding Item.Description}" HeightRequest="100"/>
<Label Text="Area"/>
<Entry Text="{Binding Item.LineNumber}"/>
<Label Text="Identifier"/>
<Entry Text="{Binding Item.Identifier}"/>
<Label Text="Code"/>
<Entry Text="{Binding Item.Code}"/>
<Label Text="Priority" />
<userControls:BindablePicker
ItemsSource="{Binding Priorities}"
SelectedItem="{Binding Path=Item.ItemPriority, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.ItemPriorityCode, Mode=TwoWay}"/>
<Label Text="Owner" />
<userControls:BindablePicker
ItemsSource="{Binding Users}"
SelectedItem="{Binding Path=Item.OwnerName, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.OwnerId, Mode=TwoWay}"/>
<Label Text="Due Date" />
<DatePicker Date="{Binding Item.DateDue}" />
<Label Text="Date Identified" />
<DatePicker Date="{Binding Item.DateIdentified}" />
<Label Text="Status" />
<userControls:BindablePicker
ItemsSource="{Binding Statuses}"
SelectedItem="{Binding Path=Item.Status, Mode=OneWay}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding Path=Item.StatusCode, Mode=TwoWay}"/>
<Label Text="Comment" />
<Editor Text="{Binding Item.Comment}" HeightRequest="100"/>
<Label Text="IOM" />
<Entry Text="{Binding Item.OutcomeMeasurementInitial, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="FOM" />
<Entry Text="{Binding Item.OutcomeMeasurementFinal, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="Longitude" />
<Entry Text="{Binding Item.Longitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Label Text="Latitude" />
<Entry Text="{Binding Item.Latitude, Mode=TwoWay, Converter={StaticResource DoubleConverter}}" Keyboard="Numeric" />
<Button Margin="0,20,0,20" Command="{Binding OnSave}" BackgroundColor="{StaticResource Primary}"
BorderRadius="2" Text="Save" VerticalOptions="End" TextColor="White" ></Button>
</StackLayout>
</Grid>
</ScrollView>
<controls:Loading LoadingIndicator="{Binding IsLoading}"></controls:Loading>
</Grid>
</ContentPage.Content>
</ContentPage>
ItemCreatePage.xaml.cs
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ItemCreatePage : ContentPage
{
public ItemCreatePage ()
{
InitializeComponent ();
}
protected override async void OnAppearing()
{
var vm = BindingContext as ItemCreateViewModel;
vm.Item = new Data.Entities.Item();
await vm?.GetDeviceLocation();
base.OnAppearing();
}
}
The view model code:
public class ItemCreateViewModel : FormViewModel<Data.Entities.Item>
{
public async Task GetDeviceLocation()
{
this.Item = await this.Item.AddDeviceLocation();
OnPropertyChanged(nameof(this.Item));
}
public ILookupService LookupService { get; set; }
public IItemService ItemService { get; set; }
#region selectLists
public List<EnumListItem<ItemPriority>> Priorities => EnumExtensions.ToEnumList<ItemPriority>();
public List<EnumListItem<ItemStatus>> Statuses => EnumExtensions.ToEnumList<ItemStatus>();
public string PageTitle => $"{PageTitles.ItemCreate}{this.OfflineStatus}";
public List<Data.Entities.User> Users => UserService.GetAll(this.Offline);
public List<Data.Entities.Lookup> Categories => LookupService.GetLookups(this.Offline, LookupTypeCode.ItemCategories);
#endregion
public Data.Entities.Item Item { get; set; }
public ICommand OnSave => new Command(async () =>
{
await Loading(CreateItem);
});
private async Task CreateItem()
{
// ... Save logic is here
}
FormViewModel:
public class FormViewModel<T> : BaseViewModel
{
public IValidator<T> Validator => Resolve<IValidator<T>>();
public bool IsLoading { get; set; }
/// <summary>
/// Render a loading spinner whilst we process a request
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
public async Task Loading(Func<Task> method)
{
IsLoading = true;
await method.Invoke();
IsLoading = false;
}
}
BaseViewModel:
public class BaseViewModel : IViewModelBase
{
public BaseViewModel()
{
if (this.GetCurrentUserToken() != null && !UserService.IsActive())
{
SettingService.ClearToken();
Bootstrapper.MasterDetailPage.IsPresented = false;
Application.Current.MainPage = new LoginPage();
}
}
public T Resolve<T>() => AutofacBootstrapper.Container.Resolve<T>();
public IUserService UserService => Resolve<IUserService>();
public INavigator Navigator => AutofacBootstrapper.Navigator;
public IDisplayAlertFactory DisplayAlert { get; set; }
public INavigation MasterNavigation => Bootstrapper.MasterDetailPage?.Detail?.Navigation;
public bool Offline => SettingService.GetSetting<bool>(CacheProperties.Offline);
public string OfflineStatus => this.Offline ? " - Offline" : string.Empty;
public Token GetCurrentUserToken() => SettingService.GetToken() ?? null;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
You don't need to set your custom control's BindingContext here:
public Loading()
{
InitializeComponent();
BindingContext = this;//It's wrong!
//because the custom control's BindingContext will
//automatically be set to the BindingContext of
//the page where it's used which is what we usually want.
}
Here is a way to achieve what you want:
Your Custom Control's XAML:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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"
mc:Ignorable="d"
x:Class="Framework.Controls.Loading" x:Name="LoadingControl"
HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<ContentView.Content>
<ActivityIndicator x:Name="TheIndicator" HorizontalOptions="CenterAndExpand"
VerticalOptions="CenterAndExpand" Color="DarkBlue"/>
</ContentView.Content>
</ContentView>
And here is its code-behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Loading : ContentView
{
public static readonly BindableProperty LoadingIndicatorProperty =
BindableProperty.Create(propertyName:nameof(LoadingIndicator),
returnType: typeof(bool), declaringType: typeof(Loading), defaultValue: default(bool),
defaultBindingMode:BindingMode.Default, propertyChanged:LoadingBindingChanged);
private static void LoadingBindingChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var view = (Loading)(bindable);
view.SetLoadingVisibility((bool)newvalue);
}
public Loading()
{
InitializeComponent();
IsVisible = false; // we do this because by default a view' IsVisible property is true
}
public bool LoadingIndicator
{
get => (bool)GetValue(LoadingIndicatorProperty);
set => SetValue(LoadingIndicatorProperty, value);
}
public void SetLoadingVisibility(bool show)
{
IsVisible = show;
TheIndicator.IsVisible = show;
TheIndicator.IsRunning = show;
}
}
You are not invoking PropertyChanged event when you change IsLoading property. If you want UI to refresh you need to invoke this event for the chosen property.
Change implementation of IsLoading property to:
private bool _isLoading;
public bool IsLoading
{
get=> _isLoading;
set
{
_isLoading=value;
OnPropertyChanged(nameof(IsLoading));
}
}
and it should work
I've been working on a school assignment, where you need to load data into a ListView. According to the course manual, you need to use 'ObservableCollection' which I figured out. However, I can't seem to get the ItemTapped to work.
I'm creating the following table using SQLite.
public class Settings
{
[Primarykey]
public string Name { get; set; }
[MaxLength(255)]
public string Value { get; set; }
}
Then on the OnAppearing I initialise the database and add a row of data.
public partial class SQL : ContentPage
{
private SQLiteAsyncConnection _connection;
private ObservableCollection<Settings> _settings;
public SQL()
{
InitializeComponent();
_connection = DependencyService.Get<ISQLiteDb>().GetConnection();
}
protected override async void OnAppearing()
{
await _connection.CreateTableAsync<Settings>();
var settings_name = new Settings { Name = "Meaning of Life", Value = "42" };
await _connection.InsertAsync(settings_name);
await DisplayAlert("Alert", "Value Added to database!", "OK");
var settings = await _connection.Table<Settings>().ToListAsync();
_settings = new ObservableCollection<Settings>(settings);
ListView.ItemsSource = _settings;
base.OnAppearing();
}
void MyItemTapped (object sender, System.EventArgs e)
{
DisplayAlert("Alert", "You Pressed Something!", "OK");
}
void MyItemSelected (object sender, System.EventArgs e)
{
DisplayAlert("Alert", "You Selected Something!", "OK");
}
}
In my XAML file, I have the following, with the ItemTapped going to the function above.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView">
<ListView.ItemTemplate ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
I can't figure out what I'm doing wrong. Why doesn't my ItemTapped and ItemSelected work? Also, what would be the best way to access the Value associated with the Name I pressed in the ListView from the ObservableCollection
The reason your ItemTapped and ItemSelected aren't working is because they are in the wrong location.
This is what you have:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView">
<ListView.ItemTemplate ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
This is what you should do:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="BudgetBuddy.SQL">
<ContentPage.Content>
<ListView x:Name="ListView" ItemTapped="MyItemTapped" ItemSelected="MyItemSelected">
<ListView.ItemTemplate >
<DataTemplate>
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
I would like to bind an Entry with a Slider and vice versa. I wrote something like this:
<Entry x:Name="myEntry" Text="{Binding Value, Mode=TwoWay}" BindingContext="{x:Reference slider}"/>
<Slider x:Name="slider" Maximum="100" Minimum="0" BindingContext="{x:Reference myEntry}"/>
When I use the slider, the value in the entry is updated, but when I put manually some value in the Entry, the value append a 0 or change to 0. What can be the problem. I am working on android.
You should bind both your Slider and Entry to string/ integer in a backing View Model.
class MyViewModel
{
private int _sliderValue;
public string EntryText
{
get => _sliderValue.ToString();
set => SetProperty(ref _sliderValue, int.Parse(value) );
}
public int SliderValue
{
get => _sliderValue;
set => (ref _sliderValue, value);
}
}
And in the view
<Entry Text="{Binding EntryText}" />
<Slider Value="{Binding SliderValue}" />
More MVVM Info
Fresh MVVM for Xamarin
Caliburn Micro for Xamarin
please refer the following xaml code
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry Text="{Binding Path=Value}"
FontSize="18"
x:Name="label"
BindingContext="{x:Reference Name=slider}"/>
<Slider x:Name="slider"
Maximum="1500"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</Frame>
The same can be achieved by using the view model please refer the code
Xaml
<Frame HorizontalOptions="FillAndExpand" VerticalOptions="StartAndExpand">
<StackLayout>
<Entry x:Name="NameEntry" Placeholder="Enter Name" Text="{Binding Forename,Mode=TwoWay}" />
<Slider Value="{Binding Forename}" Minimum="0" Maximum="10"/>
</StackLayout>
</Frame>
------------------- now see the model in a c# file-----------------------------------
public partial class MainPage : ContentPage
{
public class DetailsViewModel : INotifyPropertyChanged
{
int forename;
public int Forename
{
get
{
return forename;
}
set
{
if (forename != value)
{
forename = value;
OnPropertyChanged ("Forename");
}
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var changed = PropertyChanged;
if (changed != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public MainPage()
{
InitializeComponent();
BindingContext = new DetailsViewModel();
}
}
I am having trouble getting bindings defined in a ControlTemplate to work against my model.
Notice in the below ControlTemplate, I am using a TemplateBinding to bind to a property called Count (olive label). I am using Parent.Count as prescribed by this article, but neither values of Parent.Count nor Count are working.
The following page uses the ControlTemplate. Just to prove my ViewModel works I have a gray Label bound to the Count property as well.
Notice the resulting screen. The gray label is showing the Count property. The olive label from the ControlTemplate is not showing anything.
How can I make the Label in the ControlTemplate show the Count property from the ViewModel?
VIEW MODEL
namespace SimpleApp
{
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
_count = 10;
Uptick = new Command(() => { Count++; });
}
private int _count;
public int Count
{
get { return _count; }
set
{
_count = value;
OnPropertyChanged("Count");
}
}
public ICommand Uptick { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
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:local="clr-namespace:SimpleApp"
x:Class="SimpleApp.MainPage"
ControlTemplate="{StaticResource ParentPage}">
<StackLayout>
<Button Command="{Binding Uptick}" Text="Increment Count" />
<Label Text="{Binding Count}" BackgroundColor="Gray" />
</StackLayout>
</ContentPage>
CODE BEHIND
Notice the BindingContext is set to MainViewModel here. I need to use my own ViewModel and not the code behind.
namespace SimpleApp
{
public partial class MainPage : ContentPage
{
public MainPage()
{
BindingContext = new MainViewModel();
InitializeComponent();
}
}
}
CONTROL TEMPLATE
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SimpleApp.App">
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="ParentPage">
<StackLayout>
<Label Text="{TemplateBinding Parent.Count}" BackgroundColor="Olive" />
<ContentPresenter />
</StackLayout>
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
On your ControlTemplate please use the following code:
<Label Text="{TemplateBinding BindingContext.Count}" BackgroundColor="Olive" />
It seems that BindingContext is not being automatically applied to your ContentPage child, maybe it could be a bug in Xamarin.