Xamarin Forms - Adding constructor to MasterDetailPage.Master - xamarin

I have a Xamarin.Forms app with a master-detail page and it works well.
But I've recently needed to add a parameter to the constructor of the master page (AttendPageMaster), but now I need to pass this constructor.
How do I add a parameter to the xaml?
<MasterDetailPage.Master>
<pages:AttendPageMaster x:Name="MasterPage" />
</MasterDetailPage.Master>
The code behind page with constructor:
public AttendPageMaster(AttendanceViewModel viewModel)
{
}
Let me know if you need any more info.

You do not have to pass ViewModel to Page via constructor, you can set the Page's BindingContext:
<MasterDetailPage.Master>
<pages:AttendPageMaster x:Name="MasterPage">
<pages:AttendPageMaster.BindingContext>
<myViewModels:AttendanceViewModel />
</pages:AttendPageMaster.BindingContext>
</pages:AttendPageMaster>
</MasterDetailPage.Master>
This solution will work if your ViewModel does not expect any parameters in constructor. Otherwise you may consider using ViewModelLocator and DI to inject the constructor parameters.
Please note that myViewModels should be defined in the header of your XAML page as xmlns:myViewModels.
P.S.: Previously you mentioned that you got an exception while trying to use code behind approach. You could easily solve it by setting the Title property of the AttendPageMaster. Example:
new AttendPageMaster(new AttendanceViewModel()){ Title = " " };

I managed to do this from the code behind by creating the menu page in the constructor of the masterdetail and assigning it to the "Master" property:
AttendMasterPageMaster MasterPage;
public AttendMasterPage(AttendanceViewModel viewModel)
{
MasterPage = new AttendMasterPageMaster(viewModel);
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(StartPage), viewModel));
Master = MasterPage;

Related

Insert Html file content in xamarin forms

I have one Html file. That html file contains only some div. and i have applied some styles to that all div like margin, color and font-family. Now i want to make a page in my xamarin form app(both in ios and android) and want to add that html file's whole contain in my page.
I have tried by using web view control of xamarin forms. But my html file's content is too long, and because of that, xamarin code is going too long too as we have to applied html as a string to web view control. so i don't want to use that way.
So please give me brief explanation or solution to add html file in xamarin forms.
Hope for better solution.
Thanks in advance.
Not 100% sure what do you mean by "xamarin code is going to long too".
But you can make HtmlWebViewSource object in your ViewModel or in a code-behind, depending what approach you are using and later on you can set or bind WebViews's Source property to it.
In order to set it from code-behind you can do it like this:
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = #"<html><body><h1>Xamarin is awesome</h1></body></html>";
yourWebView.Source = htmlSource;
On the other hand, if you are using MVVM and whole View-ViewModel concept you just need to have a property in your ViewModel of type HtmlWebViewSource and do the simple binding in your View.
Let's say you have the property of type HtmlWebViewSource named HtmlSource, you can set its value to your HTML content like we did in the previous example and than bind to it from WebView control, that should look something like this:
<WebView x:Name="yourWebView" Source="{Binding HtmlSource}" WidthRequest="1000" HeightRequest="1000" />
Hope this was helpful for you, wishing you lots of luck with coding!
If you want to load local html in your contentpage, I suggest you can use DependencyService to get html url from Android Assets file, I create simple in android that you can take a look.
Firstly, creating html in Android--Assets file, name as TestWebPage.html.
Then Creating Interface in Form, IBaseUrl.
public interface IBaseUrl
{
string GetUrl();
}
Then implementing this interface in Android platform:
public class LocalFiles : IBaseUrl
{
public string GetUrl()
{
return "file:///android_asset/";
}
}
ContentPage code:
<StackLayout>
<!-- Place new controls here -->
<WebView
x:Name="webviewjava"
HeightRequest="300"
WidthRequest="300" />
</StackLayout>
public MainPage()
{
InitializeComponent();
var urlSource = new UrlWebViewSource();
string baseUrl = DependencyService.Get<IBaseUrl>().GetUrl();
string filePathUrl = Path.Combine(baseUrl, "TestWebPage.html");
urlSource.Url = filePathUrl;
webviewjava.Source = urlSource;
}
Here is the sample at github, you can take a look:
https://github.com/CherryBu/htmlapp

Why is Xamarin DatePicker Binding causing a navigation failure?

