Items in collection view not displaying with MVVM while link is showing - c#

I need help trying to figure out why my collection-view is not displaying the data that its binded to. When I run the application in debug mode the data is being populated into the Viewmodel and binded. When I go to the View.xaml and hover over the source where its binded, it displays.
I have provided the Model, ModelView, View and the code behind for the View and even a screen shot of the view running in the debugger showing that the bind seems to be working.
I have been stuck for a while any help will be truly appreciated.
What I see when I run in debug mode showing the view model is binded but just not showing.
ContactsPage.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"
mc:Ignorable="d"
x:Class="Project_contact.Views.ItemsPage"
Title="{Binding Title}"
x:Name="BrowseItemsPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadDataCommand}">
<StackLayout>
<Label x:Name="TopBanner" Text="Welcome Please wait..." />
<StackLayout Orientation="Horizontal">
<Label Text= "{Binding StringFormat='Welcome You have' }" />
</StackLayout>
<CollectionView x:Name="ItemsCollectionView2"
ItemsSource="{Binding Contacts}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10">
<Label Text="{Binding name}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<Label Text="{Binding desc}"
d:Text="Item descripton"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="OnContactSelected_Tapped"></TapGestureRecognizer>
<SwipeGestureRecognizer Direction="Left" />
</StackLayout.GestureRecognizers>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</RefreshView>
</ContentPage>
ContactsPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Project_Contact.Models;
using Project_Contact.Views;
using Project_Contact.ViewModels;
using Project_Contact.Services;
using System.Data;
namespace Project_Contact.Views
{
// Learn more about making custom code visible in the Xamarin.Forms previewer
// by visiting https://aka.ms/xamarinforms-previewer
[DesignTimeVisible(false)]
public partial class ItemsPage : ContentPage
{
public ContactsViewModel viewModel { get; set; }
public ContactStore contactStore { get; set; }
public ContactsPage()
{
contactStore = new ContactStore(DependencyService.Get<Database>());
viewModel = new ContactsViewModel(contactStore);
viewModel.LoadDataCommand.Execute(null);
BindingContext = viewModel;
InitializeComponent();
}
async void OnItemSelected(object sender, EventArgs args)
{
var layout = (BindableObject)sender;
var item = (Item)layout.BindingContext;
await Navigation.PushAsync(new ItemDetailPage(new ItemDetailViewModel(item)));
}
async void AddItem_Clicked(object sender, EventArgs e)
{
await Navigation.PushModalAsync(new NavigationPage(new NewContactPage(contactStore)));
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.LoadDataCommand.Execute(true);
}
async void OnContactSelected_Tapped(object sender, EventArgs e)
{
var layout = (BindableObject)sender;
var contact = (Contact)layout.BindingContext;
await Navigation.PushAsync(new ContactDetailPage(new ContactDetailViewModel(contactStore,contact)));
}
}
}
ContactsPageViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;
using Project_contact.Models;
using Project_contact.Views;
using Project_contact.Services;
namespace Project_contact.ViewModels
{
public class ContactsViewModel : BaseViewModel
{
public ObservableCollection<Contact> Contacts { get; set; } = new ObservableCollection<Contact>();
public Command LoadContacts { get; private set; }
public Command LoadDataCommand { get; private set; }
// public Command load
public ContactStore contactStore;
public int numberofContacts { get; set; }
public string TopBannerText { get; set; }
public ContactsViewModel(ContactStore contactStore)
{
Title = "Browse";
this.contactStore = contactStore;
LoadDataCommand = new Command(async () => await ExecuteLoadDataCommand());
}
public async Task ExecuteLoadDataCommand()
{
Contacts = new ObservableCollection<Contact>(await contactStore.GetContactsAsync());
LoadContacts = new Command(async () => await ExecuteLoadContactsCommand());
TopBannerText = String.Format("Welcome you have {0} contacts ",numberofContacts);
}
async Task ExecuteLoadContactsCommand()
{
if (!IsBusy)
{
IsBusy = true;
try
{
if (Contacts.Count > 0)
{
Contacts.Clear();
numberofContacts = 0;
}
var contacts = await contactStore.GetContactsAsync();
foreach (var contact in contacts)
{
Contacts.Add(contact);
numberofContacts++;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
}
}
Contact.cs
using System;
using System.Collections.Generic;
using System.Text;
using SQLite;
namespace Project_Contact.Models
{
public class Contact
{
[PrimaryKey, AutoIncrement]
public int id { get; set; }
public string name { get; set; }
public string desc { get; set; }
public string number {get; set;}
}
}

Related

Re-render the screen when an object changes

I would like to slightly modify the code generated when creating a maui project to implement the following
add an object to Meetings in MainPage.xaml.cs when the button is clicked
display the contents of that Meetings
I wrote the following code for this purpose, but there is no change in the output content. One possible reason for this is that adding data to the object does not re-render the screen. How can I solve this problem?
Views/MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App.Views"
x:Class="App.Views.MainPage">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="Click me"
SemanticProperties.Hint="Counts the number of times you click"
Clicked="OnCounterClicked"
<ListView ItemsSource="{Binding Meetings}" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
Views/MainPage.xaml.cs
namespace App.Views;
using App.Models;
public partial class MainPage : ContentPage
{
int count = 0;
public MainPage()
{
InitializeComponent();
BindingContext = new Models.AllMeetings();
}
private void OnCounterClicked(object sender, EventArgs e)
{
count++;
if (count == 1)
CounterBtn.Text = $"Clicked {count} time";
else
CounterBtn.Text = $"Clicked {count} times";
SemanticScreenReader.Announce(CounterBtn.Text);
((Models.AllMeetings)BindingContext).Meetings.Add(new Models.Meeting() { Name = "foo" });
}
}
Modes/AllMeetings
namespace App.Models;
internal class AllMeetings
{
public List<Meeting> Meetings { get; set; }
}
Models/Meetings.cs
namespace App.Models;
internal class Meeting
{
public string Name { get; set; }
}
Updates
Models/AllMeetings.cs
using System.Collections.ObjectModel;
namespace ailia_speech_gui.Models;
internal class AllMeetings
{
public ObservableCollection<Meeting> Meetings { get; set; }
public void Add_Meeting(Meeting meeting)
{
this.Meetings.Add(meeting);
}
}
I made a demo on my side. You can refer to my demo to change your project.
Here is the code in my Model named Products.cs:
namespace ListViewDelete.Models
{
public class Products
{
public string Name
{
get; set;
}
public double Price
{
get; set;
}
}
}
Then you need to create a viewmodel to realize the delete and add method and create the ObservableCollection to load the data.
Here is the code in my ViewModel:
namespace ListViewDelete.ViewModels
{
internal class ProductsViewModels
{
public ObservableCollection<Products> Products
{
get; set;
}
public Command<Products> RemoveCommand
{
get
{
return new Command<Products>((Product) => {
Products.Remove(Product);
});
}
}
public Command<Products> AddCommand
{
get
{
return new Command<Products>((Product) => {
Products.Add(Product);
});
}
}
public ProductsViewModels()
{
Products = new ObservableCollection<Products> {
new Products {
Name = "name1",
Price = 100
},
new Products {
Name = "name2",
Price = 100
},
new Products {
Name = "name3",
Price = 100
}
};
}
}
}
Last, you need to create the ListView or the CollectionView in the MainPage.xaml. Here is the code in the MainPage.xaml:
<StackLayout>
<Button Text="add" Clicked="Button_Clicked"></Button>
<CollectionView ItemsSource="{Binding Products}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Name}" />
<Label Text="{Binding Price}" />
<Button Text="Remove" Clicked="Remove_Clicked" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is the code in MainPage.xaml.cs:
namespace ListViewDelete
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
// bind the viewmodel to the Mainpage
BindingContext = new ProductsViewModels();
}
//delete the item from the observablecollection
public void Remove_Clicked(object sender, EventArgs e)
{
var button = sender as Button;
var product = button.BindingContext as Products;
var vm = BindingContext as ProductsViewModels;
vm.RemoveCommand.Execute(product);
}
//add the new item to the observablecollection
private void Button_Clicked(object sender, EventArgs e)
{
var product = new Products()
{
Name =" new name",
Price = 100
};
var vm = BindingContext as ProductsViewModels;
vm.AddCommand.Execute(product);
}
}
}
Meeting collection must be somewhere initialized before calling any operation on collestion (be it on property level or in constructor):
public class AllMeetings
{
public ObservableCollection<Meeting> Meetings { get; } = new ObservableCollection<Meeting>();
public void Add_Meeting(Meeting meeting)
{
this.Meetings.Add(meeting);
}
}
And ListView must have some data template to tell UI how data should be presented:
<ListView ItemsSource="{Binding Meetings}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}"/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

How to capture data from a view, perform a calculation, and return the result back to the view?

UPDATE 1:
The HoldingsValue calculation works if I use a local HoldingsAmount value. How do I get it to take the value from the xaml HoldingsAmount Entry?
View: TickerView.xaml
Added binding to the text property of the entry control:
<Entry Placeholder="Enter holdings"
Text="{Binding Ticker.HoldingsAmount}"
Keyboard="Numeric" />
Models: Tickers.cs
Added HoldingsAmount to the model:
namespace MVVMDemo2.MVVM.Models
{
public class Ticker
{
public decimal HoldingsAmount { get; set; }
public string AskPrice { get; set; }
public string BidPrice { get; set; }
public string HoldingsValue { get; set; }
}
}
ViewModels: TickerViewModel.cs
using Binance.Net.Clients;
using Binance.Net.Objects;
using MVVMDemo2.MVVM.Models;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMDemo2.MVVM.ViewModels
{
[AddINotifyPropertyChangedInterface]
public class TickerViewModel
{
public Ticker Ticker { get; set; }
//public decimal HoldingsAmount { get; set; }
public TickerViewModel()
{
var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions { });
socketClient.SpotStreams.SubscribeToBookTickerUpdatesAsync("BTCUSDT", data => {
decimal HoldingsAmount = 2;
Ticker = new Ticker
{
AskPrice = $"${data.Data.BestAskPrice.ToString("N2")}",
BidPrice = $"${data.Data.BestBidPrice.ToString("N2")}",
HoldingsValue = (HoldingsAmount * data.Data.BestBidPrice).ToString()
};
});
socketClient.UnsubscribeAllAsync();
}
}
}
ORIGINAL:
I'm trying to learn MAUI and MVVM, but I can't seem to read a value from the XAML form to use with data from a WebSocket.
I currently have the following which works:
Views: TickerView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MVVMDemo2.MVVM.Views.TickerView"
Title="TickerView">
<VerticalStackLayout
VerticalOptions="Center"
HorizontalOptions="Center">
<Label
Text="Holdings"
FontSize="25" />
<Entry Placeholder="Enter holdings"
BackgroundColor="Gray"
Keyboard="Numeric" />
<Label
Text="Ask price"
FontSize="25" />
<Label
Text="{Binding Ticker.AskPrice}"
FontSize="50" />
<Label
Text="Bid price"
FontSize="25" />
<Label
Text="{Binding Ticker.BidPrice}"
FontSize="50" />
<Label
Text="Holdings value"
FontSize="25" />
<Label
Text="{Binding Ticker.HoldingsValue}"
FontSize="50" />
</VerticalStackLayout>
</ContentPage>
Views: TickerView.xaml.cs
using MVVMDemo2.MVVM.Models;
using MVVMDemo2.MVVM.ViewModels;
namespace MVVMDemo2.MVVM.Views;
public partial class TickerView : ContentPage
{
public TickerView()
{
InitializeComponent();
BindingContext = new TickerViewModel();
}
}
Models: Ticker.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMDemo2.MVVM.Models
{
public class Ticker
{
public string AskPrice { get; set; }
public string BidPrice { get; set; }
}
}
ViewModels: TickerViewModel.cs
using Binance.Net.Clients;
using Binance.Net.Objects;
using MVVMDemo2.MVVM.Models;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMDemo2.MVVM.ViewModels
{
[AddINotifyPropertyChangedInterface]
public class TickerViewModel
{
public Ticker Ticker { get; set; }
public TickerViewModel()
{
var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions { });
socketClient.SpotStreams.SubscribeToBookTickerUpdatesAsync("BTCUSDT", data => {
Ticker = new Ticker
{
AskPrice = $"${data.Data.BestAskPrice.ToString("N2")}",
BidPrice = $"${data.Data.BestBidPrice.ToString("N2")}",
};
});
socketClient.UnsubscribeAllAsync();
}
}
}
I want to be able to read the value from the first Entry, so I can perform the following calculation:
BestAskPrice x HoldingsAmount
Once that calculation has been done, it should constantly update the xaml view similar to how the websocket constantly the AskPrice and BidPrice in the view in realtime.
In other words, I have figured out how to get value from a websocket and display it in a xaml view, but I can';t seem to figure out how to get data from a xaml view, process it with data from a websocket, and then in realtime display the data in the xaml view.
Can anyone help?
Solution using MVVM (you need to apply so many changes):
First you need to install nuget package CommunityToolkit.MVVM
Models: Ticker.cs
namespace MVVMDemo2.MVVM.Models
{
public class Ticker
{
public string AskPrice { get; set; }
public string BidPrice { get; set; }
public string HoldingsValue { get; set; }
}
}
Views: TickerView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MVVMDemo2.MVVM.Views.TickerView"
Title="TickerView">
<VerticalStackLayout
VerticalOptions="Center"
HorizontalOptions="Center">
<Label
Text="Holdings"
FontSize="25" />
<Entry Placeholder="Enter holdings"
BackgroundColor="Gray"
Text="{Binding HoldingsAmount}"
Keyboard="Numeric" />
<Label
Text="Ask price"
FontSize="25" />
<Label
Text="{Binding Ticker.AskPrice}"
FontSize="50" />
<Label
Text="Bid price"
FontSize="25" />
<Label
Text="{Binding Ticker.BidPrice}"
FontSize="50" />
<Label
Text="Holdings value"
FontSize="25" />
<Label
Text="{Binding Ticker.HoldingsValue}"
FontSize="50" />
</VerticalStackLayout>
Views: TickerView.xaml.cs
using MVVMDemo2.MVVM.Models;
using MVVMDemo2.MVVM.ViewModels;
namespace MVVMDemo2.MVVM.Views;
public partial class TickerView : ContentPage
{
public TickerView(TickerViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
}
ViewModels: TickerViewModel.cs
using Binance.Net.Clients;
using Binance.Net.Objects;
using MVVMDemo2.MVVM.Models;
using PropertyChanged;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MVVMDemo2.MVVM.ViewModels
{
public partial class TickerViewModel : ObservableObject
{
[ObservableProperty]
public Ticker ticker;
[ObservableProperty]
public decimal holdingsAmount;
public TickerViewModel()
{
var socketClient = new BinanceSocketClient(new BinanceSocketClientOptions { });
socketClient.SpotStreams.SubscribeToBookTickerUpdatesAsync("BTCUSDT", data => {
Ticker = new Ticker
{
AskPrice = $"${data.Data.BestAskPrice.ToString("N2")}",
BidPrice = $"${data.Data.BestBidPrice.ToString("N2")}",
HoldingsValue = (HoldingsAmount * data.Data.BestBidPrice).ToString()
};
});
socketClient.UnsubscribeAllAsync();
}
}
}
Explanation: using OBSERVABLE PROPERTIES you will be able to see the changed between your ui and apply them on the viewmodel. it's tested and got it working on my emulator:
i recommend you to follow James Montemagno and Gerald Versluis that explains everything on their videos about bindable properties, observable objects/properties and everything about maui!
UPDATE if you get a blank page:
App.xaml.cs
public App()
{
InitializeComponent();
MainPage = new AppShell();
}
AppShell.xaml:
<Shell
x:Class="MVVMDemo2.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MVVMDemo2.MVVM.Views">
<ShellContent
ContentTemplate="{DataTemplate local:TickerView}"
Route="TickerView" />
</Shell>
AppShell.xaml.cs
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(TickerView), typeof(TickerView));
}
You have to bind the property in your ViewModel same as the labels
Kindly go through this for more in-depth info
https://www.c-sharpcorner.com/UploadFile/3d39b4/getting-data-from-view-to-controller-in-mvc/

