I'm having a problem on how to display my binding context, i have it on my code behind but its not working. it says no data context found
I tried other methods but it still doesn't read my binding. Here is my xaml code
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App1.RecipePage1"
Title="Recipe Name">
<ContentPage Title="Ingredients">
<StackLayout>
<ListView ItemsSource="{Binding Recipes}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Text="{Binding Ingredient}"></Label>
<Label Grid.Row="0" Grid.Column="1" Text="{Binding Quantity}"></Label>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
And this is my code behind
public partial class RecipePage1 : TabbedPage
{
public IList<Recipe> Recipes { get; set; }
public RecipePage1()
{
InitializeComponent();
Recipes = new List<Recipe>();
Recipes.Add(new Recipe() { Ingredient = "Kimchi", Quantity = "100 Grams" });
Recipes.Add(new Recipe() { Ingredient = "Noodle", Quantity = "80 Grams" });
Recipes.Add(new Recipe() { Ingredient = "Scallions", Quantity = "150 Grams" });
Recipes.Add(new Recipe() { Ingredient = "Spinach", Quantity = "200 Grams" });
Recipes.Add(new Recipe() { Ingredient = "Prawns", Quantity = "500 Grams" });
BindingContext = this;
}
public class Recipe
{
public string Ingredient { get; set; }
public string Quantity { get; set; }
}
}
there is no error, it will run but it doesnt read my binding even if i already called it on my behind
It is a simple binding problem. If you want to use the binding in the xaml, you need to use the MVVM to set a ViewModel which implements the INotifyChanged interface as the binding context. You can check the official document about it.
In addition, you can also give the ListView a name such as:
<ListView x:Name = "listview">
And set the ItemSource in the page such as:
listview.ItemSource = Recipes;
You can check the official document about the ListView.
Related
I am binding ObservableCollection with CollectionView.
<CollectionView ItemsSource="{Binding LeftExercises}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="models:ExerciseModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="{Binding SetNumber}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
<Label Grid.Column="1" Grid.Row="0" Text="{Binding Weight}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
<Label Grid.Column="2" Grid.Row="0" Text="{Binding Reps}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" TextColor="Black" FontSize="Medium" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
private ObservableCollection<ExerciseModel> _leftExercises;
public ObservableCollection<ExerciseModel> LeftExercises
{
get => _leftExercises;
set
{
if (_leftExercises != value)
{
_leftExercises = value;
OnPropertyChanged(nameof(LeftExercises));
}
}
}
When I add a new object to the Collection, it will reflect in my UI but whenever I try to update the value of any object, it will not reflect.
This is my model
public class ExerciseModel
{
public int SetNumber { get; set; }
public decimal Weight { get; set; }
public int Reps { get; set; }
public ExerciseType ExerciseType { get; set; }
public Side Side { get; set; }
}
I am incrementing the Reps (update Reps property) from the below command.
private Command _dummyLeftIncreaseRepsCommand;
public Command dummyLeftIncreaseRepsCommand
{
get
{
return _dummyLeftIncreaseRepsCommand ??= new Command(() =>
{
ExerciseModel lastObj = LeftExercises.Last(x => x.Side == SharedVM.ActiveSide);
lastObj.Reps += 1;
});
}
}
Your ExerciseModel class needs to implement INotifyPropertyChanged.
To do so, simply add the interface name to your class code, like this:
class MyClass : INotifyPropertyChanged
Alternatively, you can set the ItemsSource for the object again. Doing so will update the visual as well. You can do this at runtime like this:
XAML file:
<CollectionView x:Name="MyView">[...]
Code in background:
MyView.ItemsSource = MyCollectionVar;
I advise you to use CommunityToolkit.MVVM and CommunityToolkit.MAUI, when you try to bind commands, observable properties and collections to your XAML.
The benefit of this will be that you wont have to write walls of code, and spend hours to figure out why it is not working.
It saves you time, that you can put in something useful, instead of dealing with boilerplate code.
What I'm trying to implement is a message chat bubble wherein you tap the control/message and the message would expand showing details of the date and the seen / sent status below. I do have a DataTemplate Selector for the different controls I have for the Sender and the Receiver.
My problem is changing the Height of the message in the ListView. I tried implementing the Binding of the RowDefinition to whatever the Height variable is in my Message class (the class that holds info regarding the message). Although the height was updated, it didn't reflect on the ListView. I've scoured over the internet for existing chat UI templates but I think most of them are paid. Hence, I'm trying to do follow Change WPF DataTemplate for ListBox item if selected. But for Xamarin, there's no ListBoxItem as there's only a ListView.
On a further note, I am working on Android and iOS. An example that is cross-platform to solve this will be greatly appreciated. Below are parts of my code.
datatemplate.cs
class MessageTemplateSelector : DataTemplateSelector
{
public MessageTemplateSelector()
{
ReceiverDataTemplate = new DataTemplate(typeof(MessageReceiver));
SenderDataTemplate = new DataTemplate(typeof(MessageSender));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var message = item as Message;
if (message == null)
return null;
return message.isSender ? ReceiverDataTemplate : SenderDataTemplate;
}
private readonly DataTemplate ReceiverDataTemplate;
private readonly DataTemplate SenderDataTemplate;
}
MessageSender.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.layout.MessageSender">
<ViewCell.View>
<Grid HorizontalOptions="EndAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Path=Height}"/>
<RowDefinition Height="*" />
<RowDefinition Height="{Binding Path=Height}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Path=timestamp}" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="{Binding Path=Selected}"/>
<Frame Padding="0" CornerRadius="20" Grid.Column="1" Grid.Row="1" HorizontalOptions="EndAndExpand" >
<Grid BackgroundColor="White" VerticalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{Binding Path=text}" VerticalOptions="FillAndExpand" Margin="15,10"/>
</Grid>
</Frame>
<Label Text="Seen" Grid.Column="1" Grid.Row="2" HorizontalOptions="EndAndExpand" IsVisible="{Binding Path=Selected}"/>
</Grid>
</ViewCell.View>
</ViewCell>
Message.cs
class Message
{
public bool isSender { get; set; }
public sbyte status { get; set; }
public string text { get; set; }
public string timestamp { get; set; }
public Message(bool isSender, sbyte status, string text, string timestamp)
{
this.isSender = isSender;
this.status = status;
this.text = text;
this.timestamp = timestamp;
}
public sbyte height = 0;
public sbyte Height { get { return height; } set { height = value; } }
bool selected = false;
public bool Selected
{
get { return selected; }
set { selected = value;if (value) { Height = 25; } else { Height = 0; } }
}
}
Show mainpage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Project.model"
x:Class="Project.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector"></local:MessageTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="conversation"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Message}"
HasUnevenRows="True"
SeparatorVisibility="None"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
IsPullToRefreshEnabled="true"
ItemTapped="Conversation_ItemTapped"
Refreshing="Conversation_Refreshing">
</ListView>
</ContentPage>
MainPage.cs
private void Conversation_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null) return;
Message selectedItem = (Message)e.Item;
Log.Debug("ItemTap","Height before:" + selectedItem.Height);
if (selectedItem.Selected) { ((ListView)sender).SelectedItem = null; selectedItem.Selected = false; }
else { selectedItem.Selected = true; }
Log.Debug("ItemTap", "Height after:" + selectedItem.Height);
}
This is a screenshot of my log that is present in the ItemTapped event in my ListView. As you can see, the height updates but it's not reflecting on the ListView.
I have a DetailPage where I have put a ContenView into. In this ContenView I implemented a Grid because I wanted something like Refresh-Button for the JSON-ListView. That worked really well so far.
With your help I've accomplished to parse the JSON Data and put it into a ListView. My plan was to put this ListView in the 2nd row of my first Grid. I did another Grid in the first Grid where I tried to push the ListView inside, but the fields are empty...
Screenshot: http://imgur.com/a/Tj0kd
Here is my XAML Code from the DetailPage
<?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="ODC_Foto_ConWin_v1.YardList"
xmlns:local="clr-namespace:ODC_Foto_ConWin_v1"
Title="YardList"
BackgroundColor="LightGray">
<ContentPage.Content>
<Grid BackgroundColor="#004d93">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="File-Age: < 12m" FontSize="Small" Margin="5,5,0,0" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="Start" VerticalOptions="Center" TextColor="White" FontAttributes="Bold"/>
<Image Source="sync.png" Grid.Column="1" Grid.Row="0" Scale="0.7" HorizontalOptions="End" VerticalOptions="Center" Margin="0,5,0,0"/>
<Label Grid.Column="2" Grid.Row="0" Text="Sync" FontSize="Small" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="End" VerticalOptions="Center" TextColor="White" Margin="0,5,5,0" FontAttributes="Bold"/>
<Button Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="2" BackgroundColor="Transparent" VerticalOptions="Center" Clicked="yardListSyncButton_Clicked"/>
<Grid Grid.ColumnSpan="3" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView x:Name="listViewJson">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Grid.Column="0" Text="{Binding id}" BackgroundColor="Transparent" />
<Label Grid.Column="1" Text="{Binding kfz_nr}" BackgroundColor="Transparent" />
<Label Grid.Column="2" Text="{Binding kfz_kz}" BackgroundColor="Transparent" />
<Label Grid.Column="3" Text="{Binding timestamp}" BackgroundColor="Transparent" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<ActivityIndicator x:Name="ProgressLoader" IsVisible="True" IsRunning="True"/>
</Grid>
</ContentPage.Content>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="add.png" Activated="ToolbarItem_Activated"/>
</ContentPage.ToolbarItems>
</ContentPage>
and here is my C# Code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ODC_Foto_ConWin_v1
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class YardList : ContentPage
{
public YardList()
{
InitializeComponent();
GetJSON();
}
private void yardListSyncButton_Clicked(object sender, EventArgs e)
{
GetJSON();
}
private void ToolbarItem_Activated(object sender, EventArgs e)
{
// code for manual process
}
public async void GetJSON()
{
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("http://x.x.x.x/xample.JSON");
string json = await response.Content.ReadAsStringAsync();
RootObject rootObject = new RootObject();
ListView listViewJson = new ListView();
if (json != "")
{
rootObject = JsonConvert.DeserializeObject<RootObject>(json);
}
listViewJson.ItemsSource = rootObject.process;
ProgressLoader.IsVisible = false;
}
public class Process
{
public string id { get; set; }
public string fzg_nr { get; set; }
public string fzg_kz { get; set; }
public string timestamp { get; set; }
}
public class RootObject
{
public string file_timestamp { get; set; }
public List<Process> process { get; set; }
}
}
}
Is this something about wrong DataBinding? Am I initiating the ItemSource at the wrong point?! Because when I set the datatemplate in the GetJSON() method the data is visible...
So far i tried a few examples but at some point there must be a little mistake. I am new programming (I'm normally a systemadministrator) so I don't where to start looking for the mistakes.
Any help is really appreciated.
Thanks in advance,
Paul
Short answer: don't create new list, use existing one - just remove ListView listViewJson = new ListView(); from GetJSON and it should work.
Long answer
In your XAML you have:
<ListView x:Name="listViewJson">
and in your GetJSON method you are creating NEW ListView and setting ItemSource like this:
public async void GetJSON()
{
ListView listViewJson = new ListView();
(...)
listViewJson.ItemsSource = rootObject.process;
}
So you have 2 ListViews, one in XAML, and one in GetJSON method. Your ListView in XAML has the same name like ListView in GetJSON method (listViewJson). Because ListView created in method is 'closer' in line listViewJson.ItemsSource = rootObject.process then ListView created in XAML,you are setting ItemSource of newly created ListView that was not added to your ContentView.
EDIT:
I see couple other minor issues in your code, not related to the question but because you are learning, I thing it's worth to point them out:
Labels in your ListView template do not have Grid as direct parent so Grid.Column="0" does not have any effect
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Grid.Column="0">
In C# it is a common practice to use PascalCase naming style for properties. I think that you did this: public string timestamp { get; set; } because of json format. You can use JsonPropertyAttribute to set your mapping between C# and json like this:
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
You are hiding ProgressLoader at the end of GetJSON but you are not showing it at the beginning, so it is visible only on your fist GetJSON call (so in constructor).
now your are using code behind and it is totally ok when you are starting with Xamarin. Later on I would suggest you go take a look at MVVM pattern.
I'm new to Xamarin and C#, so forgive me if my code is crap.
So essentially I'm trying to create a simple page that displays a list of course names, and allows a button to insert or remove items from the list within the view.
As of now the program builds and runs but the view does not display the contents of the list. Any help would be greatly appreciated. Thanks!
Here's the view:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="65" />
<RowDefinition Height="*"/>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame OutlineColor="Black" Grid.Row="0" Grid.ColumnSpan="2">
<Label Text= "My Courses" FontSize="20" TextColor="Black" HorizontalTextAlignment="Center"/>
</Frame>
<Frame OutlineColor="Black" Grid.Row="1" Grid.ColumnSpan="2">
<ListView x:Name="CourseList" ItemsSource="{Binding GetCourseList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Label FontSize="14" Text="{Binding Name}" TextColor="Black" Grid.Row="0" Grid.Column="0"/>
<Button FontSize="14" Text="X" TextColor="Black" Grid.Row="0" Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
<Button Text="+" FontSize="20" TextColor="Green" BackgroundColor="Silver"
HorizontalOptions="Fill" VerticalOptions="Center"
Grid.Row="2" Grid.ColumnSpan="2"
/>
</Grid>
Here's the View.cs code:
public partial class MainPage : ContentPage
{
MainPageViewModel vm;
public MainPage()
{
vm = new MainPageViewModel();
vm.AddCourse();
BindingContext = vm;
InitializeComponent();
}
Here's the View Model:
class MainPageViewModel
{
public string Name { get; set; }
public List<Course> Courses;
public MainPageViewModel()
{
Courses = new List<Course>();
}
public List<Course> GetCourseList()
{
return Courses;
}
public void AddCourse()
{
Course NewCourse = new Course();
NewCourse.Name = "New Course Added";
Courses.Add(NewCourse);
}
}
Finally, Here's my Model:
public class Course
{
public string Name { get; set; }
}
So I have been able to display a list using this code but I do not believe this will allow me to add or delete from the list and update the view via button click (Correct me if I'm wrong).
public List<Course> Courses{
get{
return new List<Course>(){
new Course(){Name = "Added a course"}
};
}
I think I just found my error and solution.
public List<Course> Courses{
get{
return new List<Course>(){
new Course(){Name = "Added a course"}
};
}
Because Courses is "read only" and now being bound to the view I created a member variable List VMCourse to hold the actually list of objects and am using Courses to be bounded to and read into the view.
It appears to work, but I still need to test it with button features for adding and deleting from the list. Does this look like a decent solution to you guys?
Thanks in advance!
class MainPageViewModel
{
public string Name { get; set; }
public List<Course> VMCourse;
//This is now bound to the view's listview
public List<Course> Courses {
get
{
return VMCourse;
}
}
public MainPageViewModel()
{
VMCourse = new List<Course>();
}
public void AddCourse()
{
Course NewCourse = new Course();
NewCourse.Name = "New Course Added";
VMCourse.Add(NewCourse);
}
You cannot bind a list to a method. You should use binding with properties. So to fix this you only need to change
public List<Course> GetCourseList()
{
return Courses;
}
To
public List<Course> GetCourseList
{
get { return Courses; }
}
But, as it's mentioned in the comments not any property is suitable to be used with binding unless you know what you are doing so search about binding.
I need to dig into a nested observable collection in UWP, which consists another observable collection inside it, and then bind it to my XAML.
How could I do it?
Allen Rufolo's Solution works. But Here is another way of approaching this.
x:Bind is newly implemented and available for UWP. My Answer is based on x:Bind
Sample Classes
public class MainItems
{
public string ItemName { get; set; }
public ObservableCollection<SubItems> SubItemsList { get; set; }
}
public class SubItems
{
public string SubItemName { get; set; }
}
Sample Data
ObservableCollection<MainItems> _data = new ObservableCollection<MainItems>();
for (int i = 1; i <= 5; i++)
{
MainItems _mainItems = new MainItems();
_mainItems.ItemName = "Main" + i.ToString();
_mainItems.SubItemsList = new ObservableCollection<SubItems>();
for (int j = 1; j <= 3; j++)
{
SubItems _subItems = new SubItems()
{
SubItemName = "SubItem" + i.ToString()
};
_mainItems.SubItemsList.Add(_subItems);
}
_data.Add(_mainItems);
}
My XAML
<ListView x:Name="MyMainList">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MainItems">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{x:Bind ItemName}" />
<ListView ItemsSource="{x:Bind SubItemsList}" Grid.Row="1">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:SubItems">
<TextBlock Foreground="Red" Text="{x:Bind SubItemName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
x:Bind gives you an easy way to Bind your Nested Observable Collection
Output
A code example of your observable collections would help but you could do something like this...
public class MyViewModel
{
public ObservableCollection<MyObject> MyObjectCollection { get; set;}
}
public class MyObject
{
public string ObjectName {get; set;}
public ObservableCollection<AnotherObject> AnotherObjectCollection { get; set; }
}
And in your XAML you can bind to these collection similar to this
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView x:Name="ListView1" Grid.Column="0"
ItemsSource="{Binding MyObjectCollection}">
<ListView.ItemTemplate>
<Datatemplate>
<TextBlock Text="{Binding ObjectName}"/>
</Datatemplate
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column=1 DataContext="{Binding ElementName=ListView1, Path=SelectedItem}">
<ListView ItemsSource="{Binding AnotherObjectCollection}"/>
</Grid>
</Grid>
In this example, the DataContext of the second Grid is bound to the selected item in ListView1.
I am not sure that I get what do you need, but I guess that it could be the same as for WPF.
Check out questions and answers for the next questions:
Binding nested ItemsControls to nested collections
Nested ObservableCollection data binding in WPF
WPF Binding on Nested ItemControls with Sub Collection
Databinding for nested collections in XAML (WPF and Silverlight)