How to get the value from my picker? Xamarin forms - c#

I have trouble to get out the selected string from my picker.
This is my code:
XAML:
<Picker x:Name="thePicker" >
<Picker.Items>
<x:String>info1</x:String>
<x:String>info2 </x:String>
</Picker.Items>
</Picker>
CODE:
thePicker.SelectedIndex = 1; //here is the problem i suppose, any idea what i should type?
var ourPickedItem = thePicker.Items[thePicker.SelectedIndex];
Now I only get the value "info1" even if i select number 2. It has something to do with the SelectedIndex so it only picks the "1", but I am not sure how to write the code to make it work for the selected item.

You should take a look at this:
picker.SelectedIndexChanged += (sender, args) =>
{
if (picker.SelectedIndex == -1)
{
boxView.Color = Color.Default;
}
else
{
string colorName = picker.Items[picker.SelectedIndex];
boxView.Color = nameToColor[colorName];
}
};
Otherwise in new Xamarin Forms Release 2.3.4 exists the
Bindable Picker
you can set an ItemSource
<Picker ItemsSource="{Binding Countries}" />
and Bind the SelectedIndex property
SelectedIndex="{Binding CountriesSelectedIndex}"

XLabs is already providing it. Here is an example:
<ContentPage x:Class="XLabs.Samples.Pages.Controls.ExtendedPickerPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XLabs.Forms.Controls;assembly=XLabs.Forms"
Title="Picker">
<ContentPage.Content>
<StackLayout x:Name="myStackLayout">
<Label Text="Xaml:" />
<controls:ExtendedPicker x:Name="myPicker"
DisplayProperty="FirstName"
ItemsSource="{Binding MyDataList}"
SelectedItem="{Binding TheChosenOne}" />
</StackLayout>
</ContentPage.Content>

Related

MAUI add control element to GUI using MVVM