I am using Prism and Autofac with Xamarin.Forms 4.0 with an MVVM architecture. Using the Navigation.NavigateAsync("MyPage") works unless I have a binding to the Date object with my ViewModel.
The page renders properly and I am navigated to it if my DatePicker has no binding.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" />
However the following will cause me to never navigate to the page.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" Date="{Binding SelectedStartDate, Mode=TwoWay}"
The property in the View Model, MyVM, looks like this.
private DateTime selectedStartDate;
public DateTime SelectedStartDate
{
get
{
return selectedStartDate;
}
set
{
SetProperty(ref selectedStartDate, value);
sample.ProcessStartDate = value;
}
}
Navigation with the following code fails with the Binding in XAML above:
INavigationResult status;
try
{
var parameters = new NavigationParameters();
parameters.Add("CurrentSample", SelectedSample);
status = await NavigationService.NavigateAsync("MyPage", parameters); //MyPage is registered with MyVM
}
catch (Exception ex)
{
string mess = ex.Message;
}
My work-around is to add an event handler to the code-behind.
<DatePicker x:Name="ProcessStartDate" Format="D" MinimumDate="01/01/2000" DateSelected="OnStartDateSelected"
So now my code-behind has a handler:
void OnStartDateSelected(object sender, DateChangedEventArgs args)
{
SampleDetailsViewModel vm = BindingContext as SampleDetailsViewModel;
vm.SelectedStartDate = args.NewDate;
}
I have a work-around for this page, But I don't want put code in the code-behind. This breaks the MVVM standard that I've managed to maintain on the other seven pages of the app. Am I Binding improperly with the DatePicker?
When Binding SelectedStartDate, you are not initializing it, making it binding to a null, because you have set the Binding Mode to "TwoWay".
Here you can find the various types of binding modes, quoting:
Causes changes to either the source property or the target property to
automatically update the other. This type of binding is appropriate
for editable forms or other fully-interactive UI scenarios.
a solution would be something like this (if you wanna keep the TwoWay mode, and dont mind starting with an default selected):
private DateTime selectedStartDate = DateTime.Now;
Or
Making the binding mode to "OneWayToSource", this makes updates to the binding source without, and not the target (remember that this way you can't change the selected date from the binding, only the datepicker can).
Updates the source property when the target property changes.
Or
If you wanna keep the TwoWay Mode and not having a default date selected, the way you did with code behind is a nice workaround.

how to update ObservableCollection<> of one page from another page in xamarin

My application is in MVVM architecture.
I Have photo.xaml page in which i have one 1 ListView whoose bindingcontext is ObservableCollection listphoto of photos which is defined in its viewmodel.cs file.
now i have to redirect to BarcodeScan.cs from button click of photo.xaml .
my que i how can i add item to listphoto from here(BarcodeScan.cs )??
I tried to define new list in BarcodeScan like this
public ObservableCollection<JobPhoto> ListSerialNumbers { get; set; }
and intialised in its constructor like this
ListSerialNumbers = new ObservableCollection<JobPhoto>();
but it dont update list on photo.xaml page.
how can i achieve this. I am new to MVVM.Please Help.
Thank you.
You should use messaging center for this
First get it method registered as :
MessagingCenter.Subscribe<YourObjectClassComesHere>(this, "Any Message or empty string will be okay", (Obj) =>
{
//Code you want to execute
});
After this you can invoke it from another page as
MessagingCenter.Send(YourObject(of type "YourObjectClassComesHere"), "Any Message or empty string will be okay");
Hope it helps.
More details are available at : https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/messaging-center/
You can try MessageCenter https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/messaging-center/
In Phonepage you subscript message and send a message from another page.
I Have photo.xaml page in which i have one 1 ListView whoose bindingcontext is ObservableCollection listphoto of photos which is defined in its viewmodel.cs file.
Firstly it would be worth showing your XAML code.
You say in the quote above that you set the bindingcontext of the listview to the collection. You should be setting the ItemSource property of the ListView to the collection.

Xamarin Forms Force view to bind values

I have a view. I have a bindable property there.
public partial class OrderCard : ContentView
{
public static readonly BindableProperty OrderProperty = BindableProperty.Create(nameof(Order), typeof(Order), typeof(OrderCard), null);
public Order Order
{
get { return (Order)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
public OrderCard()
{
InitializeComponent();
}
}
In the xaml of this view I'm binding to Order property like this:
Text="{Binding Order.Name, Source={x:Reference Root}}"
Root is a name in the xaml of a view OrderCard
When I use this view in the page everything works ok.
But I want to measure it's size even before adding it to the page.
var orderCard = new OrderCard { Order = order};
SizeRequest sizeRequest = orderCard.Measure(OrdersContainer.Width/5, OrdersContainer.Height);
But it gives me wrong numbers because bindings isn't applied.
How to force to apply bindings when view isn't attached to the page?
Bindings do not require being attached to a Page or anything else to be applied.
You might think they're not applied if your method for figuring out is to set a breakpoint on get_Order because that is never used, the Xaml loader uses GetValue directly. The usual way of figuring out if a Binding is correctly applied, is to add a PassthroughConverter (don't look for it, you have to write it yourself) to the Binding and put the breakpoint in the Convert method.
That being said, you can't Measure anything unless it's added to a page that is rendered on screen. If you try to Measure before that, you indeed get dummy values.
I was able to solve this problem by not doing a property Order but passing an order as BindingContext. Then I can measure the size of a view without attaching it to a page like this:
var orderCard = new OrderCard { BindingContext = order};
SizeRequest sizeRequest = orderCard.Measure(widthToTryToFitInTheView,heightToTryToFitInTheView);

How to get ContentPage's BindingContext from ContentView?

I have the following Contentpage.content, where I set certain binding context.
<StackLayout>
<local:Post />
<local:Comments />
</StackLayout>
In Post.xaml.cs (ContentView), I've tried to get the binding context of the ContentPage this way but it doesn't work.
BindingContext = (PostViewModel)Parent.BindingContext;
How can I get the binding context of the ContentPage if I'm standing in a ContentView?
By the time your constructor is called, the BindingContext might not be initialised yet.
So, you should wait for the BindingContext being changed to perform operations on it.
I think the answer is OnBindingContextChanged event.
https://developer.xamarin.com/api/member/Xamarin.Forms.View.OnBindingContextChanged()
Little sample:
protected override void OnBindingContextChanged ()
{
base.OnBindingContextChanged ();
//BindingContext should not be null at this point
// and you may add your code here.
}
Note:
If you have a ContentView inside a ContentPage, unless explicitly set by another Control (like when using an ItemTemplate for a ListView) or by your code, the BindingContext of the ContentView is the same as the ContentPage.
So, it shouldn't be necessary to call "Parent".
Let me know if more clarification is needed.
The BindingContext of the ContentView is usually also the BindingContext of the ContentPage since it is passed down from the parent.
So you should not even need to set ContentView.BindingContext if you already set the parent ContentPage.BindingContext.
If I am missing something, please let me know.

Resources