Issue
I need to set two ViewModels from the code behind in the xaml code. Or if there is better way doing would be great to.
When I do it like this way the application crashes. When I set ProductDetailViewModel in the code behind (BindingContext ViewModel) everything works fine.
update It’s not an good idea to pass viewModels as parameters. I have now one class “ViewModelLocator” which contains all the ViewModels as static properties. Use Google for more info. This way things are way easier.
example ViewModelLocator
public static class ViewModelLocator
{
public static AddProductViewModel AddProductViewModel { get; set; } new AddProductViewModel(App.ProductDataStore, App.NavigationService);
}
end update
update 2
As @Waescher stated, it’s better to use FreshMvvm. The static approach is simple and fast but not good for slow devices or larger apps. Thanks.
end update 2
**Xamarin.Forms.Xaml.XamlParseException:** 'Position 9:10. Can not find the object referenced by `ProductDetailViewModel`'
Since I can’t set the ViewModels directly in the xaml I need to do it by reference from code behind.
See < *** First ViewModel *** > and < *** Second ViewModel *** > in the xaml code.
<?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:BoerPlaza.Controls"
xmlns:flv"clr-namespace:DLToolkit.Forms.Controls;assemblyDLToolkit.Forms.Controls.FlowListView"
xmlns:ffimageloading"clr-namespace:FFImageLoading.Forms;assemblyFFImageLoading.Forms"
x:Class"BoerPlaza.Views.Product.ProductCustomerPictures">
<ContentPage.BindingContext>
<x:Reference Name"ProductDetailViewModel" /><!-- *** First ViewModel ***!-->
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<!-- Total image count -->
<Label Text"{Binding Product.UserImages.Total}"
Style"{StaticResource H2}" />
<!-- Title -->
<Label Text"{Binding Product.Title}"
Style"{StaticResource H1}" />
<!-- reviews -->
<StackLayout Orientation"Horizontal">
<controls:StarDisplayTemplateView x:Name"customRattingBar"
SelectedStarValue"{Binding Product.RatingTotal}" />
<Label Text"{Binding Product.RatingAmount, StringFormat'{0} reviews | '}" />
<Label Text"Schrijf een review" />
</StackLayout>
<Label Text"{Binding Product.Title, StringFormat'Heb je een productfoto van {0} die je wilt delen? '}" />
<Button Text"Foto's toevoegen"
Command"{Binding SelectImagesCommand}"
BackgroundColor"{StaticResource neutral-color}"
BorderColor"{StaticResource alt-color}"
BorderWidth"1"
TextColor"{StaticResource primary-color}"
HorizontalOptions"Start"
HeightRequest"40"
FontSize"12" />
<!-- hr -->
<BoxView Style"{StaticResource separator}" />
<flv:FlowListView FlowColumnCount"3"
x:Name"listItems"
FlowItemsSource"{Binding Media}"
SeparatorVisibility"None"
HasUnevenRows"false"
RowHeight"100"
HeightRequest"0">
<flv:FlowListView.BindingContext>
<x:Reference Name"MultiMediaPickerViewModel" /> <!-- *** Second ViewModel ***!-->
</flv:FlowListView.BindingContext>
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Grid>
<ffimageloading:CachedImage DownsampleToViewSize"true"
HeightRequest"100"
Source"{Binding PreviewPath}"
Aspect"AspectFill"
HorizontalOptions"FillAndExpand">
</ffimageloading:CachedImage>
<Image Source"play"
IsVisible"false"
HorizontalOptions"End"
VerticalOptions"End">
<Image.Triggers>
<DataTrigger TargetType"Image"
Binding"{Binding Type}"
Value"Video">
<Setter Property"IsVisible"
Value"True" />
</DataTrigger>
</Image.Triggers>
</Image>
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
Code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ProductCustomerPictures : ContentPage
{
public ProductDetailViewModel ProductDetailViewModel
{
get { return _productDetailViewModel; }
set { _productDetailViewModel value; }
}
public MultiMediaPickerViewModel MultiMediaPickerViewModel
{
get { return _multiMediaPickerViewModel; }
set { _multiMediaPickerViewModel value; }
}
private ProductDetailViewModel _productDetailViewModel;
private MultiMediaPickerViewModel _multiMediaPickerViewModel;
public ProductCustomerPictures(ProductDetailViewModel viewModel)
{
InitializeComponent();
ProductDetailViewModel viewModel;
MultiMediaPickerViewModel new MultiMediaPickerViewModel(MultiMediaPickerServiceStaticVariableHolder.MultiMediaPickerService);
}
}
Solution
If I understood this correctly and if you want to keep the pattern to pass in the view model as constructor argument …
public ProductCustomerPictures(ProductDetailViewModel viewModel)
{
InitializeComponent();
ProductDetailViewModel viewModel;
MultiMediaPickerViewModel new MultiMediaPickerViewModel(MultiMediaPickerServiceStaticVariableHolder.MultiMediaPickerService);
}
… then you can remove this completely …
<ContentPage.BindingContext>
...
</ContentPage.BindingContext>
… and this property …
public ProductDetailViewModel ProductDetailViewModel
{
get { return _productDetailViewModel; }
set { _productDetailViewModel value; }
}
Instead, just set the BindingContext directly in the constructor.
public ProductCustomerPictures(ProductDetailViewModel viewModel)
{
InitializeComponent();
BindingContext viewModel; // <-- here
MultiMediaPickerViewModel new MultiMediaPickerViewModel(MultiMediaPickerServiceStaticVariableHolder.MultiMediaPickerService);
}
Now, each and every control in the XAML is binding to the ProductDetailViewModel
.
But you still have the FlowListView which should bind to the MultiMediaPickerViewModel
. Instead of setting its binding context directly in XAML, it is common to use the binding with a reference, but first you have to give the whole page a name with which we can refer in the binding:
<ContentPage xmlns"http://xamarin.com/schemas/2014/forms"
...
...
x:Name"thisPage" <--- here
x:Class"BoerPlaza.Views.Product.ProductCustomerPictures">
Now, you can use the name as reference in the binding expression:
<flv:FlowListView FlowColumnCount"3"
x:Name"listItems"
FlowItemsSource"{Binding Source{x:Reference thisPage}, PathMultiMediaPickerViewModel.Media}"
SeparatorVisibility"None"
HasUnevenRows"false"
RowHeight"100"
HeightRequest"0">
"{Binding Source{x:Reference thisPage}, PathMultiMediaPickerViewModel.Media}"
uses the page itself (by name thisPage
) and binds to the property Media
of the property MultiMediaPickerViewModel
of the page.
With that, you can safely remove this code as well:
<flv:FlowListView.BindingContext>
...
</flv:FlowListView.BindingContext>
By the way, you can condense the properties in the code behind:
public MultiMediaPickerViewModel MultiMediaPickerViewModel { get; private set; }
public ProductCustomerPictures(ProductDetailViewModel viewModel)
{
InitializeComponent();
BindingContext viewModel;
MultiMediaPickerViewModel new MultiMediaPickerViewModel(MultiMediaPickerServiceStaticVariableHolder.MultiMediaPickerService);
}
Answered By – Waescher