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;
});
Related
I read several similar issues with MVVM, but just cannot find the right fix to this problem. I am sure it's really simple, but I cannot work out how to update an UI from two different classes.
I have one ModelView (Homepage.xaml) and MVVM binded label, which works fine from the Homepage.cs, but how can I update this label from from a different class (anotheractivty.cs). Is there a way to reference the string from from the second class file or do I need to somehow call and pass the string between the anotheractivty to the homepage class?
Many thanks
Please note the code is simplified:
Homepage.xaml
<Label
Text={"Binding Updatetext}"
/>
Homepage.xaml.cs
String updatetext = "";
public Homepage()
{
BindingContent = this;
}
Public string Updatetext
{
get=> updatetext
set
{
if (value == updatetext)
return;
updatetext = value;
OnPropertyChange(nameof(Updatetext));
}
}
Public updatetest()
{
Updatetext = "new text";
}
anotheractivty.cs
How to link this to the Homepage.cs?
Public updatetest()
{
var page = new Homepage;
Homepage.Updatetext = "new text";
}
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;
}
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();
}
I have a page where I bind a command to a button. When I click it I calls a method where I get the data I want from an API.What if I don't want to only bind the data at the view but also use these data in code behind?!
Lets say this is my view :
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="HelloWorld.Pages.JohnDoePage"
xmlns:local="clr-namespace:Xamarin.Forms.Maps;assembly=Xamarin.Forms.Pages"
xmlns:vm="clr-namespace:HelloWorld.ViewModel;assembly=HelloWorld">
<StackLayout Padding="20, 10" HorizontalOptions="Center">
<Button Command="{Binding JohnDoe}"
Text="Data about John Doe" />
</StackLayout>
</ContentPage>
the code behihnd :
Models.Info info;
public JohnDoePage(Models.Info info)
{
InitializeComponent();
BindingContext = new InfoDetailsViewModel(info);
this.info= info;
// i want to use the data here
//using the data
}
the view model :
Data _data;
public Data Data
{
get { return _data; }
set
{
if (value == _data) return;
_data = value;
OnPropertyChanged();
}
}
public ICommand JohnDoe
{
get
{
return new Command(async () =>
{
var Dataa = await _apiServices.InfoAboutJohnDOe();
});
}
}
and the service where I get the data I need is OK. I'm using the same viewmodel for binding different commands, and I don't know if this is possible by the way so I'm stuck. Any idea how can I use the data I get in the view code-behind?? Thanks in advance!!
why not just maintain a class level reference to your VM?
Models.Info info;
InfoDetailsViewModel vm;
public JohnDoePage(Models.Info info)
{
InitializeComponent();
vm = new InfoDetailsViewModel(info);
BindingContext = vm;
this.info= info;
}
Save the result of your Service call as a public property
public <TargetType> Dataa { get; set; }
and save the DataContext of your JohnDoePage as a member.
JohnDoePageViewModel dataContext = (JohnDoePageViewModel)this.DataContext;
Because then you are able to get the information from the ViewModel.
var data = dataContext.Dataa;
Does this help?
Basic question from a novice. I've been stuck on this and have read through a lot of material and several similar questions on SO; hopefully not a completely duplicate question. I simplified the code as much as I know how to.
I'm trying to make the ListView show a filtered ObservableCollection) property (as the ItemsSource?), based on the selection in the ComboBox.
Specifically, which "meetings" have this "coordinator" related to it.
I'm not seeing any data errors in the output while it's running and debugging shows the properties updating correctly, but the ListView stays blank. I'm trying to avoid any code-behind on the View, there is none currently.
Thanks!
public class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Meeting> meetings;
public ObservableCollection<Meeting> Meetings
{
get
{
return meetings;
}
set
{
meetings = value;
OnPropertyChanged("ListProperty");
OnPropertyChanged("Meetings");
}
}
private string coordinatorSelected;
public string CoordinatorSelected
{
get
{
return coordinatorSelected;
}
set
{
coordinatorSelected = value;
Meetings = fakeDB.Where(v => v.CoordinatorName == CoordinatorSelected) as ObservableCollection<Meeting>;
}
}
private ObservableCollection<string> comboProperty = new ObservableCollection<string> { "Joe", "Helen", "Sven" };
public ObservableCollection<string> ComboProperty
{
get
{
return comboProperty;
}
}
private ObservableCollection<Meeting> fakeDB = new ObservableCollection<Meeting>() { new Meeting("Joe", "Atlas"), new Meeting("Sven", "Contoso"), new Meeting("Helen", "Acme") };
public ObservableCollection<Meeting> ListProperty
{
get
{
return Meetings;
}
}
public class Meeting
{
public string CoordinatorName { get; set; }
public string ClientName { get; set; }
public Meeting(string coordinatorName, string clientName)
{
CoordinatorName = coordinatorName;
ClientName = clientName;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window.Resources>
<local:ViewModel x:Key="VM"></local:ViewModel>
</Window.Resources>
<DockPanel DataContext="{StaticResource ResourceKey=VM}">
<ComboBox Margin="10" ItemsSource="{Binding ComboProperty}" SelectedItem="{Binding CoordinatorSelected}" DockPanel.Dock="Top"/>
<ListView Margin="10" ItemsSource="{Binding ListProperty, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="ClientName"/>
</DockPanel>
Update:
This seems to show that the lambda is returning a Meeting object but the assignment to Meetings is failing. Is this an error in casting maybe?
Thanks again.
You always have to change a property's backing field before you fire a PropertyChanged event. Otherwise a consumer of the event would still get the old value when it reads the property.
Change the Meetings property setter like this:
public ObservableCollection<Meeting> Meetings
{
get
{
return meetings;
}
set
{
meetings = value;
OnPropertyChanged("ListProperty");
OnPropertyChanged("Meetings");
}
}
I believe I've found two solutions to the same problem. The error pointed out #Clemens was also part of the solution. The Meetings property problem is solved if I change ListProperty and Meetings to IEnumerable. Alternatively this approach without changing the type, which I believe invokes the collection's constructor with the filtered sequence as an argument.
set
{
coordinatorSelected = value;
var filteredList = fakeDB.Where(v => v.CoordinatorName == coordinatorSelected);
Meetings = new ObservableCollection<Meeting>(filteredList);
OnPropertyChanged("ListProperty");
}