I use the community tool MVVM for my current MAUI project.
I would like to dynamically add controls like an entry to the GUI during runtime. I would like to do that from the ViewModel.
Using the toolkit, it is of course very easy to provide and interact with functions and properties. Unfortunately I haven't found a way to directly access a StackLayout or something similar.
I tried giving the VerticalStackLayout property x:name (in my xaml document) a name and then accessing it. This works from the code-behind, but not from the ViewModel itself.
I expected that with in the viewModel for example my StackLayout is displayed and then I can execute the following.
stackLayout.Add(new Label { Text = "Primary colors" }));
Furthermore I tried to provide a binding to the property x:name.
x:Name="{Binding StackLayout}
In the ViewModel I then tried to provide the property.
[ObservableProperty]
VerticalStackLayout stackLayout;
To clarify: the ViewModel doesn't know about the View, but the View DOES know about the ViewModel.
Thus, the view's code behind can do what is needed.
If the View doesn't already have a property holding the viewmodel, then add to code behind:
private MyVM VM => (MyVM)BindingContext;
That defines a VM property, so you can do VM.MyDictionary[someKey] or similar.
If you need to access VM in constructor BEFORE setting BindingContext,
then edit question, to show how BindingContext is set currently.
Yes, you can use MVVM to achieve this.
A simple method is to use Bindable Layouts to achieve this.
Please refer to the following code:
1.create a viewmodel for current page
MyViewModel.cs
public class MyViewModel
{
public int index = 0;
public ObservableCollection<Data> Items { get; set; }
public ICommand AddItemCommand => new Command(addItemMethod);
private void addItemMethod(object obj)
{
index++;
Items.Add(new Data { FileName ="File " + index});
}
public MyViewModel()
{
Items = new ObservableCollection<Data>();
}
}
Data.cs
public class Data
{
public string FileName { get; set; }
}
2.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:mauiapp="clr-namespace:MauiAddViewApp116"
x:Class="MauiAddViewApp116.MainPage"
x:Name="mainpage"
>
<ContentPage.BindingContext>
<mauiapp:MyViewModel></mauiapp:MyViewModel>
</ContentPage.BindingContext>
<ScrollView>
<VerticalStackLayout
Margin="10"
VerticalOptions="StartAndExpand">
<Button Text="Add item" Command="{Binding AddItemCommand}"></Button>
<StackLayout BindableLayout.ItemsSource="{Binding Items}" Orientation="Vertical">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label HorizontalOptions="Fill" Text="{Binding FileName}" FontSize="Large" HeightRequest="38" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</VerticalStackLayout>
</ScrollView>
</ContentPage>
I have found a solution to my problem.
As you have advised me, I have put it around. I use the code-behind of my view to access the StackLayout.
1. MainPage.xaml
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center"
x:Name="VStackLayout">
</VerticalStackLayout>
</ScrollView>
With the property x:name I can access the VS layout from the code behind.
2. MainPage.xaml.cs
Dictionary<string, object> keyValuePairs = new Dictionary<string, object>();
public MainPage(MainPageViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
foreach (var item in viewModel.KeyValues)
{
if (item.Value == "String")
{
keyValuePairs.Add(item.Key, "");
var entry = new Entry {
Placeholder = item.Key,
ClassId = item.Key,
Text = (String)keyValuePairs.Where(k => k.Key == item.Key).First().Value
};
VStackLayout.Add(entry);
}
else if (item.Value == "Boolean")
{
keyValuePairs.Add(item.Key, true);
Label label = new Label { Text = item.Key};
var toogle = new Switch
{
IsEnabled = true,
ClassId = item.Key,
IsToggled = (Boolean)keyValuePairs.Where(k => k.Key == item.Key).First().Value
};
HorizontalStackLayout views = new HorizontalStackLayout();
views.HorizontalOptions = LayoutOptions.StartAndExpand;
views.VerticalOptions = LayoutOptions.Center;
views.Add(label);
views.Add(toogle);
VStackLayout.Add(views);
}
}
Here the Dic in the ViewModel is accessed and then the GUI is created from it.
Unfortunately, the access to the content of the elements (entries) does not work yet. I would like to see how to write the content in a Dictonary. The binding at this point does not work yet. Does anyone have an idea?
First of all, I want to answer that nothing is stopping you from passing a reference of your StackLayout as CommandParameter to your Command in the ViewModel. Write this:
[RelayCommand]
void Add(StackLayout myLayout)...
And just pass the reference in the XAML.
However, there are very few situations that justify this.
None of those situations are "to customize the GUI".
You need to learn how to use DataTemplates, DataTriggers, Styles, EventToCommandBehaviors, Gestures, ControlTemplates, Validators and ValueConvertors.
This will cover your basic needs for accessing the View and its elements.

How to refresh a collectionView when click on button

I would like to refresh my collectionView When I click on a button and make the app continue to run while the collectionview is filled
Here is my Xaml :
<CollectionView x:Name="WordSList" ItemsLayout="Vertical" >
<CollectionView.ItemTemplate >
<DataTemplate>
<StackLayout >
<Label Text="{Binding Word1}" />
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
What I would like :
List<MyClass> MyWordsList;
ObservableCollection<MyClass> datasource;
int ndx = 100;
public void OnRefreshing_MyCollection_View()
{
// refresh the collection view without making the app waiting for it to be filled
MyWordsList = await mywordsdatabase.GetWords();
WordSList.ItemsSource = datasource = new ObservableCollection<MyClass (MyWordsList.Take(ndx));
}
Thanks for your help
you can use the RefreshView of Xamarin.Forms like in this example of James Montemagno:
https://devblogs.microsoft.com/xamarin/refreshview-xamarin-forms/
You have a property IsRefreshing which you set on the beginning of your refreshing-method. After refreshing you set it to false.
<RefreshView IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}">
<CollectionView ItemsSource="{Binding Items}">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<!-- Add ItemTemplate Here -->
</CollectionView>
</RefreshView>
void ExecuteRefreshCommand()
{
Items.Clear();
Items.Add(new Item { Text = "Refreshed Data", Description = "Whoa!" });
// Stop refreshing
IsRefreshing = false;
}
In the example its an drag-down-element in your collection view. But you can also do this with a button event and an async method to load your data.

Xamarin: Change value of GridItemLayout in codebehind

<CollectionView Grid.Row="2" Grid.Column="0" x:Name="collectionViewItemsLayout" ItemsSource="{Binding BaseCustomersCards}" ItemTemplate="{StaticResource CustomerCardTemplateSelector}" >
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="5" />
</CollectionView.ItemsLayout>
</CollectionView>
I've got this collectionview but am attempting to change the span for phone-tablet. Default is 5 but phone value should be 3.
var idiom = DeviceInfo.Idiom;
if (idiom == DeviceIdiom.Phone)
{
collectionViewItemsLayout.SetValue(GridItemsLayout.SpanProperty, 3);
}
I made this in the code-behind to change it, the method triggers but doesn't change anything. I've tried to put the 3 as a string and as pure value. I've also attempted to put the x:name in the attribute but it cannot go there.
Works by creating a new GridItem and setting it instead
if (idiom == DeviceIdiom.Phone)
{
var grid = new GridItemsLayout(ItemsLayoutOrientation.Vertical)
{
Span = 3,
};
collectionViewItemsLayout.SetValue(CollectionView.ItemsLayoutProperty, grid);
}