Dynamically add UserControls to a Custom Xamarin ListView using a bindable property

I have already created all of the controls, and have tested that the controls are added as needed without using the binding.
The issue is as soon as I add the binding which is supposed to determine which control to add, the controls stop working as needed.
The list view control will be populated from a collection of a class, which will have an indicator field to determine which control needs to be loaded. The list view contains a 2nd user control which basically acts as a placeholder for the correct control, it has a bindable property of type text which is set to determine the correct control to be loaded.
Here is the XAML Code for the list view control
<ContentView.Content>
<StackLayout>
<Label Text="Binding Control Type"/>
<Entry x:Name="cntName"/>
<ListView x:Name="GroupedView" GroupDisplayBinding="{Binding Title}" HasUnevenRows="True" GroupShortNameBinding="{Binding ShortName}" IsGroupingEnabled="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Add Comment"/>
<MenuItem Text="Add Attachment"/>
</ViewCell.ContextActions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="7*"/>
</Grid.RowDefinitions>
<Label Text="{Binding QUESTION_ID}" Grid.Row="0" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
<Label Text="{Binding QUESTION_DETAILS}" Grid.Row="1" Grid.Column="0" VerticalTextAlignment="Center" FontSize="Medium"/>
<con:ucListViewControls ControlType="{Binding QUESTION_ANSWERCONTROL}" Grid.Row="1" Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupHeaderTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Add Comment"/>
<MenuItem Text="Add Attachment"/>
</ViewCell.ContextActions>
<StackLayout Orientation="Horizontal" Padding="5,5,5,5" BackgroundColor="#E2F5F9">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference this}, Path=Tapped}" CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<Button Image="{Binding StateIcon}" BackgroundColor="Transparent" BorderColor="Transparent" BorderWidth="0"/>
<Label Text="{Binding Title}" TextColor="#005569" FontSize="15" VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.GroupHeaderTemplate>
</ListView>
</StackLayout>
</ContentView.Content>
And the code behind for the control
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ucExpandibleListView : ContentView
{
private ObservableCollection<dbQuestionGroup> _allGroups;
private ObservableCollection<dbQuestionGroup> _expandedGroups;
public ucExpandibleListView()
{
InitializeComponent();
Tapped = new Command(x => HeaderTapped(x));
_allGroups = new ObservableCollection<dbQuestionGroup>()
{
new dbQuestionGroup("Category 1", "C1", false)
{
new dbQuestionModel() { QUESTION_ID = 1, QUESTION_DETAILS = "Testing Question 1", QUESTION_ANSWERCONTROL = "RBL" },
new dbQuestionModel() { QUESTION_ID = 2, QUESTION_DETAILS = "Testing Question 2", QUESTION_ANSWERCONTROL = "" }
}
};
UpdateListContent();
}
private void UpdateListContent()
{
_expandedGroups = new ObservableCollection<dbQuestionGroup>();
foreach (dbQuestionGroup group in _allGroups)
{
dbQuestionGroup newGroup = new dbQuestionGroup(group.Title, group.ShortName, group.Expanded);
newGroup.QuestionCount = group.Count;
if (group.Expanded)
{
foreach (dbQuestionModel question in group)
{
newGroup.Add(question);
}
}
_expandedGroups.Add(newGroup);
}
GroupedView.ItemsSource = _expandedGroups;
}
public Command Tapped { get; set; }
private void HeaderTapped(object group)
{
var groupCat = (dbQuestionGroup)group;
int selectedIndex = _expandedGroups.IndexOf(groupCat);
if (groupCat.Expanded)
{
_allGroups[selectedIndex].Expanded = false;
}
else
{
_allGroups.ToList().ForEach(x => x.Expanded = false);
_allGroups[selectedIndex].Expanded = !_allGroups[selectedIndex].Expanded;
}
UpdateListContent();
}
}
Here is the XAML Code for the placeholder control
<ContentView.Content>
<StackLayout x:Name="stkPlaceholder">
</StackLayout>
</ContentView.Content>
And the code behind for the placeholder control
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ucListViewControls : ContentView, INotifyPropertyChanged
{
public ucListViewControls()
{
InitializeComponent();
}
#region Control Attributes
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
#endregion
#region Bindable Properties
public static readonly BindableProperty ControlTypeProperty = BindableProperty.Create(nameof(ControlType), typeof(string), typeof(ucListViewControls));
public string ControlType
{
get
{
return (string)GetValue(ControlTypeProperty);
}
set
{
SetValue(ControlTypeProperty, value);
AddControl();
NotifyPropertyChanged("ControlType");
}
}
#endregion
public void AddControl()
{
switch (ControlType)
{
case "RBL":
ucRadiobuttons radiobuttons = new ucRadiobuttons();
radiobuttons.lblTitle1 = "Yes";
radiobuttons.lblTitle2 = "No";
radiobuttons.lblTitle3 = "N/A";
radiobuttons.OnColor1 = Color.Green;
radiobuttons.OnColor2 = Color.Red;
radiobuttons.OnColor3 = Color.Transparent;
stkPlaceholder.Children.Add(radiobuttons);
break;
default:
Entry placeholder = new Entry();
stkPlaceholder.Children.Add(placeholder);
break;
}
}
}
I have tested that the controls are added without the binding, which works perfectly.
I have tried to rewrite the bindable property multiple times in case I missed something, I also could not find any post relating to something similar which wouldve helped me.
Any clues?
The first screenshot shows the expected output, and the seconds screenshots shows what happens when the binding is applied.
dbQuestionModel:
using System.Collections.Generic;
namespace PivotMobile_BusinessLayer.Models
{
public class dbQuestionModel
{
public int QUESTION_PK { get; set; }
public int QUESTION_ID { get; set; }
public string QUESTION_CATEGORY { get; set; }
public string QUESTION_DETAILS { get; set; }
public string QUESTION_TYPE { get; set; }
public string QUESTION_ANSWERCONTROL { get; set; }
public string QUESTION_COMMENT { get; set; }
public List<string> QUESTION_ATTACHMENTS { get; set; }
}
}
dbQuestionGroup:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace PivotMobile_BusinessLayer.Models
{
public class dbQuestionGroup : ObservableCollection<dbQuestionModel>, INotifyPropertyChanged
{
public static ObservableCollection<dbQuestionGroup> All { private set; get; }
private bool _expanded;
public string Title { get; set; }
public string ShortName { get; set; }
public bool Expanded
{
get
{
return _expanded;
}
set
{
if (_expanded != value)
{
_expanded = value;
OnPropertyChanged("Expanded");
OnPropertyChanged("StateIcon");
}
}
}
public string StateIcon
{
get
{
return Expanded ? "expanded_blue.png" : "collapsed_blue.png";
}
}
public int QuestionCount { get; set; }
public dbQuestionGroup(string title, string shortName, bool expanded = true)
{
Title = title;
ShortName = shortName;
Expanded = expanded;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
ListView Page 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"
x:Class="PivotMobile.Views.ObservationsView"
xmlns:con="clr-namespace:PivotMobile.Controls">
<ContentPage.Content>
<StackLayout Margin="5">
<con:ucExpandibleListView/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
ListView Page Code Behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace PivotMobile.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ObservationsView : ContentPage
{
public ObservationsView ()
{
InitializeComponent ();
}
}
}

Xamarin Application crashes without any exception after implementing MasterDetailPage

my Xamarin-Application crashes without exception after i implemented the MasterDeatilPage. I sticked to a Xamarin-Tutorial (https://www.youtube.com/watch?v=K2be1RfDYK4) and i also compared my solution to the Microsoft-Docs for MasterDetailPage.
By and large I have implemented the documentation, the only diffrences are the file locations and the way i set the ItemsSource of the MasterPage. I heard about the issue, that a not set Title of the MasterPage could lead to the same problem i have, but i did specified the Title-Property.
Here is an excerpt of the Filesystem of my solution:
Here is my Code:
MasterMenuItem.cs:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace WhaleEstimate.Models
{
public class MasterMenuItem
{
public string Title { get; set; }
public string IconSource { get; set; }
public Color BackgroundColor { get; set; }
public Type TargetType { get; set; }
public MasterMenuItem(string title, string iconSource, Color color, Type type)
{
this.Title = title;
this.IconSource = iconSource;
this.BackgroundColor = color;
this.TargetType = type;
}
}
}
MasterDetail.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:WhaleEstimate.Views.Menu;assembly=WhaleEstimate"
xmlns:detailviews="clr-namespace:WhaleEstimate.Views.DetailViews;assembly=WhaleEstimate"
x:Class="WhaleEstimate.Views.Menu.MasterDetail">
<MasterDetailPage.Master>
<local:MasterPage x:Name="masterpage"/>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
<x:Arguments>
<detailviews:InfoScreen1/>
</x:Arguments>
</NavigationPage>
</MasterDetailPage.Detail>
</MasterDetailPage>
MasterDetail.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WhaleEstimate.Models;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace WhaleEstimate.Views.Menu
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterDetail : MasterDetailPage
{
public MasterDetail ()
{
InitializeComponent();
masterpage.ListView.ItemSelected += OnItemSelected;
}
private void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterMenuItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(item.TargetType));
masterpage.ListView.SelectedItem = null;
IsPresented = false;
}
}
}
}
MasterPage.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:WhaleEstimate.Models"
x:Class="WhaleEstimate.Views.Menu.MasterPage"
Title="Test Project">
<ContentPage.Content>
<StackLayout x:Name="MasterStack" VerticalOptions="FillAndExpand">
<StackLayout x:Name="TopStack">
<Label Text="TestProject App" HorizontalOptions="Center" FontSize="Large"/>
</StackLayout>
<StackLayout x:Name="MidStack" VerticalOptions="FillAndExpand">
<ListView x:Name="listView" SeparatorVisibility="None" x:FieldModifier="public">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="{Binding BackgroundColor}">
<Image Source="{Binding IconSource}" Margin="0,10,0,10"/>
<Label Grid.Column="1" Text="{Binding Title}" TextColor="Black" FontSize="Medium"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
<StackLayout x:Name="BottomStack" VerticalOptions="EndAndExpand">
<Button Text="Do some"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
MasterPage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WhaleEstimate.Models;
using WhaleEstimate.Views.DetailViews;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace WhaleEstimate.Views.Menu
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterPage : ContentPage
{
public ListView ListView { get { return ListView; } }
public List<MasterMenuItem> items;
public MasterPage ()
{
InitializeComponent ();
SetItems();
}
private void SetItems()
{
items = new List<MasterMenuItem>();
items.Add(new MasterMenuItem("InfoScreen1", "maus.jpg", Color.White, typeof(InfoScreen1)));
items.Add(new MasterMenuItem("InfoScreen2", "maus.jpg", Color.White, typeof(InfoScreen2)));
ListView.ItemsSource = items;
//listView.ItemsSource = items;
}
}
}
I had to return the ListView, that is defined in xaml and not in code-behind-file. So the clue was changing the line public ListView ListView { get { return ListView; } } to public ListView ListView { get { return listView; } }

