This is my first Xamarin Project and already, I've run into a weird issue.
Fromt he MainPage.xaml, I'm triyng to call the ItemDetailsPage with ItemFetailViewModel as a param.
Upon compilation, VS throws "The type or namespace name 'ItemDetailsPage' could not be found". There is no red underlines.
I've checked that the namaespaces are correct in both the .xaml and .xaml.cs
MainPage.xaml.cs
namespace testapp3.Views
public partial.....
{
Method()
{
await Navigation.PushAsync(new ItemDetailsPage(new ItemDefailViewModel(item)
}
}
ItemDetailsPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="testapp3.Views.ItemDetailsPage">
</ContentPage>
ItemDetailsPage.xaml.cs
namespace testapp3.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ItemDetailsPage : ContentPage
{
ItemDetailViewModel viewModel;
public ItemDetailsPage ()
{
InitializeComponent ();
}
public ItemDetailsPage(ItemDetailViewModel item)
{
InitializeComponent();
BindingContext = this.viewModel = item;
}
}
}
Related
I have a ContentView called HomePageOrientationViewLoader that I want to use in a ContentPage called HomePage. HomePageOrientationViewLoader will either load a ContentView called HomePageLandscape if the orientation is in Landscape or a ContentView called HomePagePortrait if the orientation is in Portrait.
I am doing this so that I can load a different layout for landscape vs portrait so I can optimize my layout.
My issue is that I use dependency injection to inject my ViewModel HomeViewModel. I inject this into the ContentPage HomePage and I am attempting to pass the HomeViewModel from the ContentPage HomePage's XAML into the markup for HomePageOrientationViewLoader.
Here is my HomePage.xaml.cs code behind for my ContentPage:
using ScoreKeepersBoard.ViewModels;
namespace ScoreKeepersBoard.Views;
public partial class HomePage : ContentPage
{
public HomePage(HomeViewModel homeViewModelInj)
{
HomeViewModel = homeViewModelInj;
BindingContext = homeViewModelInj;
InitializeComponent();
}
HomeViewModel HomeViewModel { get; set; }
protected override void OnNavigatedTo(NavigatedToEventArgs args)
{
((HomeViewModel)BindingContext).CreateInitialGameTypes();
base.OnNavigatedTo(args);
}
}
Here is my HomePage.xaml for the ContentPage:
<?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:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
x:Class="ScoreKeepersBoard.Views.HomePage"
Title="HomePage">
<VerticalStackLayout>
<controls:HomePageOrientationViewLoader
BindingContext="{Binding HomeViewModel}"
>
</controls:HomePageOrientationViewLoader>
</VerticalStackLayout>
</ContentPage>
Here is my HomePageOrientationViewLoader.xaml.cs code behind for my ContentView:
using System.Reflection;
using ScoreKeepersBoard.ViewModels;
namespace ScoreKeepersBoard.ContentViews;
public partial class HomePageOrientationViewLoader : ContentView
{
public ContentView homePagePortraitContentView;
public ContentView homePageLandscapeContentView;
public HomeViewModel HomeViewModel { get; set; }
public HomePageOrientationViewLoader()
{
InitializeComponent();
try
{
//homeVM is always null
HomeViewModel homeVM = ((HomeViewModel)BindingContext);
string entryValue = homeVM.EntryValue;
homePagePortraitContentView = new HomePagePortrait(homeVM);
homePageLandscapeContentView = new HomePageLandscape(homeVM);
}
catch (Exception e)
{
string message = e.Message;
}
DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? homePagePortraitContentView : homePageLandscapeContentView;
}
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
{
this.Content = homePageLandscapeContentView;
}
else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
{
this.Content = homePagePortraitContentView;
}
else
{
this.Content = homePagePortraitContentView;
}
}
}
The code compiles and runs,
but the issue is that the BindingContext on HomePageOrientationViewLoader is always null. I would expect this to be set from the property defined in the ContentView's markup in HomePage ContentPage.
I also tried to set HomePageOrientationViewLoader's markup in ContentView's markup in HomePage ContentPage as just a normal Property defined on HomePageOrientationViewLoader as such:
<?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:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
x:Class="ScoreKeepersBoard.Views.HomePage"
Title="HomePage">
<VerticalStackLayout>
<controls:HomePageOrientationViewLoader
HomeViewModel = "{Binding HomeViewModel}">
</controls:HomePageOrientationViewLoader>
</VerticalStackLayout>
</ContentPage>
But this won't even compile. I get the following error:
/Users/RemoteCommand/Projects/ScoreKeepersBoard/Views/HomePage.xaml(13,13): Error XFC0009: No property, BindableProperty, or event found for "HomeViewModel", or mismatching type between value and property. (XFC0009)
This is obviously not true since HomeViewModel is a Property on both HomePage ContentPage and HomePageOrientationViewLoader ContentView.
I need to be able to have HomeViewModel in HomePageOrientationViewLoader and then to pass this on to HomePageLandscape and HomePagePortrait ContentViews so they can share the same ViewModel when the user switches between Portrait and Landscape view but I am so far not able to get this working. I would appreciate any help.
UPDATE
Jason commented: " If you want the control to have the same BindingContext as the page, you don't need to do anything - that should automatically inherit."
I just tried to remove the binding property on the control markup as such:
<?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:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
x:Class="ScoreKeepersBoard.Views.HomePage"
Title="HomePage">
<VerticalStackLayout>
<controls:HomePageOrientationViewLoader>
</controls:HomePageOrientationViewLoader>
</VerticalStackLayout>
</ContentPage>
And when my HomePageOrientationViewLoader page loads the constructor the HomeViewModel from HomePageOrientationViewLoader's BindingContext is still null:
UPDATE 2
Jason said that you should not assume the BindingContext is set in the constructor. He was right. When my HomePageOrientationViewLoader ContentView first loads the ContentView is not set but when I change the orientation and check to see what the BindingContext is as such:
private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{
HomeViewModel homeVM = ((HomeViewModel)BindingContext);
homePageLandscapeContentView.BindingContext = homeVM;
homePagePortraitContentView.BindingContext = homeVM;
...
}
homeVM which is set from the BindingContext of HomePageOrientationViewLoader is no longer null. I can then set the BindingContext of HomePageLandscape and HomePagePortrait and their ViewModels are active and bound. The only issue for me is that when the page initially loads HomePageLandscape and HomePagePortrait don't have their BindingContext set.
How can I get around this? Is there some event on ContentView that gets triggered when BindingContext is set so I could then override that event and bind HomePageLandscape and HomePagePortrait's Binding Context to the BindingContext of HomePageOrientationViewLoader?
ContentView has an OnBindingContextChanged method you can override
I want to implement something like below in the Xamarin forms.
I don't see such implementation with Objective C so can't find a way to render it through Native either.
How do I implement this with Xamarin forms?
https://demo.mobiscroll.com/javascript/list/display#
Working Solution: what follows will get your two-column UIPickerView rendered from Xamarin.Forms, belatedly updated from my original post
Option 1: Custom Renderer
I used the documentation #Junior Jiang shared in comments to inspire a quick Custom Renderer that should suit your needs.
First, make a placeholder control in your shared/Xamarin.Forms project:
namespace samples.core.Controls
{
public class MultiPickerControl : Xamarin.Forms.StackLayout
{
public MultiPickerControl()
{
// Just a placeholder
}
}
}
Then, make the renderer in your iOS project:
[assembly: ExportRenderer(typeof(samples.core.Controls.MultiPickerControl), typeof(samples.iOS.Controls.Picker.MultiPickerRenderer))]
namespace samples.iOS.Controls.Picker
{
public class MultiPickerRenderer : ViewRenderer
{
static UIPickerView pickerControl;
static DemoModel pickerModel = new DemoModel(new UILabel());
public MultiPickerRenderer()
{
pickerControl = new UIPickerView(
new CGRect(
UIScreen.MainScreen.Bounds.X - UIScreen.MainScreen.Bounds.Width,
UIScreen.MainScreen.Bounds.Height - 230,
UIScreen.MainScreen.Bounds.Width,
180))
{
Model = pickerModel
};
}
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.Add(pickerControl);
}
if(e.NewElement != null)
{
(e.NewElement as StackLayout).Children.Add(pickerControl);
}
}
}
}
You'll also need a UIPickerViewModel to give to your picker. This example is derived from the documentation above:
public class DemoModel : UIPickerViewModel
{
public Dictionary<string, string[]> options = new Dictionary<string, string[]>()
{
{ "America", new string[] { "Mexico", "USA" } },
{ "Europe", new string[] { "Germany", "France", "Italy"} },
{ "Asia", new string[] { "Korea", "Japan"} },
};
public override nint GetComponentCount(UIPickerView pickerView) => 2; // This determines how many columns are rendered
public override nfloat GetComponentWidth(UIPickerView picker, nint component) => component == 0 ? 120f : 160f;
public override nfloat GetRowHeight(UIPickerView picker, nint component) => 40f;
/// <summary>
/// Determines the number of rows to render in a component
/// </summary>
public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
{
if (component == 0)
return options.Keys.Count;
else
{
var driver = pickerView.SelectedRowInComponent(0);
return options.Values.ElementAt((int)driver).Length;
}
}
/// <summary>
/// Gets the display value for a row in a component
/// </summary>
public override string GetTitle(UIPickerView pickerView, nint row, nint component)
{
if (component == 0)
return options.Keys.ElementAt((int)row);
else
{
var driver = pickerView.SelectedRowInComponent(0);
return options.Values.ElementAt((int)driver).ElementAt((int)row);
}
}
[Export("pickerView:didSelectRow:inComponent:")]
public override void Selected(UIPickerView pickerView, nint row, nint component)
{
// Update the display for column 2 if the value of column 1 has changed
if (component == 0)
{
pickerView.ReloadComponent(1);
pickerView.Select(0, 1, false);
}
}
Lastly, reference your placeholder control in the markup (or code behind). Xamarin will resolve the platform implementation when your code is run.
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:samples.core.Controls;assembly=samples.core"
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Content>
<StackLayout Padding="15,25">
<controls:MultiPickerControl x:Name="MultiPicker"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
The result should look like this:
Be sure to hook into the events available
I've added the above example to a samples repo on my GitHub, if you'd like to browse as a unified source.
Option 2: Native View
You also might be able to accomplish this with a Native View (documentation). The gist would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Content>
<StackLayout Padding="15,25">
<ios:UIPickerView>
<!-- Manipulate UIPickerView Properties Here -->
</ios:UIPickerView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
As with the linked documentation above, be sure to set GetComponentCount in your UIPickerViewModel to indicate how many columns you'll show.
Good luck!
I am trying to create a contentview which has a listview xamarinforms.
I want to set a property of the contentview that is then used to bind the data to the listview.
Unfortunately I am not able to populate it.
I broke down the example to get a poc. The desired result should be a contentpage with all the names.
Any helb appreciated. Thx in advance!
The example consists of:
Contentpage:
Adds the contentview.
It also has a bindingcontext to a viewmodel - CompanyVM.
Sets Property PersonList of contentview to PersonVM.Personlist. (Unsure if correct)
Contentview.XAML:
XAML of contentview
Bindings for listview (unsure if correct)
Contentview.cs
XAML code-behind
Property Settings for contentview
CompanyVM:
Viewmodel used
Company & Person & Mockup
Simple classed to work with
Example
ContentMainPage
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:ContentViewExample.XAML"
xmlns:vm="clr-namespace:ContentViewExample.ViewModel"
xmlns:local="clr-namespace:ContentViewExample"
x:Class="ContentViewExample.MainPage"
x:Name="mainpage">
<ContentPage.BindingContext>
<vm:CompanyVM/>
</ContentPage.BindingContext>
<StackLayout>
<cv:personlistCV Company="{x:Reference mainpage }"/>
<!--Is this correct?-->
</StackLayout>
</ContentPage>
```
Contentview.XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ContentViewExample.XAML.personlistCV"
x:Name="cvPersons">
<ContentView.Content>
<StackLayout>
<ListView x:Name="lstPerson"
ItemsSource="{Binding Company.Persons}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Path=Name}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentView.Content>
</ContentView>
Contentview.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ContentViewExample.XAML
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class personlistCV : ContentView
{
public personlistCV ()
{
InitializeComponent ();
}
//CompanyProperty
public static readonly BindableProperty CompanyProperty =
BindableProperty.Create(
"Company",
typeof(CompanyVM),
typeof(personlistCV),
null);
public personlistCV Company
{
set { SetValue(CompanyProperty, value); }
get { return (personlistCV)GetValue(CompanyProperty); }
}
}
}
CompanyVM
namespace ContentViewExample.ViewModel
{
public class CompanyVM: ViewModelBase
{
ObservableCollection<Person> persons;
string companyname;
public CompanyVM()
{
companyname = "Test Company";
persons = new ObservableCollection<Person>();
foreach (Person item in MockData.GetPeople())
persons.Add(item);
}
public string Company
{
set { SetProperty(ref companyname, value); }
get { return companyname; }
}
public ObservableCollection<Person> Persons
{
set { SetProperty(ref persons, value); }
get { return persons; }
}
}
}
Company & Person
public class Company
{
public string Name { get; set; }
}
public class Person
{
public string Name{get;set;}
}
public static class MockData
{
public static List<Person> GetPeople()
{
List<Person> tmp = new List<Person>
{
new Person
{
Name="Ted"
},
new Person
{
Name="Jennifer"
},
new Person
{
Name="Andy"
},
new Person
{
Name="Oscar"
}
};
return tmp;
}
}
You have tried to bind personlistCV.Company the following way
<StackLayout>
<cv:personlistCV Company="{x:Reference mainpage }"/>
<!--Is this correct?-->
</StackLayout>
I see several issues here:
Bindings are set with the XAML extension Binding
The Company is set to the mainpage, which is of Type MainPage.
It should rather be set to mainpage.BindingContext (this is a CompanyCV object)
Furthermore the personlistCV.Company is of type personlistCV, which does not really make sense. The field should be of type CompanyVM, since we'd like to bind the viewmodel (and personlistCV does not even have a Persons (bindable) property).
Having said that, the following code should work:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:cv="clr-namespace:ContentViewExample.XAML"
xmlns:vm="clr-namespace:ContentViewExample.ViewModel"
xmlns:local="clr-namespace:ContentViewExample"
x:Class="ContentViewExample.MainPage"
x:Name="mainpage">
<ContentPage.BindingContext>
<vm:CompanyVM/>
</ContentPage.BindingContext>
<StackLayout>
<cv:personlistCV Company="{Binding Path=BindingContext, Source={x:Reference mainpage}}"/> <!-- Bind the element to `mainpage.BindingContext` -->
</StackLayout>
</ContentPage>
Maybe
<cv:personlistCV Company="{Binding .}"/>
could work, too, since . usually binds to the BindingContext of the view and the BindingContext of the page is passed down to the views (unless another BindingContext is set for the views explicitly).
And for the companyCV
public partial class personlistCV : ContentView
{
public personlistCV ()
{
InitializeComponent ();
}
//CompanyProperty
public static readonly BindableProperty CompanyProperty =
BindableProperty.Create(
"Company",
typeof(CompanyVM),
typeof(personlistCV),
null);
public CompanyVM Company
{
set { SetValue(CompanyProperty, value); }
get { return (personlistCV)GetValue(CompanyProperty); }
}
}
I've got Xamarin.Forms project with pcl-part and native win, ios and android parts.
All page structure and view-models are in pcl-part. App work's fine, but when I'm trying for example to hide Grid from code behind - it do nothing. Here is code example:
Xaml:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SomeNamespase.SomePage">
<Grid x:Name="InnerGrid" BackgroundColor="Green">
<Frame x:Name="InnerContent"/>
</Grid>
</ContentPage>
.cs :
using System;
namespace SomeNamespase
{
public partial class SomePage : ContentPage
{
public void SomeMethod()
{
this.InnerGrid.IsVisible = false;
this.InnerContent.BackgroundColor = Color.Aqua;
}
}
}
I've also tried this.FindByName<Grid>("InnerGrid"); the same result
Note: if I am trying to get controls from action in PCL everything is good. Nothing going on when I'm trying to get controls from ViewPresenter in windows (or other platforms) project.
You need to make sure you are properly implementing INotifyPropertyChanged
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Please try the below code, as in your code I can't see the constructor.
using System;
namespace SomeNamespase
{
public partial class SomePage : ContentPage
{
public SomePage()
{
SomeMethod() ;
}
public void SomeMethod()
{
this.InnerGrid.IsVisible = false;
this.InnerContent.BackgroundColor = Color.Aqua;
}
}
}
I have the following page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:XamFormsBle.Pages;assembly=XamFormsBle"
x:Name="ContentPageContainer"
x:Class="XamFormsBle.Pages.ConnectPage">
<!--This does not work-->
<ContentPage.BindingContext>
<pages:ConnectPage/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<Button Text="Refresh"
Clicked="RefreshDevicesList"/>
<ListView ItemsSource="{Binding DevicesList}"/>
</StackLayout>
</ContentPage>
And the code behind:
public partial class ConnectPage : ContentPage
{
public ObservableCollection<string> DevicesList { get; set; }
public ConnectPage()
{
InitializeComponent();
DevicesList = new ObservableCollection<string>();
//BindingContext = this;
}
public void RefreshDevicesList(object sender, EventArgs e)
{
DevicesList.Add("device");
}
}
What I am trying to achieve is to bind the ListView to the DevicesList. It works when I uncomment the BindingContext line in the constructor. I want to move that line into the .xaml itself. Researching the matter leads to the ContentPage.BindingContext block in the .xaml, but that crashes the program. There also seems to be the approach of setting the Source inside the binding of the ListView's ItemsSource, but I don't understand the syntax to work it in my case (I'm new to Xamarin.Forms and XAML in general). Is there a way to set the BindingContext inside .xaml?
You're using MVVM wrong. You're trying to set viewmodel to the view itself. Create a seperate class for the viewmodel, and it shouldn't be deriving from a ContentPage as you did.