I'm trying to understand the issue of this, bit it doens't work at all...
So what I'm trying to do is to make an menu with a listview as it's content. Now, I have the view, with binding properties, and I have a class in which I make the list. Here is the code:
public class MenuList
{
public ImageSource Icon { get; set; }
public String Title { get; set; }
// Initialize the Pokemon object thing :p
//public MenuList() { }
// Here we make the pokemon list :D
public static List<MenuList> GetMenuItems()
{
return new List<MenuList>()
{
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Radio" },
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Shout/request lijn" },
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Rooster" },
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Geschiedenis" },
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Medewerkers" },
new MenuList() { Icon="RadioApp.Assets.Icons_menu.account.png", Title="Over deze app" },
};
}
}
<ListView x:Name="menuItemsLVW" Grid.Row="0">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout Orientation="Horizontal">
<Image Source="{Binding Icon}" />
<Label Text="{Binding Title}" TextColor="Black" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now, I get the errors : [0:] Binding: 'Icon' property not found on 'RadioApp.Model.MenuList', target property: 'Xamarin.Forms.Image.Source'
I have no idea why it returns the error, because as far as I can see, tis should work?
The class I generate the page:
public partial class Menu : ContentPage
{
private List<MenuList> lstOfAllMenuItems;
public Menu()
{
InitializeComponent();
lstOfAllMenuItems = MenuList.GetMenuItems();
menuItemsLVW.ItemsSource = lstOfAllMenuItems;
//Debug.WriteLine(lstOfAllMenuItems[0].Title);
//Debug.WriteLine(lstOfAllMenuItems[0].Icon);
}
}
If I do the debug, it just returns the correct item...
Related
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>
I'm trying to make CollectionView in Shell but it's not updating.
I have one view model connected to Page and AppShell but when I update Collection view only page is updationg.
`public class AppShellViewModel : INotifyPropertyChanged
{
public Command Load { get; }
public ObservableCollection<ListData> _lists { get; set; }
public ObservableCollection<ListData> Lists
{
get { return _lists; }
set
{
_lists = value;
OnPropertyChanged();
}
}
public AppShellViewModel()
{
Lists = new ObservableCollection<ListData>()
{
new ListData(){id=0,name="test",UserId=0},
new ListData(){id=1,name="test1",UserId=1},
new ListData(){id=2,name="test2",UserId=2},
new ListData(){id=3,name="test3",UserId=3},
new ListData(){id=4,name="test4",UserId=4}
};
Load = new Command(async () => await GetUserLists());
}
async Task GetUserLists()
{
for (int i = 5; i < 15; i++)
{
Lists.Add(new ListData {id=i, name=$"test{ i }", UserId=i });
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}`
Then i have App Shell Collection View
`<Shell.FlyoutContent>
<StackLayout BackgroundColor="#34495e">
<Label Text="YOUR LISTS" FontSize="50" />
<CollectionView ItemsSource="{Binding Lists}" >
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:ListData">
<Label Text="{Binding name}"
LineBreakMode="NoWrap"
FontSize="13" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</Shell.FlyoutContent>`
And There is Page CollectionView
`<?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="ToDoApp.Views.AboutPage"
xmlns:model="clr-namespace:ToDoApp.Models">
<StackLayout>
<Button Text="Load" Command="{Binding Load}"/>
<Label Text="{Binding error}"/>
<CollectionView ItemsSource="{Binding Lists}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" x:DataType="model:ListData">
<Label Text="{Binding name}"
LineBreakMode="NoWrap"
FontSize="13" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>`
before update it looks like this
Page before update
Shell before update
And after update the only what changed is content page and shell is the same as before
Page after update
Shell after update
Related to Jason's comment.
WON'T CHANGE TOGETHER
NOT the same instance - BindingContexts similar to these:
// In AppShell.xaml.cs.
public AppShell()
{
InitializeComponent();
BindingContext = new AppShellViewModel();
}
// In AboutPage.xaml.cs.
public AboutPage()
{
InitializeComponent();
BindingContext = new AppShellViewModel();
}
GOOD (SHARED BETWEEN TWO PLACES)
BindingContexts are SAME instance:
// In AppShellViewModel.cs.
public class AppShellViewModel ...
{
private static AppShellViewModel _it;
public static AppShellViewModel It
{
get {
if (_it == null)
_it = new AppShellViewModel();
return _it;
}
}
}
// In AppShell.xaml.cs.
public AppShell()
{
InitializeComponent();
BindingContext = AppShellViewModel.It;
}
// In AboutPage.xaml.cs.
public AboutPage()
{
InitializeComponent();
BindingContext = AppShellViewModel.It;
}
I am learning Xamaring forms , I want to do 4 content pages. One will display my to do listand images.
I would like to know if there is a way to charge my todo list before going to the last page from any of my 3 pages.
Knowing that I am going through pages like this :
var page = new LastPage();
MainView.Content = page.Content;
Thanks for your help
Do you want to achieve the result like following GIF?
If so, you need achieve it by MVVM and INotifyPropertyChanged
First of all, you should create a model to achieve the INotifyPropertyChanged.
public class MyModel: INotifyPropertyChanged
{
string name;
public string Name
{
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Image");
}
}
get
{
return name;
}
}
string count;
public string Count
{
set
{
if (count != value)
{
count = value;
OnPropertyChanged("Count");
}
}
get
{
return count;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then you need the ViewModel to push the data.
public class MyViewModel
{
public ObservableCollection<MyModel> myModels { get; set; }
public MyViewModel() {
myModels = new ObservableCollection<MyModel>();
myModels.Add(new MyModel() { Count = "0", Name = "test1" });
myModels.Add(new MyModel() { Count = "1", Name = "test2" });
myModels.Add(new MyModel() { Count = "2", Name = "test3" });
}
}
In the First page and end page, you should binding same viewmodel that use bindingcontext like following code format.
MainPage.xaml
<StackLayout>
<!-- Place new controls here -->
<Button Text="Next" Clicked="Button_Clicked"></Button>
<ListView x:Name="mylistview" ItemsSource="{Binding myModels}" HasUnevenRows="True" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout>
<Label Text="{Binding Name}"
FontAttributes="Bold"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="Center" />
<Label Text="{Binding Count}"
LineBreakMode="WordWrap"
HorizontalOptions="Center" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
MyViewModel viewModel;
public MainPage()
{
InitializeComponent();
viewModel = new MyViewModel();
BindingContext = viewModel;
}
private void Button_Clicked(object sender, EventArgs e)
{
Navigation.PushAsync(new Page1(viewModel));
}
}
Here is my demo, you can download it.
https://github.com/851265601/XFormsMvvmChange
Here is a helpful article about it, you can refer to it.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
This question is very similar to Picker not showing value in Xamarin.Forms on Windows Phone and UWP however there are some different aspects to this issue. Also the workaround selected as an answer to that question does not work in this case.
I have a ListView with an ItemTemplate that contains a ContentView. The ContentView contains a bindable picker. When the picker is selected the selected value is not displayed. This is an obvious bug because (1) you can resize the window and the value will appear and (2) this problem only occurs on UWP...iOS and Android versions are fine.
Here is the simplified issue. First the main window XAML...
<StackLayout Padding="10,40">
<Label>First List</Label>
<ListView Margin="20" x:Name="listView1" ItemsSource="{Binding ItemContexts}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<local:ContentView1
QuantityRows="{Binding Source={x:Reference listView1}, Path=BindingContext.QuantityRows}"
/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label>Second List</Label>
<ListView Margin="20" x:Name="listView2" ItemsSource="{Binding ItemContexts}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Picker x:Name="quantity" ItemsSource="{Binding Source={x:Reference listView2}, Path=BindingContext.QuantityView}" ItemDisplayBinding="{Binding FullName}"
SelectedItem="{Binding QuantityRow, Converter={StaticResource QuantityValueConverter}}"
WidthRequest="200">
</Picker>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label>End</Label>
</StackLayout>
There are two list views. The second one works fine and the first one illustrates the problem.
Here is the XAML for the content view...
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinFormsBench.ContentView1" x:Name="contextView1">
<ContentView.Content>
<StackLayout Orientation="Horizontal">
<Picker x:Name="quantity" ItemsSource="{Binding Source={x:Reference contextView1}, Path=QuantityRows}" ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding QuantityRow}"
WidthRequest="200">
</Picker>
</StackLayout>
</ContentView.Content>
</ContentView>
Code behind for content view...
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ContentView1 : ContentView
{
public static BindableProperty QuantityRowsProperty = BindableProperty.Create("QuantityRows", typeof(ObservableCollection<QuantityRow>), typeof(SummaryDetailView), null, BindingMode.TwoWay, null);
public ObservableCollection<QuantityRow> QuantityRows
{
get
{
return GetValue(QuantityRowsProperty) as ObservableCollection<QuantityRow>;
}
set
{
SetValue(QuantityRowsProperty, value);
}
}
public ContentView1 ()
{
InitializeComponent ();
}
}
}
Finally here is the view model...
public class QuantityRow
{
public int ID { get; set; }
public string Name { get; set; }
}
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
QuantityRows = new ObservableCollection<QuantityRow>();
QuantityRows.Add(new QuantityRow() { ID = 1, Name = "Length" });
QuantityRows.Add(new QuantityRow() { ID = 2, Name = "Diameter" });
QuantityRows.Add(new QuantityRow() { ID = 3, Name = "Temperature" });
QuantityRows.Add(new QuantityRow() { ID = 4, Name = "Pressure" });
QuantityRows.Add(new QuantityRow() { ID = 5, Name = "Angle" });
}
public ObservableCollection<QuantityRow> QuantityRows { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
}
}
I am using Xamarin Forms 2.3.4.247 which is the last stable release as of this writing. I tried the latest pre-release which might fix the problem..it is impossible to say because it created a host of other issues.
I've tried invalidating the layout of the content view when the picker's selected index changes, but again, this seems to fix the one problem and create a host of other problems.
So it seems that this will be fixed in the next stable release of Xamarin Forms. What I need now is an effective workaround so I can work with what I have.
I have tested your code and reproduced your issue. The problem is that your BindingContext of ContentView1 has never been set value. And this design is inconsequent, although it can work in the ios and andriod. For your requirement, you could realize it via custom ViewCell.
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinPickerTest.CustomViewCell">
<ViewCell.View>
<StackLayout>
<Picker x:Name="quantity" ItemsSource="{Binding QuantityRows}"
ItemDisplayBinding="{Binding Name}"
SelectedItem="{Binding SelectedQuantityRow}"
WidthRequest="200">
</Picker>
</StackLayout>
</ViewCell.View>
</ViewCell>
ViewModel.cs
public class ViewModel : ViewModelBase
{
public ViewModel()
{
QuantityRows = new ObservableCollection<QuantityRow>();
QuantityRows.Add(new QuantityRow() { ID = 1, Name = "Length" });
QuantityRows.Add(new QuantityRow() { ID = 2, Name = "Diameter" });
QuantityRows.Add(new QuantityRow() { ID = 3, Name = "Temperature" });
QuantityRows.Add(new QuantityRow() { ID = 4, Name = "Pressure" });
QuantityRows.Add(new QuantityRow() { ID = 5, Name = "Angle" });
}
private ObservableCollection<QuantityRow> quantityRows;
public ObservableCollection<QuantityRow> QuantityRows
{
get
{
return quantityRows;
}
set
{
quantityRows = value;
OnPropertyChanged();
}
}
private QuantityRow selectedQuantityRow;
public QuantityRow SelectedQuantityRow
{
get { return selectedQuantityRow; }
set
{
if (selectedQuantityRow != value)
{
selectedQuantityRow = value;
OnPropertyChanged();
}
}
}
}
public class QuantityRow : ViewModelBase
{
public int ID { get; set; }
public string Name { get; set; }
}
Usage
<ListView Margin="20" x:Name="listView1" ItemsSource="{Binding ItemContexts}" >
<ListView.ItemTemplate>
<DataTemplate>
<local:CustomViewCell/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And the above solution works well in ios and android.
When I run the app, I get two tabs (Im using Tabbed Page) but they are blank.
I have a somewhat complex ViewModel:
public partial class NowPlayingView
{
const string NowPlayingUrl = "http://api.myserver.com";
public static List<MoviesItem> MoviesLst { get; set; }
public NowPlayingView()
{
InitializeComponent();
BindingContext = new MoviesViewModel();
}
public class MoviesViewModel
{
public MoviesViewModel()
{
Action<Dictionary<string, string>> initAction = initialize;
initAction(new Dictionary<string, string>()
{
{"$format", "json"},
{"AccessKey", "f54tg5gf54g-fgs3452-324asdf4"},
{"CineplexLanguage", "en-us"}
});
}
public async void initialize(Dictionary<string,string> parameters)
{
var data = await (new ApiUtilities().CallGetData<MoviesNowPlaying>(NowPlayingUrl, "/api.svc/MoviesNowPlaying", parameters));
MoviesLst = data.d.results.Select(x => new MoviesItem() {Header = x.Title, Text = x.MediumPosterImageURL}).ToList();
}
}
public class MoviesItem
{
public string Header { get; set; }
public string Text { get; set; }
}
}
My XAML file look like this:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamPlex.MainCategories.NowPlayingView"
Title="Now Playing">
<ListView x:Name="MoviesListView" RowHeight="80" BackgroundColor="Transparent" ItemsSource="{Binding MoviesLst}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Vertical" Spacing="0" Padding="10">
<Label Font="Bold,20" Text="{Binding Header}" TextColor="Indigo"/>
<Label Font="16" Text="{Binding Text}" TextColor="Indigo"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
I checked the contents of MoviesLst and it contains plenty of data, any ideas what could be wrong?
I do not do a lot of MVC, but I believe the View should have this form:
public ActionResult Index() {
return View();
}
I would think your View should be structured something like...
public ActionResult Index() {
var data = await (new ApiUtilities().CallGetData<MoviesNowPlaying>(NowPlayingUrl, "/api.svc/MoviesNowPlaying", parameters));
var list = data.d.results.Select(x => new MoviesItem() {Header = x.Title, Text = x.MediumPosterImageURL}).ToList();
return View(list);
}
Again, though, I have only worked through the basic tutorials on MVC. I could be misunderstanding your model.
I don't see where you are posting your View anywhere. What I see appears to be the Model.