Why does the "static" keyword makes the difference in CollectionView? - .net-6.0

I've built a CollectionView with Maui, this is the code:
//if I do remove this "static" keyword the code doesn't work anymore.
public static ObservableCollection<Test> _testing = new
ObservableCollection<Test>();
public ObservableCollection<Test> Testing
{
get => _testing;
set { _testing = value; OnPropertyChanged(nameof(Test)); }
}
and here I add new data:
public IndexTest()
{
Testing.Add(new Test{ name = "hello" });
}
This is the xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="OpenToursNative.Index"
xmlns:vm="clr-namespace:OpenToursNative.Test">
<ContentPage.BindingContext>
<vm:IndexTest/>
</ContentPage.BindingContext>
<CollectionView ItemsSource="{Binding Test}">
<CollectionView.ItemTemplate>
<DataTemplate>
<LabelText="{Binding name}"></Label>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
If I run this code, everything works without problem, but, if I remove the "static" keyword on the ObservableCollection it doesn't log anything anymore.
Through the debugging it seems that every time I load the page the List counts 1 item, then after it loaded the List counts is 0.
I've wasted few days looking for a reason why it wasn't working at the beginning, adding "static" did the trick.
Can anyone explain to me why does the "static" keyword make this change?
Thank you all.

The meaning of static is fundamental to understanding c# code. It means that all the instances of that class share a single member (variable) with that name. Therefore, each time you navigate, that variable still contains whatever it had before.

Related

developing a picker that pops out from the selected frame downward in xamarin forms

I have a design i'm trying to implement. Although I've ben working with Rg.plugin for a while trying out different animations and entry behaviour but it's always covering the whole screen.
However, the current design I'm working with is different.
here is the image
Please does anyone have an idea how I can achieve this using xamarin forms please.
Any help will be appreciated.
Note, I already have the design in place using pancakeView and Rg.plugin to pop it out on click. However, the positioning is what I haven't been able to achieve. though I've not written any code for it yet cos I prefer to do my research right.
Please I need anyone to point me to the right path or how to achieve this.
thanks in advance
According to your screenshot, you can do custom control using entry and ListView to get it, there is also one custom control that you can take a look:
Installing Xamarin.Forms.EntryAutoComplete
Uisng this custom control like:
<ContentPage
x:Class="demo3.listviewsample.Page36"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customControl="clr-namespace:EntryAutoComplete;assembly=EntryAutoComplete">
<ContentPage.Content>
<StackLayout>
<customControl:EntryAutoComplete
ItemsSource="{Binding Countries}"
MaximumVisibleElements="5"
Placeholder="Enter country..."
SearchMode="{Binding SearchMode}"
SearchText="{Binding SearchCountry}"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
public class dialogvidemodel:ViewModelBase
{
private string _searchCountry = string.Empty;
private SearchMode _searchMode = SearchMode.Contains;
public string SearchCountry
{
get => _searchCountry;
set
{
_searchCountry = value;
RaisePropertyChanged("SearchCountry");
}
}
public List<string> Countries { get; } = new List<string>
{
"African Union",
"Andorra",
"Armenia",
"Austria",
"Togo",
"Turkey",
"Ukraine",
"USA",
"Wales"
};
public SearchMode SearchMode
{
get => _searchMode;
set
{
_searchMode = value;
RaisePropertyChanged("SearchMode");
}
}
}
More detailed info, please take a look:
https://github.com/krzysztofstepnikowski/Xamarin.Forms.EntryAutoComplete

Xamarin Forms ListView not Updating from ObservableCollection