DataBindings not updating with OnPropertyChanged

Building my first app with Xamarian.Forms. I have my basic menu and home page built with some labels and a button so that I can bind some data and a method and to make sure the logic code for my game is working. I got my data bindings working as far as the labels go, they appear on screen. However I was unsure if my bindings weren't updating or if my command wasn't binding. So I commented out the ICommand, removed the binding and put the method to advance a turn into the code behind my xaml. Even after this, the data is not updating when the button is clicked which leads me to believe it is a problem with my OnPropertyChanged and the data bindings. I've searched the web and related questions, I've implemented a couple different ways of writing the gets and sets for the bindings, wrote my OnPropertyChanged function a few different ways and still nothing happens when the toolbar button on the home page is clicked.
Here is my HomePageViewModel.cs containing INotifyPropertyChanged
***Edited to reflect changes made since getting the day value to update:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using Engine;
using Xamarin.Forms;
namespace TestApp
{
public class HomePageViewModel : INotifyPropertyChanged
{
public static Player _player = World.Player1;
public string Day = World.TrueDay.ToString();
public string MoneyValue = Convert.ToInt32(Math.Floor(World.Player1.PlayerMoney)).ToString();
public string CurrentLocation = _player.CurrentLocation.Name;
public HomePageViewModel()
{
OnTurn = new Command(execute: On1Turn);
}
public ICommand OnTurn { get; private set; }
public string CurrentDay
{
get { return Day; }
set { Day = value; OnPropertyChanged(); }
}
public string Money
{
get { return MoneyValue; }
set { MoneyValue = value; OnPropertyChanged(); }
}
public string PlayerLocation
{
get { return CurrentLocation; }
set { CurrentLocation = value; OnPropertyChanged(); }
}
void On1Turn()
{
World.TrueDay = World.TrueDay + 1;
CurrentDay = World.TrueDay.ToString();
World.Player1.PlayerMoney = World.Player1.PlayerMoney + 1000;
MoneyValue = Convert.ToInt32(Math.Floor(World.Player1.PlayerMoney)).ToString();
OnPropertyChanged(Money);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Here is my HomePage.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:local1="clr-namespace:TestApp"
mc:Ignorable="d"
x:Class="TestApp.HomePage">
<ContentPage.BindingContext>
<local1:HomePageViewModel/>
</ContentPage.BindingContext>
<ContentPage.ToolbarItems>
<ToolbarItem Text="+24hrs" Clicked="ToolbarItem_Clicked" />
</ContentPage.ToolbarItems>
<StackLayout Padding="20">
<StackLayout Orientation="Horizontal">
<Label Text="Money:" HorizontalOptions="Start"/>
<Label x:Name="lblPlayerMoney" Text="{Binding Money, Mode=OneWay}" HorizontalOptions="FillAndExpand"/>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="EndAndExpand" Margin="0,-40,0,0">
<Label Text="Current Location:" HorizontalOptions="CenterAndExpand"/>
<Label x:Name="lblPlayerLocation" Text="{Binding PlayerLocation, Mode=OneWay}" HorizontalOptions="CenterAndExpand"/>
</StackLayout>
<StackLayout Orientation="Horizontal" VerticalOptions="StartAndExpand">
<Label Text="Current Day:" HorizontalOptions="CenterAndExpand" Margin="30,0,0,0"/>
<Label x:Name="lblCurrentDay" Text="{Binding CurrentDay, Mode=OneWay}" HorizontalOptions="CenterAndExpand"/>
</StackLayout>
</StackLayout>
</ContentPage>
And the HomePage.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Engine;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
//BindingContext = new HomePageViewModel();
}
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
World.TrueDay = World.TrueDay + 1;
World.Player1.PlayerMoney = World.Player1.PlayerMoney + 1000;
}
}
}
Any insight into the issue would be greatly appreciated as I'm new to Xamarian.Forms and the OnPropertyChanged feature in general. Thanks for the time!
EDIT******
Here is the World.cs were I set the properties if it helps
using System;
using System.Collections.Generic;
using System.Text;
namespace Engine
{
public class World
{
public static decimal TrueDay = 1;
//public string LocationText = Player1.CurrentLocation.Name.ToString();
public static Player Player1;
public static readonly List<Location> Locations = new List<Location>();
public const int LOCATION_ID_OSHAWA = 1;
public const int LOCATION_ID_TORONTO = 2;
public static void GenerateWorld()
{
PopulateLocations();
Player1 = new Player("Jordan", LocationByID(LOCATION_ID_OSHAWA), 5000);
}
private static void PopulateLocations()
{
Location oshawa = new Location(LOCATION_ID_OSHAWA, "Oshawa");
Location toronto = new Location(LOCATION_ID_TORONTO, "Toronto");
Locations.Add(oshawa);
Locations.Add(toronto);
}
public static Location LocationByID(int id)
{
foreach (Location location in Locations)
{
if (location.ID == id)
{
return location;
}
}
return null;
}
}
}
Not too understanding logic about project, but if want to change model data can do as follow
HomePage.xaml.cs:
namespace TestApp
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class HomePage : ContentPage
{
HomePageViewModel homePageViewModel = new HomePageViewModel();
public HomePage()
{
InitializeComponent();
BindingContext = homePageViewModel ;
}
private void ToolbarItem_Clicked(object sender, EventArgs e)
{
homePageViewModel.CurrentDay = xxx ;
homePageViewModel.xxxxx = xxxx;
//Something like this can change model data
}
}
}
Here is a sample data binding discussion can be refer to.

Categories