I'm still learning Xamarin Forms and C# in general. So in my MainPage.xaml i have this:
<StackLayout>
<Button Text="bttn1" Clicked="Button_Clicked"/>
<Button Text="bttn2" Clicked="Button_Clicked_1"/>
<ContentView x:Name="DisplayCustomContentView">
</ContentView>
</StackLayout>
and two ContentViews:
View 1:
public class View1 : ContentView
{
public View1 ()
{
Content = new StackLayout {
Children = {
new Label { Text = "View 1" },
new Entry {Placeholder = "entry1 View 1"},
new Entry {Placeholder = "entry2 View 1"}
}
};
}
}
View 2:
public class View2 : ContentView
{
public View2 ()
{
Content = new StackLayout {
Children = {
new Label { Text = "View 2" },
new Entry {Placeholder = "entry2 View 1"},
new Entry {Placeholder = "entry2 View 2"}
}
};
}
}
So I want to swap between the views when I click on the buttons. I have tried this:
private void Button_Clicked(object sender, EventArgs e)
{
DisplayCustomContentView = new View1();
}
And how can i get the values from the Entry fields?
I'm mostly certain that I'm doing not the right way. And I could use all the help I can get!
To answer your question:
And how can i get the values from the Entry fields?
There's the simple answer, and the not-so-simple answer.
The simple answer, since you're building View1 and View2 in C# (rather than XAML), is to store a reference to the Entry objects:
public class View1 : ContentView
{
public Entry Entry1 { get; private set; }
public Entry Entry2 { get; private set; }
public View1 ()
{
Entry1 = new Entry { Placeholder = "entry1 View 1" };
Entry2 = new Entry { Placeholder = "entry1 View 2" };
Content = new StackLayout {
Children = {
new Label { Text = "View 1" },
Entry1,
Entry2
}
};
}
}
Then, in some button callback or Command (or whenever your program gets control), you can look at the Entry1.Text and Entry2.Text properties to see what the user entered.
Now on to the not so simple way, MVVM and model binding. While the code for this gets a bit more involved, it's a very popular way to write Xamarin.Forms applications, as it gives you better ability to separate your view code from the rest of the logic. This can help separate concerns within your application to improve testability, maintainability, etc. You can do this with Xamarin built-in features, but a lot of people like to use various MVVM packages to further support MVVM principles.
To give a simple illustration using View1 (using just built-in Xamarin.Forms features), you would also create a View1ViewModel class, as such:
public class View1ViewModel
{
public string Entry1 { get; set; }
public string Entry2 { get; set; }
}
public class View1 : ContentView
{
public View1ViewModel ViewModel { get; private set; }
public View1 ()
{
ViewModel = BindingContext = new View1ViewModel();
var entry1 = new Entry { Placeholder = "entry1 View 1" };
var entry2 = new Entry { Placeholder = "entry1 View 2" };
entry1.SetBinding(Entry.TextProperty, "Entry1");
entry2.SetBinding(Entry.TextProperty, "Entry2");
Content = new StackLayout {
Children = {
new Label { Text = "View 1" },
entry1,
entry2
}
};
}
}
In this model, rather than directly looking at properties on the underlying UI elements (the Entry objects), other code can look at the view model, as those properties get updated automatically when the entry's value changes.
While this sample code does get bigger with the addition of model binding, it tends to be a bit more concise when using XAML.
There are, of course, additional aspects of MVVM (INotifyPropertyChanged, etc.), and I encourage you to learn more, but that goes a bit beyond the scope of the original question.
You need to set the Content property.
private void Button_Clicked(object sender, EventArgs e)
{
DisplayCustomContentView.Content = new View1();
}
Related
Am I missing something or is there more to it that I am not getting? I'm working on a mobile app and have to use pickers for choices from a data table. To start, I have many such pickers that are key/value based. I have an internal ID and a corresponding Show value. The IDs do not always have 1, 2, 3 values such as originating from a lookup table and may have things as
KeyID / ShowValue
27 = Another Thing
55 = Many More
12 = Some Item
Retrieved as simple as
select * from LookupTable where Category = 'demo'
So I have this class below that is used for binding the picker via a list of records
public class CboIntKeyValue
{
public int KeyID { get; set; } = 0;
public string ShowValue { get; set; } = "";
}
Now, the data record that I am trying to bind to has only the ID column associated to the lookup. Without getting buried into XAML, but in general, I have my ViewModel. On that I have an instance of my data record that has the ID column.
public class MyViewModel : BindableObject
{
public MyViewModel()
{
// Sample to pre-load list of records from data server of KVP
PickerChoices = GetDataFromServerForDemo( "select * from LookupTable where Category = 'demo'" );
ShowThisRecord = new MyDataRec();
// for grins, I am setting the value that SHOULD be defaulted
// in picker. In this case, ID = 12 = "Some Item" from above
ShowThisRecord.MyID = 12;
}
// this is the record that has the "ID" column I am trying to bind to
public MyDataRec ShowThisRecord {get; set;}
// The picker is bound to this list of possible choices
public List<CboIntKeyValue> PickerChoices {get; set;}
}
I can’t bind to the index of the list, because that would give me 0, 1, 2, when I would be expecting the corresponding "ID" to be the basis of proper record within the list.
In WPF, I have in the past, been able to declare the show value for the screen, but also the bind value to the ID column in similar. So, the binding of the INT property on my "ShowThisRecord" would drive and properly refresh.
I can see the binding of SelectedItem, but that is the whole item of the KVP class which is not part of the MyDataRec. Only the ID is the common element between them.
What is the proper bindings to get this to work?
<Picker ItemDisplayBinding="{Binding ShowValue}"
SelectedItem="{Binding ???}" />
Just to confirm my record bindings are legitimate, my page has binding context to MyViewModel as I can properly see the ID via a sample text entry I added to the page via.
<Entry Text="{Binding Path=ShowThisRecord.MyID}"/>
I created a demo to test your code, and it works properly. The full demo is here. I also added a function to verify the selected item.
If you want to get the SelectedItem object synchronously, the MyViewModel should implement INotifyPropertyChanged, and I created a selectedRecordfield for SelectedItem, so you can do like this:
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
// Sample to pre-load list of records from data server of KVP
//PickerChoices = GetDataFromServerForDemo("select * from LookupTable where Category = 'demo'");
PickerChoices = new ObservableCollection<TestModel>() {
new TestModel{MyID = 5, ShowValue="test1"}, new TestModel{MyID = 9, ShowValue="test2"},
new TestModel{MyID = 18, ShowValue="test18"}, new TestModel{MyID = 34, ShowValue="test4"}
};
// Set the default selected item
// foreach (TestModel model in PickerChoices) {
// if (model.MyID == 18) { // Default value
// SelectedRecord = model;
// break;
// }
// }
ShowThisRecord = new TestModel();
// For grins, I am setting the value that SHOULD be defaulted
// in picker. In this case, ID = 12 = "Some Item" from above
ShowThisRecord.MyID = 12;
}
// This is the record that has the "ID" column I am trying to bind to
public TestModel ShowThisRecord { get; set; }
//*****************************************
TestModel selectedRecord; // Selected item object
public TestModel SelectedRecord
{
get { return selectedRecord; }
set
{
if (selectedRecord != value)
{
selectedRecord = value;
OnPropertyChanged();
}
}
}
//*****************************************
// The picker is bound to this list of possible choices
public ObservableCollection<TestModel> PickerChoices { get; set; }
}
class ViewModelBase
public class ViewModelBase: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the XAML content:
<Picker Title="Select a value" x:Name="mypicker"
ItemsSource="{Binding Path= PickerChoices}"
SelectedItem="{Binding SelectedRecord}"
ItemDisplayBinding="{Binding MyID}"/>
File xaml.cs:
public partial class MainPage : ContentPage
{
ObservableCollection<TestModel> items = new ObservableCollection<TestModel>();
MyViewModel testModel = null;
public MainPage()
{
InitializeComponent();
testModel = new MyViewModel();
BindingContext = testModel;
// This will also work
//if (testModel!=null && testModel.PickerChoices!=null) {
// for (int index=0; index< testModel.PickerChoices.Count; index++) {
// TestModel temp = testModel.PickerChoices[index];
// if (18 == temp.MyID) {
// mypicker.SelectedIndex = index;
// break;
// }
// }
//}
foreach (TestModel model in testModel.PickerChoices)
{
if (model.MyID == 18)
{ // Default value
testModel.SelectedRecord = model;
break;
}
}
}
// To show the selected item
private void Button_Clicked(object sender, EventArgs e)
{
if (testModel.SelectedRecord!=null) {
DisplayAlert("Alert", "selected Item MyID: " + testModel.SelectedRecord.MyID + "<--> ShowValue: " + testModel.SelectedRecord.ShowValue, "OK");
}
}
}
The result is:
You need to set the ItemsSource property to your list of CboIntValue items:
<Picker Title="Select a value"
ItemsSource="{Binding PickerChoices}"
ItemDisplayBinding="{Binding ShowValue}" />
After much work, I ended up writing my own separate class and template style for what I needed. Due to the length of it, and posting the source code for anyone to use, review, assess, whatever, I posted that out on The Code Project.
Again, the primary issue I had is if I have an integer key ID coming from a data source, the picker would not automatically refresh itself by just the given ID (int or string).
Hi am having a problem to understand binding in WPF.
I have got a User Control and contains ListView, and reading data from database but it takes a minimum of 60 seconds and then fill the listview with data.
There is a status bar which shows the loading process.
If data is loaded in the memory and User opens the UC, View model loads the data to ListView. Everything works fine.
But if User opens the UC before data read finish and at the end of the reading data i call the same method in the viewModel, in the code behind data is loaded to List item but ListView is still empty. Somehow ListView doesn't show data.
<ListView x:Name="ListViewUK" ItemsSource="{Binding ListOfAccountsFromExchUK}" >
View Model
public class ExchequerViewModel :BaseView
{
List<Exch_Account> exch_Accounts_UK;
public ObservableCollection<PAMHeaderModel> ListOfAccountsFromExchUK { get; set; }
#region CONSTRUCTOR
public ExchequerViewModel()
{
ListOfAccountsFromExchUK = new ObservableCollection<PAMHeaderModel>();
PopulateExchequerList();
}
#endregion
public void PopulateExchequerList()
{
exch_Accounts_UK = ExchequerMemory.ExcAccountList_UK;
if (exch_Accounts_UK == null)
{
AutoClosingMessageBox.Show("Exchequer UK - datas are loading Pleae try again later.", "Information", 2000);
}
if (exch_Accounts_UK != null)
{
UmbList = new Dictionary<string, string>();
foreach (var acc in exch_Accounts_UK)
{
ListOfAccountsFromExchUK.Add(new PAMHeaderModel
{
Company = "UK",
RefNo = acc.Code,
Name = acc.Company,
Subsidiary = acc.UDF6
});
UmbList.Add(acc.Code, acc.UDF6); ? acc.UDF6 : "";
}
if (ListOfAccountsFromExchUK != null)
UKStatusInfoLabel = ListOfAccountsFromExchUK.Count();
}
User Control
public ExchequerViewModel viewModel;
public ExchequereUC()
{
InitializeComponent();
viewModel = new ExchequerViewModel();
DataContext = viewModel;
}
So, I have this app where I can choose a car and see the car info... I'm displaying the cars like this.
I'm using the Rg.Plugins.Popup so when I click the car icon, it opens this popup with "my cars"
So now I'm facing a problem which is, when I choose a car, I want to refresh my current page so the car's info can be shown... I'm handling the car button click on this next view model:
public class MyCarViewModel : ViewModelBase
{
public MyCarViewModel()
{
}
public MyCarViewModel(INavigation navigation)
{
this.Navigation = navigation;
this.SelectedCar = null;
GetClientCars();
}
private Page page { get; set; }
private List<CarInfo> _CarList;
public List<CarInfo> CarList
{
get
{
return _CarList;
}
set
{
_CarList = value;
OnPropertyChanged("CarList");
}
}
private CarInfo _SelectedCar;
public CarInfo SelectedCar
{
get
{
return _SelectedCar;
}
set
{
_SelectedCar = value;
OnPropertyChanged("SelectedCar");
if (_SelectedCar != null)
{
CarSelected(_SelectedCar);
}
}
}
public INavigation Navigation { get; set; }
private void CarSelected(CarInfo car)
{
App.choosedCar = car;
PopupNavigation.Instance.PopAllAsync();
this.SelectedCar = null;
}
}
And I want this View to refresh
<views:BaseMainPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="OficinaDigitalX.Views.CarDetails"
xmlns:views="clr-namespace:OficinaDigitalX.Views">
<views:BaseMainPage.Content>
<StackLayout>
<Label Text="{Binding VID, StringFormat='Modelo: {0:F0}'}" FontAttributes="Bold"
FontSize="Large"/>
<Label Text="{Binding LicencePlate, StringFormat='Matrícula: {0:F0}'}"/>
<Label Text="{Binding Chassis, StringFormat='Chassis: {0:F0}'}"/>
<Label Text="{Binding Km, StringFormat='Ultimos Km Registados: {0:N0}'}"/>
</StackLayout>
</views:BaseMainPage.Content>
</views:BaseMainPage>
and xaml.cs
public partial class CarDetails : BaseMainPage
{
public CarDetails(CarInfo car)
{
InitializeComponent();
BindingContext = new CarDetailViewModel(this);
App.currentPage = this;
if (car != null)
{
this.Title = "Dados de " + car.MakerandModel;
}
else
{
this.Title = "Escolha uma Viatura";
}
}
}
I'm facing a lot of issues here because my car icon is a part of my "BaseMainPage" which is extended by the other Views (so the icon can be shown on all views)...
So when I click the button, the application doesn't know its current page...
I thought I might use the Navigation Stack to reload it but I don't quite know how to do this...
Hope you guys can help
Well, essentially you do not need to refresh page or reload page, you just need to refresh the data.
since you are using OnPropertyChanged(INotifyPropertyChanged) you are half way there.
instead of using List CarList use ObservableCollection CarList.
and if you deliberately want to reload the page, on dismissing the pop.up save your data and call the constructor/reinitiate the Page.
hopefully you should achieve what you are looking for.
I think you don't need to reload the page, you need to reload your data. Your page will be updated automatically with the databindings.
For me it looks like you're using Prism, so you could override the OnNavigatingTo Method and load the data every time the page is "opened".
I've just used MessagingCenter and I've called it with my OnPropertyChanged and this seemed to do the work! Thanks a lot!
View Model:
OnPropertyChanged("SelectedCar");
if (_SelectedCar != null)
{
CarSelected(_SelectedCar);
MessagingCenter.Send(this, "Hi");
}
My other view model's constructor
MessagingCenter.Subscribe<MyCarViewModel>(this, "Hi", (sender) => {
this.currentCar = App.choosedCar;
});
My setup is basically as follows:
NavigationView's MenuItemsSource is bound to viewModel.NavItems.
NavItems is a computed property of viewModel.
The view model class implements INotifyPropertyChanged for binding purpose
Books of view model is loaded asynchronously.
What works
The NavigationViewItems show up after the page is reached.
The Problem
I need to set a specified NavigationViewItem as the SelectedItem of the NavigationView. But there's no NavigationViewItem (from viewModel) to use inside OnNavigatedTo(NavigationEventArgs e), because at that point viewModel.NavItems is not ready yet.
So is there a pattern for selecting a NavigationViewItem in this async situation?
XAML
<NavigationView x:Name="navView"
MenuItemsSource="{x:Bind viewModel.NavItems, Mode=OneWay}"
SelectionChanged="NavView_SelectionChanged" >
…
the view model
internal class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// The data service
private MainDataService mainDataService = new MainDataService();
private List<Book> books = new List<Book>();
public List<Book> Books
{
get
{
return this.books;
}
set
{
this.books = value;
this.OnPropertyChanged();
this.OnPropertyChanged("NavItems");
}
}
public IEnumerable<NavigationViewItemBase> NavItems
{
get
{
return Books.SelectMany(
b => (new List<NavigationViewItemBase> { new NavigationViewItemHeader {
Content = b.Title,
Tag = b.Title
} })
.Concat(
b.Sections.Select(s => new NavigationViewItem
{
Content = s.Title,
Icon = new FontIcon { Glyph = "\uE8B7", FontFamily = new FontFamily("Segoe MDL2 Assets") }
})
)
);
}
}
// #param selectedBookIndex: the index of the book whose first section
// should be selected.
public async Task UpdateBooks(int selectedBookIndex)
{
await mainDataService.PrepareData();
this.Books = mainDataService.Books;
}
…
}
So is there a pattern for selecting a NavigationViewItem in this async situation?
For async situation, you need use ObservableCollection but not List. And it represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
But there's no NavigationViewItem (from viewModel) to use inside OnNavigatedTo(NavigationEventArgs e), because at that point viewModel.NavItems is not ready yet.
Before the data is not ready, you could keep the Frame navigation to the default page that used as placehoder within NavigationView Loaded event.
For more you could refer Data binding in depth.
I'm new to Xamarin Forms and I would want to add a input field (Numeric) on the right side of each item in my listview, so that I can type a quanty to my items. Somthing like a shopping list of items with the quantity of how many you want.
here is my code of my listview
List<string> item = new List<string>();
public MainPage()
{
InitializeComponent();
item.Add("Apple");
item.Add("Banana");
item.Add("Graps");
item.Add("Orange");
item.Add("Pineapple");
item.Add("Strawberry");
item.Add("Lemon");
item.Add("Mango");
item.Add("Cherry");
item.Add("Watermelon");
item.Add("Add");
var listView = new ListView
{
RowHeight = 40
};
listView.ItemsSource = item;
StackLayout layout = new StackLayout();
layout.Children.Add(listView);
listView.ItemSelected += (sender, e) =>
{
if (e.SelectedItem.ToString() == "Add")
{
var MyEntry = new Entry { Placeholder = "new item" };
layout.Children.Add(MyEntry);
MyEntry.Completed += MyEntry_Completed;
}
};
this.Content = layout;
}
private void MyEntry_Completed(object sender, EventArgs e)
{
var text = ((Entry)sender).Text;
item.Add(text);
}
I had a lot of trouble wrapping my head around databinding from the C# side in Xamarin Forms. I would recommend you checkout DataBinding to get a better understanding of this. I created a custom ViewCell for you to play around with, and a basic implementation so you can get a visual of how to implement it. You can adjust the view cell as much as needed. I just wanted to give you a bases of how to implement it. Hope this helps.
ViewCell
This is the view that displays for each list item in the list view.
using Xamarin.Forms;
namespace BountyApp.Controls
{
public class CustomViewCell : ViewCell
{
private Grid _grid = new Grid();
private Label _lbl = new Label() { HorizontalTextAlignment = TextAlignment.End, VerticalTextAlignment = TextAlignment.Center };
private Entry _entry = new Entry();
public CustomViewCell()
{
_lbl.SetBinding(Label.TextProperty, new Binding("Title"));
_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.3, GridUnitType.Star) });
_grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(0.7, GridUnitType.Star) });
_grid.Children.Add(_lbl, 0, 0);
_grid.Children.Add(_entry, 1, 0);
View = _grid;
}
}
}
Implementing ViewCell
using Xamarin.Forms;
namespace BountyApp.Pages
{
public class ViewModel
{
public string Title { get; set; }
}
public class StepperPage : ContentPage
{
public ObservableCollection<ViewModel> List { get; set; }
public StepperPage()
{
List = new ObservableCollection<ViewModel>();
List.Add(new ViewModel { Title = "Apple" });
List.Add(new ViewModel { Title = "Banana" });
List.Add(new ViewModel { Title = "Graps" });
List.Add(new ViewModel { Title = "Orange" });
List.Add(new ViewModel { Title = "Pineapple" });
List.Add(new ViewModel { Title = "Strawberry" });
var listView = new ListView
{
RowHeight = 40,
ItemTemplate = new DataTemplate(typeof(CustomViewCell)),
ItemsSource = List
};
Content = listView;
}
}
}
Result