The ListView of my XAML file is being filled with a ViewModel that has an ObservableCollection from a service but the ListView is not showing the information. I already check that the service is returning the correct information.
This is the code of my XML file :
<?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="XAMLUnit1.Views.NetflixRoulettePage" Title="Movie">
<ContentPage.Content>
<StackLayout>
<AbsoluteLayout>
<BoxView Color="Gray" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1"></BoxView>
<SearchBar x:Name="searchBar"
Placeholder="Search By Actor's name"
PlaceholderColor="White"
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0.1,0.1,1,1" SearchCommand="{Binding SearchMovieCommand}" ></SearchBar>
</AbsoluteLayout>
<ListView x:Name="ListOfMovies"
ItemsSource="{ Binding MovieList}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<ImageCell
ImageSource="{Binding poster_path, StringFormat='https://image.tmdb.org/t/p/w500{0}'}">
</ImageCell>
<StackLayout Orientation="Horizontal">
<TextCell Detail="{Binding title}"></TextCell>
<TextCell Detail="{Binding release_date}"></TextCell>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
This is the ViewModel that calls the service and it's uses its ObservableCollection as ItemsSource for the ListView :
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XAMLUnit1.Models;
using XAMLUnit1.ServiceImpl;
using XAMLUnit1.Services;
namespace XAMLUnit1.ViewModels
{
public class MovieRouletteViewModel
{
IMovieService service;
public ObservableCollection<Movie> MovieList { get; set; }
public ICommand SearchMovieCommand { get; set; }
public MovieRouletteViewModel()
{
service = new MovieServiceFinder();
SearchMovieCommand = new Command(GetMovies);
}
private void GetMovies()
{ var list= service.GetMovies("");
MovieList = list;
}
}
}
public partial class NetflixRoulettePage : ContentPage
{
MovieRouletteViewModel viewModel;
public NetflixRoulettePage()
{
InitializeComponent();
viewModel = new MovieRouletteViewModel();
BindingContext = viewModel;
}
private void SearchBar_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
Do not set the ObservableCollection to a new List it will break the binding. Clear the items from the list and add the new items to it.
public MovieRouletteViewModel()
{
service = new MovieServiceFinder();
SearchMovieCommand = new Command(GetMovies);
MovieList = new ObservableCollection<Movie>();
}
private void GetMovies()
{
var list= service.GetMovies("");
MovieList.Clear();
foreach(Movie movie in list)
{
MovieList.Add(movie);
}
}
There is a few things i want to note on this problem, though mainly as the comments rightly illuminate, replacing the collection with a new collection breaks the bindings. hence why its not updating.
However there are several solutions to consider.
Implement INotifyPropertyChanged on the collection and replace the whole collection like you are doing, This will work fine. However it does resets the scroll and basically recalculates everything from scratch. Some would say this defeats the point of an ObservableCollection as they have their own built in notification plumbing, Yeah it does. Though, if you are replacing the whole Collection with a new collection then you are going to save on oodles of Cyclic calculations when compared to cleaRing and adding each item back individually Which will basically fire for every update
Call Clear and Add on each item individually.. If the collection hasn't changed much, you can even take it a step further by just comparing the 2 collections and updating whats needed. However once again, if the collections are dissimilar, then this approach is still expensive, and on a mobile device you want to minimize screen recalculations where ever possible .
Create a subclassed collection, implement your own replace/update, and Add/Remove range methods, and INotifyPropertyChanged giving you the best of both worlds, this will allow atomic modification of the collection and then you can fire property changed event once.
There are many examples of all these approaches online, its just worth noting clearing and adding items sometimes is not the best approach for mobile devices. It depends how big your collections are, how much is changing and why.
If it is a completely different list, then replacing the collection is fine in my opinion.
If its the same list, then well you may want to compare and modify to save on property changes
Anyway good luck
I disagree with other answers. You can set the Movies as many times as you want, this is not the problem.
The problem is just that your viewmodel doesn't implement INotifyPropertyChanged.
Your UI is just not notified when you set your ObservableCollection.
Your service command is actually replacing the ObservableCollection, you need to change your GetMovies() method to
var list = service.GetMovies("");
MovieList.clear();
foreach(Movie m in list)
{ MovieList.Add(m);}

Xamarin XAML variable scope not working like expected

I have a really odd problem with variable scopes. A Listview named "TodoListView" is defined via xaml, and it's ItemSource populated from a SQListe database. Works. Inside the ListView I have a ViewCell to display the data row-wise.
<ContentPage ... x:Class="JanitorPro.MainPage" ...>
<StackLayout>
<ListView x:Name="TodoListView" Margin="20" ItemSelected="OnListItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<Label Text="{Binding name}" VerticalTextAlignment="Center" HorizontalOptions="StartAndExpand" />
<Switch HorizontalOptions="End" IsToggled="{Binding done}" Toggled="DoneSwitchToggled"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
The codebehind looks like this (some irrelevant portions removed):
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
protected override async void OnAppearing()
{
base.OnAppearing();
// load Database
TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
}
async void OnReloadButtonClicked(object sender, EventArgs e)
{
Debug.WriteLine("Reload Button Click");
TodoListView.ItemsSource = await App.TodoDatabase.GetItemsAsync("SELECT * FROM [TodoItem]");
Debug.WriteLine("Reload done.");
}
async void OnListItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem != null)
{
await Navigation.PushAsync(new TodoItemPage
{
BindingContext = e.SelectedItem as TodoItem
});
}
}
private void DoneSwitchToggled(object sender, ToggledEventArgs e)
{
// TodoItem i = null;
TodoItem i = TodoListView.SelectedItem;
if (i != null)
{
Debug.WriteLine("Toggle: {0}", i.id);
}
}
}
}
The oddity has two stages. Before I inserted the DoneSwitchToggled event handler, every occurrance of TodoListView.ItemsSource got a red underline under TodoListView and a hint that "The name TodoListView does not exist in the current context". OK, I thought that VS was not smart enough to find a definition in the xaml file, because, despite of the warning, the program compiled and ran fine. TodoListView gets initialized and does correctly display the rows of the underlying database, so it does clearly exist at runtime.
Things went wild when I added the DoneSwitchToggled event handler to both XAML and the codebehind. All the sudden the program won't compile any longer but bail out with a CS0103 error "The name "TodoListView" does not exist in the current context". The error appears three times, with the line numbers pointing to the other occurrances of TodoListView in onAppearing() and OnReloadButtonClicked(). Huh? How the heck can the addition of a variable reference in an event handler render that variable invalid in completely different methods? OK, there was something fishy with the variable before (still don't know what ...), but it worked. Now it doesn't any more, whch doesn't make any sense for me. Furthermore, if I comment out the offending line in the DoneSwitchToggled event handler, and insert a dummy definition for i, like so:
TodoItem i = null;
// TodoItem i = TodoListView.SelectedItem;
everything is like before, VS still underlines the other appearances of TodoListView, but now the program builds and runs ok again.
Anyone who can explain this effect, and show me how correct my code? I think the objective is clear: DoneSwitchToggled is supposed to write back the switch value into the database (and do some other processing not shown in my stripped down sample), and though the "sender" object is correctly set to reference my button, I found no way to access the underlying data binding, since ToggledEventArgs unfortunately does seem to only pass the switch position "true" or "false", but - unlike the OnListItemSelected event handler - not pass any reference to the bound row through the second argument. So my idea was to use ListView.SelectedItem for this purpose.
Finally I figured it out myself. This seems to be a glitch in VS 2017. There is nothing wrong with TodoListView, so error CS0103 is misleading nonsense.
What VS really means is an error CS0266. TodoListView is defined by a generic list
List<TodoItem>
to access SelectedItem i need to typecast it:
TodoItem i = (TodoItem)TodoListView.SelectedItem;
This added, all errors are gone, app code builds OK.
Btw, unfortunately this approach to get at the item where the Switch has been flipped has proven not working, TodoListView does always return null for SelectedItem, seems that the ListView control doesn't see the button press. Need to find a different way to find the list item beneath the switch to get at the bound row id.

