Xamarin MVVM Reference Content View and Pass Parameters Too - xaml

Working in xamarin forms. How do I reference a content view from a content page but also include passing information like below?
await Navigation.PushAsync(new SecondContentPage(new NextViewModel(FirstViewModel.id)));
I've tried xaml binding context but don't know where to go from there and it keeps giving me errors about a parameterless constructor.
xmlns:viewmodel="clr-namespace:Project.ViewModels"
<StackLayout>
<local:SecondContentView>
<local:SecondContentView.BindingContext>
<viewmodel:NextViewModel></viewmodel:NextViewModel>
</local:SecondContentView.BindingContext>
</local:SecondContentView>
</StackLayout>
I need the id passed along basically so that the list can run on the content view. thanks all
Updated - I created some new example code. I created one page to nest a second page with a Listview. Works great until I try to pass a parameter with x:Arguments or in a ViewModel constructor from first page to second page.
First page
**<StackLayout>
<StackLayout>
<Label Text="First Page Content"></Label>
</StackLayout>
<StackLayout>
<local:SecondContentView>
<local:SecondContentView.BindingContext>
<viewmodel:SecondViewModel>
<x:Arguments>102</x:Arguments>
</viewmodel:SecondViewModel>
</local:SecondContentView.BindingContext>
</local:SecondContentView>
</StackLayout>
</StackLayout>**
second page
<ContentView.Content>
<StackLayout>
<Label Text="Second Page"></Label>
<ListView x:Name="FirstListView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10">
<Label Text="{Binding pType}"></Label>
<Label Text="{Binding fDepartment}"></Label>
<Label Text="{Binding Description}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView.Content>
second page's code behind
SecondViewModel ViewModel;
public SecondContentView(SecondViewModel viewmodel)
{
InitializeComponent();
BindingContext = ViewModel = viewmodel;
FirstListView.ItemsSource = ViewModel.TypeList;
}
second page's view model
**public List<TypeModel> TypeList;
public SecondViewModel(int parameter)
{
var p = parameter;
TypeList = new List<TypeModel>()
{
new TypeModel { pType = 1, Title = "First Type", Description = "First Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
new TypeModel { pType = 2, Title = "Second Type", Description = "Second Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
new TypeModel { pType = 3, Title = "Third Type", Description = "Third Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"},
new TypeModel { pType = 4, Title = "Fourth Type", Description = "Fourth Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"}
};
}**

I was able to find a solution with another mocked example. The key was x:Argument and an override method.
Firstly, if you want to pass the simple type values we need to explicitly point out its type. Try this format:
<StackLayout>
<local:SecondContentView>
<local:SecondContentView.BindingContext>
<local:SecondViewModel>
<x:Arguments>
<x:Double>102</x:Double>
</x:Arguments>
</local:SecondViewModel>
</local:SecondContentView.BindingContext>
</local:SecondContentView>
</StackLayout>
Change its constructor like:
public SecondViewModel(double parameter)
{
var p = parameter;
TypeList = new List<TypeModel>()
{
new TypeModel { pType = 1, Title = "First Type", Description = "First Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
new TypeModel { pType = 2, Title = "Second Type", Description = "Second Description", Version = "9.9.9", fDepartment = 101 , Comments = "None"},
new TypeModel { pType = 3, Title = "Third Type", Description = "Third Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"},
new TypeModel { pType = 4, Title = "Fourth Type", Description = "Fourth Description", Version = "9.9.9", fDepartment = 102 , Comments = "None"}
};
}
Secondly, you are not passing parameters to the second view's constructor. Instead, you consumed the defualt null parameter constructor and set its binding context directly. So the public SecondContentView(SecondViewModel viewmodel) won't be triggered. We could consume the binding context directly in your second view like:
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
ViewModel = BindingContext as SecondViewModel;
FirstListView.ItemsSource = ViewModel.TypeList;
}

You would just set the BindingContext for SecondContentPage to NextViewModel.
In SecondContentPage.xaml.cs constructor:
public SecondContentPage(NextViewModel vm)
{
InitializeComponent();
localVm = vm;
BindingContext = localVm;
}
private readonly NextViewModel localVm;
Then you would access the property where FirstViewModel.Id is stored in NextViewModel in xaml using a binding. For this example we'll say the Id was stored in NextViewModel.Id. Example using Label:
<Label Text="{Binding Id}"/>

Related

XAML small Multicolumn List inside LiesView Element

In my Xamarin App I have a ListView, inside the ItemTemplate I have to show a text-only list with 3 columns.
The first idea was to use a GroupedListView, but in this case all subentries are selectable separately.
That is not what I want. The ListView Item should appear as one selectable element.
The Second idea I found on my research is to add Gridview rows by code, but this would break my MVVM concept. I need a solution which works with databinding only.
The third thing I sometimes read is: cascade a ListView inside the ListView. But mostly the answer to such idea is: "never do this".
Any other idea what I can do for this?
What I want is something like this:
ListView Entry 1
04:13 Jhonny 3,24$
09:45 Some Long Nam... 8,23$
14:42 Mike 5,45$
----------------------------------------
ListView Entry 2
07:13 Jhonny 3,24$
11:22 Some Long Nam... 8,23$
18:42 Mike 5,45$
----------------------------------------
ListView Entry 3
05:13 Jhonny 3,24$
15:45 Some Long Nam... 8,23$
19:42 Mike 5,45$
----------------------------------------
Always the whole Listview Entry should be selectable as one element.
Guess you could use the BindableLayout. You didn't specify the Type, so i made a dummy type.
View:
<CollectionView ItemsSource="{Binding GroupItems}" SelectionMode="Single">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical" Margin="5">
<Label Text="{Binding Title}"/>
<StackLayout BindableLayout.ItemsSource="{Binding Entries}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding Time}" HorizontalTextAlignment="Center"/>
<Label Text="{Binding Name}" Grid.Column="1" LineBreakMode="TailTruncation"/>
<Label Text="{Binding Amount}" Grid.Column="2" HorizontalTextAlignment="Center"/>
</Grid>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Codebehind:
public ObservableCollection<GroupItem> GroupItems { get; set; } = new ObservableCollection<GroupItem>();
public MainPage()
{
InitializeComponent();
BindingContext = this;
GroupItem item1 = new GroupItem();
item1.Title = "Entry 1";
item1.Entries.Add(new Entry { Time = "10:13", Name = "Johnny", Amount = "10,24$" });
item1.Entries.Add(new Entry { Time = "12:11", Name = "Some long name that will be truncated", Amount = "20,14$" });
item1.Entries.Add(new Entry { Time = "10:13", Name = "Mike", Amount = "14,27$" });
GroupItems.Add(item1);
GroupItem item2 = new GroupItem();
item2.Title = "Entry 2";
item2.Entries.Add(new Entry { Time = "11:13", Name = "Finn", Amount = "10,24$" });
item2.Entries.Add(new Entry { Time = "14:11", Name = "Some long name that will be truncated", Amount = "20,14$" });
GroupItems.Add(item2);
GroupItem item3 = new GroupItem();
item3.Title = "Entry 3";
item3.Entries.Add(new Entry { Time = "11:13", Name = "Finn", Amount = "10,24$" });
item3.Entries.Add(new Entry { Time = "14:11", Name = "Some long name that will be truncated", Amount = "20,14$" });
item3.Entries.Add(new Entry { Time = "14:11", Name = "Martin", Amount = "50,15$" });
item3.Entries.Add(new Entry { Time = "14:11", Name = "Elon musk", Amount = "30,14$" });
GroupItems.Add(item3);
}
public class GroupItem
{
public string Title { get; set; }
public List<Entry> Entries { get; set; } = new List<Entry>();
}
public class Entry
{
public string Time { get; set; }
public string Name { get; set; }
public string Amount { get; set; }
}
This gives you the possibility of a dynamic amount of rows, and every group is selectable.
Result:

Contact Number repeating in Xamarin.forms

I am facing a issue while retrieving contact list from the phone i am getting same contact number for all contact names in Xamarin.Forms. I am new in Xamarin and want to load all phone contacts in listview.
Here is my Code:
private List<ContactMenu> LoadAllContacts()
{
List<ContactMenu> getContactNamesAndNumber = new List<ContactMenu>();
//Bind Contacts
var forContactNames = ContactsContract.Contacts.ContentUri;
string[] forContactNumbers = {
ContactsContract.Contacts.InterfaceConsts.Id,
ContactsContract.Contacts.InterfaceConsts.DisplayName
};
var forContactNumber = ContactsContract.CommonDataKinds.Phone.ContentUri;
string[] forContactDisplayNumber = { ContactsContract.Contacts.InterfaceConsts.Id, ContactsContract.CommonDataKinds.Phone.Number };
var names = ManagedQuery(forContactNames, forContactNumbers, null, null, null);
var nameList = new List<string>();
var number = ManagedQuery(forContactNumber, forContactDisplayNumber, null, null, null);
var numbeList = new List<string>();
if (names.MoveToFirst() && number.MoveToFirst())
{
do
{
getContactNamesAndNumber.Add(new ContactMenu
{
ContactName = names.GetString(names.GetColumnIndex(forContactNumbers[1])), ContactNumber = number.GetString(number.GetColumnIndex(forContactDisplayNumber[1]))
});
numbeList.Add(number.GetString(number.GetColumnIndex(forContactDisplayNumber[1])));
nameList.Add(names.GetString(names.GetColumnIndex(forContactNumbers[1])));
} while (names.MoveToNext());
}
ListAdapter = new ArrayAdapter<string>(this, Resource.Layout.ContactItemView, numbeList);
return getContactNamesAndNumber;
}
And My Xaml :
<StackLayout HorizontalOptions="Center">
<Label Text="Please Select Contact" TextColor="Black" FontFamily="Arial" FontSize="Medium" Font="16"></Label>
<ListView x:Name="myContacts" HorizontalOptions="Start" ItemTapped="myContacts_ItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding ContactName}" DetailColor="Green" TextColor="Black"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
You have two ManagedQuerys, names and number but are only advancing the names query via names.MoveToNext() at the end of your do loop.
You should remove one of the ManagedQuery and return all the columns that you need in just one query so you can obtain the name and matching number from the same data row.

Reuse xamarin.forms xaml contentpage data throughout the app

I got a bit stuck on a problem and I was wondered if it's possible you can make a video on my problem it seems may others may have the same problem but, I been looking online but no real straight forward solution to the issue. The problem is, for instance, I have a stack layout in a content page page1.xaml and I added labels to that page 4 of them and I set properties for those labels setters and getters. however, if I make page2.xaml how would I be able to move the labels along with the data to page2.xaml basically reuse the data/labels declared from page1.xaml to page2.xaml throughout the app? here is what I have so far.
<StackLayout>
<StackLayout x:Name = "CustomerOrderStackLayout" Orientation = "Horizontal" HorizontalOptions = "Center" Padding = "20" >
<Label x:Name ="CustomerOrderNumberLabel" HorizontalOptions = "Center" />
</StackLayout>
<StackLayout Orientation = "Vertical" Padding = "20" >
<Label x:Name ="CustomerFirstNameLabel"
VerticalOptions = "Start" />
<Label x:Name ="CustomerLastNameLabel"
VerticalOptions = "Start" />
<Label x:Name ="CustomerAddressLabel"
VerticalOptions = "Start" />
<Label x:Name ="CustomerZipCodeLabel"
VerticalOptions = "Start"/>
<Label x:Name ="CustomerPhoneNumberLabel"
VerticalOptions = "Start"/>
</StackLayout>
</StackLayout>
</ContentPage>
public partial class CustomerInfoContentV : ContentPage
{
public CustomerInfoContentV()
{
InitializeComponent();
orderNum = "N";
FirstName = "Nl";
LastName = "Jk";
Address = "203030 drive";
ZipCode = "77088";
PhoneNumber = "833-223-2222";
}
public string orderNum
{
get
{
return CustomerOrderNumberLabel.Text;
}
set
{
CustomerOrderNumberLabel.Text = value;
}
}
public string FirstName
{
get
{
return CustomerFirstNameLabel.Text;
}
set
{
CustomerFirstNameLabel.Text = value;
}
}
public string LastName
{
get
{
return CustomerLastNameLabel.Text;
}
set
{
CustomerLastNameLabel.Text = value;
}
}
public string Address
{
get
{
return CustomerAddressLabel.Text;
}
set
{
CustomerAddressLabel.Text = value;
}
}
public string ZipCode
{
get
{
return CustomerZipCodeLabel.Text;
}
set
{
CustomerZipCodeLabel.Text = value;
}
}
public string PhoneNumber
{
get
{
return CustomerPhoneNumberLabel.Text;
}
set
{
CustomerPhoneNumberLabel.Text = value;
}
}
}
I want to know is there a way to take this what i have and reuse it on a whole different contentpage thats my issue i want to take this what i created and reuse it through out the app is this possible? can anyone lead me in the right direction! please thanks :)
If I am understanding you correct. You can use templates. https://developer.xamarin.com/guides/xamarin-forms/templates/control-templates/creating/ . For my projects I just create a code based template and render it whenever I want it and wherever I want them.

AlternationCount not found in listview xaml + application métro

Why i can't found the memeber AlternationCount in a listview XAML in my Windows8 application??
Best regards
To do this you could use the ItemContainerGenerator property and chain its ContainerFromItem and IndexFromContainer methods to get the index of your item and then use a converter to get a background color from the index.
public class Item : BindableBase
{
public ItemsControl itemsControl {get;set;}
private string name;
public string Name
{
get{
return name;
}
set
{
name = value;
OnPropertyChanged();
}
}
public int Index
{
get
{
return itemsControl.ItemContainerGenerator.IndexFromContainer(
itemsControl.ItemContainerGenerator.ContainerFromItem(this)
);
}
}
}
public sealed partial class ItemContainerGeneratorTest : App1.Common.LayoutAwarePage
{
...
public ObservableCollection<Item> test
{
get
{
var test = new ObservableCollection<Item>();
test.Add(new Item() { Name = "Index for item 1: ", itemsControl = ItemsControlControl });
test.Add(new Item() { Name = "Index for item 2: ", itemsControl = ItemsControlControl });
test.Add(new Item() { Name = "Index for item 3: ", itemsControl = ItemsControlControl });
test.Add(new Item() { Name = "Index for item 4: ", itemsControl = ItemsControlControl });
test.Add(new Item() { Name = "Index for item 5: ", itemsControl = ItemsControlControl });
test.Add(new Item() { Name = "Index for item 6: ", itemsControl = ItemsControlControl });
return test;
}
}
...
}
<ItemsControl x:Name="ItemsControlControl" ItemsSource="{Binding ElementName=pageRoot, Path=test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Index}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that this example doesn't currently handle changes to the collection (i.e. OnPropertyChanged isn't called for Index when the collection changes).

Dynamically Create controls using ContentControl and Converters in silverlight4

I have a problem to create dynamic controls in silverlight 4.
My requirement is:
I have question table in database, which is like below.
QuestionText, AnswerControl, AnswerDefaultText, IsItmandatory
Question1 TextBox null Yes
QuestionText2, RadioButton, Yes, Yes
Question3, ComboBox, null, no
..........................................
I need to get this data into object and conver the question text into TextBlock, and based on answercontrol value, need to create controls dynamically.
I tried like as you mentioned in your post, but data is not binding and not able to send default values as parameter values to converter.
My Converter is not getting called. Is there any thing wrong in below code?
My Codes are:
1)My Xaml Code:
<UserControl x:Class="SilverlightApplication5.DynamicControls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication5.Converter"
xmlns:question="clr-namespace:SilverlightApplication5"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White" Width="400" Height="400">
<Grid.Resources>
<local:UILocatorConverter x:Key="UILocatorConverter" />
<question:Questions x:Key="Questions"/>
</Grid.Resources>
<ListBox ItemsSource="{Binding Questions}" Width="400" Height="400">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<ContentControl Content="{Binding Converter={StaticResource UILocatorConverter},ConverterParameter={Binding QuestionText},Path=QuestionControl}" Grid.Column="0" />
<ContentControl Content="{Binding Converter={StaticResource UILocatorConverter},ConverterParameter={Binding DefaultValue},Path=AnswerControl}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
2) Code behind file code is:
namespace SilverlightApplication5
{
public partial class DynamicControls : UserControl
{
ObservableCollection<Questions> Question;
public DynamicControls()
{
InitializeComponent();
Question = new ObservableCollection<Questions>();
Question.Add(new Questions { QuestionControl = "TextBlock", QuestionText = "What is your name?", AnswerControl = "TextBox", AnswerValues = "", DefaultValue = "" });
Question.Add(new Questions { QuestionControl = "TextBlock", QuestionText = "What is your surname?", AnswerControl = "TextBox", AnswerValues = "", DefaultValue = "" });
Question.Add(new Questions { QuestionControl = "TextBlock", QuestionText = "Sex:", AnswerControl = "ComboBox", AnswerValues = "Male,Female,Others", DefaultValue = "Select a Value" });
Question.Add(new Questions { QuestionControl = "TextBlock", QuestionText = "Marital Status", AnswerControl = "RadioButton", AnswerValues = "", DefaultValue = "Not Married" });
this.DataContext = Question;
}
}
}
3) My converter is:
namespace SilverlightApplication5.Converter
{
public class UILocatorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
String param="This is control created dynamically";
if (parameter != null)
{
param = System.Convert.ToString(parameter);
}
switch (value.ToString())
{
case "TextBlock":
return new TextBlock() { Text = param, HorizontalAlignment=HorizontalAlignment.Center,TextWrapping=TextWrapping.NoWrap,Width=200 };
case "Button":
return new Button() { Content = param, Width=150 };
case "TextBox":
return new TextBox() { Text = param };
case "RadioButton":
return new TextBox() { };
case "ComboBox":
return new TextBox() { };
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
4) My Question Class is:
namespace SilverlightApplication5
{
public class Questions
{
private string _questionControl;
public string QuestionControl {
get
{
return _questionControl;
}
set
{
_questionControl = value;
}
}
private string _questionText;
public string QuestionText
{
get
{
return _questionText;
}
set
{
_questionText = value;
}
}
private string _answerControl;
public string AnswerControl
{
get
{
return _answerControl;
}
set
{
_answerControl = value;
}
}
private string _answerValues;
public string AnswerValues
{
get
{
return _answerValues;
}
set
{
_answerValues = value;
}
}
private string _defaultValue;
public string DefaultValue
{
get
{
return _defaultValue;
}
set
{
_defaultValue = value;
}
}
}
}
My converter is not getting called, is there any issues in this code?
You need to use this:
<ListBox ItemsSource="{Binding}" Width="400" Height="400">
As you want to access the collection of Questions you set in the DataContext.
What you were doing was creating a single Questions class in your Resources and telling the ListBox to use that.
So you won't need this at all:
<question:Questions x:Key="Questions"/>
(You might have to use BindsDirectlyToSource...because you are setting the DataContext to a collection directly...could be wrong on that!).
Alternatively, you can do this in your control:
public partial class DynamicControls : UserControl
{
public ObservableCollection<Questions> Question { get; set; }
...
Set the DataContext in this way:
DataContext = this;
And then use this Binding:
<ListBox ItemsSource="{Binding Question}" Width="400" Height="400">
I'd recommend you rename your Questions class to Question, and then rename the Property to Questions, to avoid confusion.