Im Looking for a way to display my C# Objects in a Grid. But even after serveral hours of searching I couldn't find a valid Way. How can I Achieve the following result?
first in your c# code create an observable collection of your object type;
private ObservableCollection<YourModel> objList;
public ObservableCollection<YourModel> ObjList
{
get { return objList; }
set
{
objList = value;
}
}
add your data to the list:
ObjList = new ObservableCollection<YourModel>(){ new YourModel{ ...} , new YourModel{...} };
In xaml create a grid inside n collectionview :
<CollectionView ItemsSource="{Binding ObjList}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="10,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0"
Text="{Binding Name}"
/>
<Label
Grid.Column="1"
Text="{Binding Att1}"
/>
<Label
Grid.Column="2"
Text="{Binding Att2}"
/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
...
</CollectionView>
Alternatively Check DataGrid in syncfusion :
check
Related
I have a dynamic checkbox,
private async void GetOffices()
{
string url = $"{baseurl}/get-offices";
var response = await httpservices.SendGetRequest(url);
for (int i = 0; i < response.Count; i++)
{
Offices.Add(new Office() { id = (string)response[i]["id"], OfficeName = (string)response[i]["office_name"] });
}
}
And my XAML looks like this:
<CollectionView ItemsSource="{Binding Offices}">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="viewmodel:Office" >
<Grid Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame Grid.ColumnSpan="2" Padding="15" BackgroundColor="LightBlue" x:Name="OfficeSelection" CornerRadius="0">
<Grid Grid.ColumnSpan="2" RowDefinitions="Auto,Auto" ColumnDefinitions="40,*">
<CheckBox Grid.Column="0" x:Name="{Binding OfficeName}" CheckedChanged="OnCheckBoxCheckedChanged"/>
<Label TextTransform="Uppercase" Padding="10" FontSize="Default" Text="{Binding OfficeName}" Grid.Column="1"></Label>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
And it will display like this:
What I want to do is, when I click the continue button, I can get all the checked checkboxes Name or value where in this case are: ADMISSION, CASHIER, PLENARY HALL and store it in array?
This is my button XAML:
<Button
Grid.Row="1"
Grid.Column="0"
Margin="5"
BorderWidth="0"
Command="{Binding GetSelectedOfficesCommand}"
CornerRadius="0"
Text="Continue" />
[RelayCommand]
public void GetSelectedOffices()
{
//CODe HERE
}
MAUI related MS Docs is quiet confusing for me. Anyone can give me a code snippet to solve this?
You should add a boolean property to the office view model and bind it to the checkbox IsChecked. Then in your GetSelectedOffices function you just filter by this property:
var selectedNames = Offices.Where(x => x.IsSelected).Select(x => x.OfficeName);
I am quite a beginner so take it easy on me. I have a collection view which is bound to an observable collection. The observable collection receives data and has the items but the Collectionview doesn't display anything at all. Could someone please help me with this. Thanks.
XAML
<CollectionView Grid.Row="1" ItemsSource="{Binding Fav}" x:Name="CVWatchItems" SelectionMode="Single" SelectionChanged="CVWatchItems_SelectionChangedAsync">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="8, 8, 8, 0">
<Frame BorderColor="LightGray" CornerRadius="0" HasShadow="True" Padding="5">
<Grid Padding="0" ColumnSpacing="0" RowSpacing="0" Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ffimageloading:CachedImage Source="{Binding SingleimageUrl}" Aspect="AspectFill" WidthRequest="120" HeightRequest="100" Grid.Row="0" Grid.Column="0"/>
<Grid Grid.Row="0" Grid.Column="1" Padding="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Padding="0, 0, 0, 3" Text="{Binding Title}" LineBreakMode="TailTruncation" TextColor="Black" FontSize="Medium" Grid.Row="0"/>
<Label Text="{Binding Price, StringFormat='Nu.{0}'}" FontSize="Small" TextColor="Black" Grid.Row="1" HorizontalOptions="StartAndExpand" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="7*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding location}" FontSize="Small" TextColor="Gray" Grid.Column="0" HorizontalOptions="StartAndExpand" VerticalOptions="EndAndExpand"/>
<!--Watchlist Icon-->
<Image Source="{Binding Favorite}" Grid.Column="1" Aspect="AspectFill" HeightRequest="28" Margin="2, 0" HorizontalOptions="EndAndExpand" VerticalOptions="EndAndExpand">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
<!--Give ID to each ad and stored data is retrieved through id.-->
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</Grid>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Code behind
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
public Watchlist ()
{
InitializeComponent ();
NavigationPage.SetHasNavigationBar(this, false);
NavigationPage.SetHasBackButton(this, false);
CVWatchItems.SetBinding(CollectionView.ItemsSourceProperty, nameof(Fav));
GetFavItems();
}
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
}
Thanks guys.
First your observable collection is static and you never set BindingContext to your page.
public static ObservableCollection<Item> Fav { get; set; } = new ObservableCollection<Item>();
Remove static keyword from the definition of Fav, change binding in your XAML (`assign a Name to your page) like:
<ContentPage x:Name="Root" ..../>
<CollectionView Grid.Row="1" ItemsSource="{Binding Source={x:Reference Name=Root}, Path=Fav}" ..../>
Also remove binding from your code behind. Last thing, your page has to implement INotifyPropertyChanged interface because you have to tell the page that this collection changed (your collection is filled after your async task is finished) So you have to raise PropertyChanged after your collection is filled.
But maybe in your case is better and easier to set ItemsSource without binding because you are not using MVVM.
If you are in code behind of your page it is not necessary to use binding. You can set collectionview itemsource like:
private async void GetFavItems()
{
var MyFavorites = await Task.WhenAll(FirebaseDataHelper.GetFavoriteItems(allFavorites));
foreach (var favorite in MyFavorites)
{
if (favorite.Count > 0)
{
for (int i = 0; i < favorite.Count; i++)
{
favorite[i].IsFavorite = true;
if (!Fav.Any(s => s.Id == favorite[i].Id))
Fav.Add(favorite[i]);
}
}
}
CVWatchItems.ItemsSource = Fav;
}
If you will use MVVM in your application you should use bindings.
First of all im just starting with xamarin forms, and c# i have managed to use listviews, however im a lil bit confused when it comes to grids, basically what i need is to bind an object to a serie of grids that behave like this:
2 columns per row, i have managed to do it with this code:
<Grid HorizontalOptions="Fill" VerticalOptions="Fill" Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<!--fila 1-->
<StackLayout Grid.Column="0" Grid.Row="0" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="0" BackgroundColor="Red" HorizontalOptions="Fill"></StackLayout>
<!--fila 2-->
<StackLayout Grid.Column="0" Grid.Row="1" BackgroundColor="Red" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="1" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<!--fila 3-->
<StackLayout Grid.Column="0" Grid.Row="2" BackgroundColor="Blue" HorizontalOptions="Fill"></StackLayout>
<StackLayout Grid.Column="1" Grid.Row="2" BackgroundColor="Red"
HorizontalOptions="Fill"></StackLayout>
</Grid>
However, i dont know how to fill the grids with information dynamically. with the listview is quite simple since its only with the bind command, here i dont have idea, can someone please point me into the right direction?
thanks.
int row = 0;
int col = 0;
// data is a List<string>
foreach (var text in data) {
var label = new Label() { Text = text };
grid.Children.Add(box, col, row);
col++;
if (col > 1) {
col = 0;
row++;
}
}
I have a usercontrol with 2 ListViews in it. One for holding a list of predefined categories and one for the list with all the categories in it.
When i place the ListViews inside a <Grid> than everything works perfect.
The working xaml code (with Grid):
<Grid Style="{StaticResource ResourceKey=ContentStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListView x:Name="lstPredefinedCategories" Grid.Row="0" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
<ListView.Header>
<StackPanel>
<TextBlock Text="Voorgestelde categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20,0">
<TextBlock Text="Alle categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
<TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
<Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
<Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
<Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
</Grid>
</Grid>
</Grid>
The only problem with this is that I dont get the UI behaviour that I want. If I use a grid then only the red border is scrollable (because of the ListView). But what I need is that the entire green border is scrollable.
So I want to put everything in a <ScrollViewer><StackPanel></StackPanel></ScrollViewer>.
But when I do so, I occasionally get an out-of-memory exception (sometimes the apps just freezes and close without an exception).
Here is my not working xaml with the <ScrollViewer>:
<ScrollViewer>
<StackPanel>
<ListView x:Name="lstPredefinedCategories" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
<ListView.Header>
<StackPanel>
<TextBlock Text="Voorgestelde categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Margin="20,0">
<TextBlock Text="Alle categorieën" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
<Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
</StackPanel>
<TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
<Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
<Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
<Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
</Grid>
</Grid>
</StackPanel>
</ScrollViewer>
Any thoughts on why my app is freezing or get an OOM-exception?
Update
It comes because in the 2nd ListView they are too much objects loaded. So I'm gonna try to fix it with ISupportIncrementalLoading.
Or is there an other way?
The solution was to use virtualization (ISupportIncrementalLoading) like suggested in the comments.
Here you can find my implementation class of ISupportIncrementalLoading:
public class StringKeyValueIncrementalCollection : ObservableCollection<StringKeyValue>, ISupportIncrementalLoading
{
private List<StringKeyValue> allCategories;
private int lastItem = 1;
public StringKeyValueIncrementalCollection(List<StringKeyValue> categories)
{
this.allCategories = categories;
}
public bool HasMoreItems
{
get
{
if (lastItem == allCategories.Count)
{
return false;
}
else
{
return true;
}
}
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
{
CoreDispatcher coreDispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(async () =>
{
List<StringKeyValue> items = new List<StringKeyValue>();
for (int i = 0; i < count; i++)
{
items.Add(allCategories[i]);
lastItem++;
Debug.WriteLine(lastItem);
if (lastItem == allCategories.Count)
{
break;
}
}
await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
() =>
{
foreach (StringKeyValue item in items)
{
this.Add(item);
}
});
return new LoadMoreItemsResult() { Count = count };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
And then my code in the ViewModel. As you can see, I use the StringKeyValueIncrementalCollection instead of a regular List<object>:
private StringKeyValueIncrementalCollection categories;
private StringKeyValueIncrementalCollection allCategories;
public StringKeyValueIncrementalCollection Categories
{
get { return categories; }
set
{
filteredCategories = value;
RaisePropertyChanged("Categories");
}
}
public async void LoadCategories()
{
List<StringKeyValue> temp = await this.openVlaanderenService.GetCategoriesData();
allCategories = new StringKeyValueIncrementalCollection(temp);
Categories = allCategories;
}
The only problem that you than have is that the ScollViewer will allow it's content to fill as much space as it wants, so the data just will keep loading. To fix that I did what was suggested in ISupportIncrementalLoading inside ScrollViewer not supported?
So I added a SizeChanged="ScrollViewer_SizeChanged" event to my ScrollViewer and in code behind set the size of the ListView based on the viewport size properties of the ScrollViewer:
private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
lstCategories.Height = scrollViewer.ViewportHeight;
}
In my project, i have two ListBox. The first is populated from JSON file. So de user select one item and click "Add" button, the item is added to ListBox 2.
But, I have an "amount" TextBox and I want that its content appears on ListBox 2 also.
Following example:
How is it my screen:
(source: infassteste.url.ph)
As it should be:
(source: infassteste.url.ph)
My code:
private void AddProd(object sender, RoutedEventArgs e)
{
if (ListBoxx.SelectedItem != null)
{
Fields fi = (Fields)this.ListBoxx.SelectedItem;
ListBoxx2.Items.Add(fi);
}
else
{
MessageBox.Show("Selecione um item para adicionar!");
}
}
My XAML:
<ListBox Name="ListBoxx"
FontSize="30"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Items}" Margin="10,10,10,411" BorderBrush="Red">
<ListBox.Background>
<SolidColorBrush Color="#FFC3C3C3" Opacity="0.51"/>
</ListBox.Background>
<!-- Template to display each item -->
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding FNome}"/>
<TextBlock Grid.Column="1" Text="{Binding FEstado}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="ListBoxx2"
FontSize="30"
HorizontalContentAlignment="Stretch"
Margin="10,311,10,10" BorderBrush="Red">
<ListBox.Background>
<SolidColorBrush Color="#FFC3C3C3" Opacity="0.51"/>
</ListBox.Background>
<!-- Template to display each item -->
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding FNome}"/>
<TextBlock Grid.Column="1" Text="{Binding FEstado}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Add a property to your Fields class to hold the quantity. Lets call it Quantity. The Modify the ListBoxx2 ItemTemplate like this.
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding FNome}"/>
<TextBlock Grid.Column="1" Text="{Binding FEstado}"/>
<TextBlock Grid.Column="2" Text="{Binding Quantity}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
Then in the click event of the Button, add the following..
private void AddProd(object sender, RoutedEventArgs e)
{
if (ListBoxx.SelectedItem != null)
{
Fields fi = (Fields)this.ListBoxx.SelectedItem;
fi.Quantity = txtQuantity.Text; // SET THE Quantity..
ListBoxx2.Items.Add(fi);
}
else
{
MessageBox.Show("Selecione um item para adicionar!");
}
}
Then the value in the textbox is added to the object and it is bound to the ListBox ItemTemplate
Define new property to you class that is displayed in ListBox and bound and bind it to third TextBlock. (FNome, FEstado).
In your AddProd method you can get instance of your custom class from ListBoxx.SelectedItem and change your new property.