How to preview an ItemTemplate in the XAML designer

Say I have a simple ItemTemplate for a ListView
<ListView ItemsSource="{x:Bind ListItems}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TextItem">
<TextBlock Text="{x:Bind Item}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml
When I run the app, I get a ListView populated with TextBlocks - one for each item in ListItems (which is a behind defined variable). However, in the XAML designer, I see nothing.
So, is there a way to preview the ItemTemplate/DataTemplate in the XAML designer, with a set number of placeholder TextBlocks with placeholder text replacing the Text="{x:Bind Item}"? Or just preview a single TextBlock?
This is not a duplicate of
Design View of a DataTemplate in a ResourceDictionary in Visual Studio - because I can't use Blend. Everything I've looked up that says use Blend in a certain way merely gives me the message 'This feature is not available for projects targeting "Windows 10 Fall Creators Update (10.0; Build 16299)"'
Can I preview my DataTemplate(s) in my collection-based controls in the designer? - well, perhaps it is, but I don't understand the accepted answer, which is now several years old. An answer containing an example tailored to my question would be really helpful.
Microsoft actually posted a (semi helpful) tutorial on this in April of this year: link
Requirements
Whatever you want to display, you will need this at the top of your XAML code (should be there by default if you created the page using VS's templates):
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
A simple list of strings
For something simple like a list of strings, something like this will work just fine (taken from the above mentioned MSDN page):
<ListView>
<d:ListView.Items>
<system:String>Item One</system:String>
<system:String>Item Two</system:String>
<system:String>Item Three</system:String>
</d:ListView.Items>
</ListView>
Display something more complex
If you want to use a data source from somewhere else in your code, it gets a little more ugly:
First you need to set your data context (usually done at the page level, but you can also add it just on the element where you want to use it):
d:DataContext="{d:DesignInstance
yournamespace:YourDataSourceClass,
IsDesignTimeCreatable=True}"
And on the ListView add:
d:ItemsSource="{Binding data}"
If you are using an ItemTemplate, you will need to add "d:" variants for everything you want to pull from your data source.
Full example code:
XAML:
<Page
x:Class="exampleApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:exampleApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:DesignTimeData,
IsDesignTimeCreatable=True}"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Grid>
<ListView ItemsSource="{x:Bind ListItems}" d:ItemsSource="{Binding DummyData}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TextItem">
<TextBlock Text="{x:Bind Item}" d:Text="{Binding Item}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
</Page>
C#:
using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
namespace exampleApp {
public sealed partial class MainPage : Page {
public ObservableCollection<TextItem> ListItems;
public MainPage() {
this.InitializeComponent();
//populate you ListItems list
}
}
public class TextItem {
public string Item { get; set; }
}
public class DesignTimeData {
public ObservableCollection<TextItem> DummyData { get; set; } = new ObservableCollection<TextItem> {
new TextItem { Item = "foo" },
new TextItem { Item = "bar" },
new TextItem { Item = "bla" },
new TextItem { Item = "blubb" }
};
}
}
Remarks
It seems you have to use "Binding" rather than "x:Bind" for the design-time bindings. From what I understand, x:Bind works using code generated at compile time, which is not executed by the XAML designer
When you change your Binding declarations or data source code, you will have to recompile to see the changes in the designer. Other design changes will reflect in real time
Binding is more limited than x:Bind in some regards (notably you cannot bind to class methods using Binding, only x:Bind). Keep that in mind when writing your design-time data
Sadly some of the more convenient ways of getting design-time data into your app are WPF and Xamarin only (Blends' data window, data declared in XAML file, and especially auto-generated sample data)
(Everything was tested using Visual Studio 2019 version 16.10.3.)

Reading from local file WP7

I am trying to read a text file that I have included with the project folder itself in a separate folder. I am trying to read this text file and then add each line to a list, each line as a separate item in the list, then I am looking to bind it to a listbox and each listbox item (each line previously) would be a hyperlink in the listbox. It's been very frustrating since the app just freezes every single time as soon as the code starts executing at runtime. What could be the problem?
I tried searching here a lot. I tried several of the similar problems' solutions but no use.
Code:
public partial class Page2 : PhoneApplicationPage
{
public Page2()
{
InitializeComponent();
// Will contain the names of malls added through a text file
List<string> Mall_List = new List<string>();
using(StreamReader reader = new StreamReader("/Mall_List/Mall_List.txt"))
{
while(reader.Peek() >= 0)
{
Mall_List.Add(reader.ReadLine());
}
reader.Close();
}
Malllist.ItemsSource = Mall_List;
}
}
XAML:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox Height="426" HorizontalAlignment="Left" Margin="6,6,0,0" Name="Malllist" VerticalAlignment="Top" Width="444">
<ListBox.ItemTemplate>
<DataTemplate>
<HyperlinkButton Name="MallNameLinkButton"
Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
Instead of just reading from "local file",
using(StreamReader reader = new StreamReader("/Mall_List/Mall_List.txt"))
you should know WP7 has Isolated Storage, for every app.
Please make sure you are familiar with it by reading following article:
http://www.windowsphonegeek.com/tips/all-about-wp7-isolated-storage--intro-to-isolated-storage
More precisely, reading files is described in following article:
http://www.windowsphonegeek.com/tips/all-about-wp7-isolated-storage-read-and-save-text-files

Resources