How to pass a ListView value to another page using an item button click?

I have ListView which is populating Id based on username:
<Entry Text="{Binding username, Mode=TwoWay}"/>
<Button Command="{Binding SearchCommand}" />
<ListView ItemsSource="{Binding SearchId}"
HasUnevenRows="False"
x:Name="list"
ItemTapped="LstItems_OnItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<StackLayout Orientation="Vertical" >
<Label x:Name="ident"
Text="{Binding Id}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
private async void LstItems_OnItemTapped(object sender, ItemTappedEventArgs e)
{
var item = (UserDetail)e.Item;
var newpage = new Contact(item.Id);
await Navigation.PushAsync(newpage);
}
public Command SearchCommand
{
get
{
return new Command(async () =>
{
var employeesServices = new EmployeesServices();
SearchId= await employeesServices.GetUserDetailAsync(_username);
});
}
}
private List<UserDetail> _SearchId;
public List<UserDetail> SearchId
{
get { return _SearchId; }
set
{
_SearchId = value;
OnPropertChanged();
}
}
I can successfully pass the id to another page, now I have another issue, I can only pass Id from listview to another page when I tap on the listview.
Is it possible if I click on button Id directly pass on next page without tap on ListView?
You can use SelectedItem inside your List View like this:
<ListView ItemsSource="{Binding SearchId}" HasUnevenRows="False" x:Name="list" ItemTapped="LstItems_OnItemTapped" SelectedItem="{Binding SelectedListItem,Mode=TwoWay}">
After using this you will have current selected item of your list and you can pass that to any page!
Hope this may solve your issue.

Xaml - Unable to get item Name in class file (inside listView)

I'm making an XAML app and i'm having some issues binding my xaml file with its class .
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="App.TestXaml"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Content>
<ListView x:Name="ListTest">
<Label FontSize="12" x:Name="test1"/>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Spacing="5" Orientation="Horizontal">
<Label FontSize="14" x:Name="test2"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
in my cs file testXaml.cs thats linked to this xaml :
test1.FontSize = 20 ;
// Works - it detects the test1 variable for that label.
In my listview however , when i try to access the label named test2 , it does not detect it in my cs file
test2.FontSize = 24 ;
// The class does not detect test2 (The name test2 does not exist in the current context)
Any idea how to fix this or set up a value for the list items from my cs file ?
You can't access the controls that are inside the itemtemplate by their name.
You have 2 options
Use a property in the binded item for the font size and bind it to the fontsize of the control. I think this is more clean solution.
Use triggers as described by #ed
But i would definetely go with Mvvm
If you need more programmatic decision here is another option.
Derive MyViewCell from ViewCell and your xml will look the same except MyViewCell and I added binding to show something. You will need to add local code reference to use MyViewCell in xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:ButtonRendererDemo;assembly=ButtonRendererDemo"
x:Class="ButtonRendererDemo.ListCellAccessPage">
<ContentPage.Content>
<ListView x:Name="ListTest" HasUnevenRows="true">
<Label FontSize="12" x:Name="test1"/>
<ListView.ItemTemplate>
<DataTemplate>
<local:MyViewCell>
<StackLayout Spacing="5" Orientation="Horizontal">
<Label FontSize="14" x:Name="test2" Text="{Binding Name}"/>
</StackLayout>
</local:MyViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
Now here is the code behind
namespace ButtonRendererDemo
{
public partial class ListCellAccessPage : ContentPage
{
MyListItem[] listItems = new MyListItem[]
{
new MyListItem { Name= "1" },
new MyListItem{Name= "2" },
new MyListItem{Name= "3" },
new MyListItem{Name= "4" }
};
public ListCellAccessPage()
{
InitializeComponent();
test1.Text = "List";
ListTest.ItemsSource = listItems;
}
}
class MyListItem
{
public string Name { get; set; }
}
class MyViewCell : ViewCell
{
protected override void OnChildAdded(Element child)
{
base.OnChildAdded(child);
var label = child.FindByName<Label>("test2");
if (label != null)
{
label.PropertyChanged -= Label_PropertyChanged1;//unsubscribe in case of cell reuse
label.PropertyChanged += Label_PropertyChanged1;
}
private void Label_PropertyChanged1(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var label = sender as Label;
if ((label != null) && (e.PropertyName == "Text"))
{
if (label.Text == "3")
label.FontSize = 48;
else
label.FontSize = 14;//in case of cell reuse
}
}
}
}
So, it examines the content of your cell and if it is confirms certain condition (in this case name is 3) it sets the font. Here is the screenshot

Categories