I am currently working on a Xamarin Forms project which is using MVVMCross. In App.xaml i created a control template as follows
<ControlTemplate x:Key="ContentPageTemplate">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" RowSpacing="0" RowDefinitions="Auto,Auto,*">
<!--StackLayout as Header-->
<StackLayout Grid.Row="0" HeightRequest="6" BackgroundColor="{StaticResource SecondaryBrandColor}"></StackLayout>
<Frame Padding="0" Grid.Row="1" >
<Label Grid.Row="1" Text="Server Not Available" BindingContext="{TemplateBinding BindingContext}"
Style="{StaticResource ServerAvailableLabel}"
IsVisible="{TemplateBinding Parent.BindingContext.IsServerAvailableLabelVisible}"/>
</Frame>
<!--Content Page Body-->
<ContentPresenter Grid.Row="2" BackgroundColor="{StaticResource PrimaryBodyColor}">
</ContentPresenter>
</Grid>
</ControlTemplate>
Now i am using this control template in contentPage as follows
<views:MvxContentPage xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.UI.Views.ReqView"
ControlTemplate="{StaticResource ContentPageTemplate}"
Title="Reqs">
</views:MvxContentPage>
In the view model i am trying to make the label visible and invisible as follows
public bool _isServerAvailableLabelVisible = false;
public bool IsServerAvailableLabelVisible
{
get
{
return _isServerAvailableLabelVisible;
}
set
{
SetProperty(ref _isServerAvailableLabelVisible, value);
}
}
The problem i am facing now is, by default the label is being visible and also it is not updating even when i am setting the value as false.
What am i missing ?
I had exactly the same issue !
After one day I found the following solution which works for me.
I added BindingContext="{TemplateBinding BindingContext.DataContext}"into the Grid and change the IsVisible property by IsVisible="{Binding IsServerAvailableLabelVisible}"
Here the recap that you can try, hope it will work for you too !
For information I use Xamarin.Forms 4.8.0 & MvvmCross 6.3.1
<ControlTemplate x:Key="ContentPageTemplate">
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" RowSpacing="0" RowDefinitions="Auto,Auto,*"
BindingContext="{TemplateBinding BindingContext.DataContext}">
<!--StackLayout as Header-->
<StackLayout Grid.Row="0" HeightRequest="6" BackgroundColor="{StaticResource SecondaryBrandColor}"></StackLayout>
<Frame Padding="0" Grid.Row="1" >
<Label Grid.Row="1" Text="Server Not Available"
Style="{StaticResource ServerAvailableLabel}"
IsVisible="{Binding IsServerAvailableLabelVisible}"/>
</Frame>
<!--Content Page Body-->
<ContentPresenter Grid.Row="2" BackgroundColor="{StaticResource PrimaryBodyColor}">
</ContentPresenter>
</Grid>
</ControlTemplate>
Are you sure that the OnPropertyChanged Event gets fired when you chcange the value?
If yes then you need to check the binding context of the view
If no you need to fire it manually (as mentioned in the MVVMCross documentation)
private string _myProperty;
public string MyProperty
{
get => _myProperty;
set
{
_myProperty = value;
RaisePropertyChanged(() => MyProperty);
// take any additional actions here which are required when MyProperty is updated
}
}
Related
Is it possible to add direct content to a CarouselView in Xaml?
I need to convert an old CarouselPage to the newer CarouselView, but it looks like it must be data bound. But my content for each slide is entirely different and does not easily lend itself to an item template.
<?xml version="1.0" encoding="utf-8" ?>
<CarouselPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myCarousel"
x:Name="carouselPage"
NavigationPage.HasNavigationBar="false" BackgroundColor="#000000">
<ContentPage BackgroundColor="#000000">
<StackLayout BackgroundColor="#000000">
<Label Text="Lorem ipsum" />
<ImageButton Margin="0,20" Source="btn_continue" HorizontalOptions="Center" HeightRequest="30" Clicked="Go_Right" />
</StackLayout>
</ContentPage>
<ContentPage BackgroundColor="#000000">
<StackLayout BackgroundColor="#000000">
<Button Text="X" TextColor="White" BackgroundColor="Transparent" VerticalOptions="Start" HorizontalOptions="End" Clicked="Go_Home"></Button>
<ImageButton Source="slider-2" Aspect="AspectFill" HorizontalOptions="FillAndExpand" VerticalOptions="Start" />
<Grid Margin="0,20" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ImageButton Source="btn_back" HorizontalOptions="Center" HeightRequest="30" Grid.Column="0" Clicked="Go_Left" />
<ImageButton Source="btn_close" HorizontalOptions="Center" HeightRequest="30" Grid.Column="1" Clicked="Go_Home" />
</Grid>
</StackLayout>
</ContentPage>
</CarouselPage>
I need to create a multi-slide carousel, where each slide has different content like above. How would I convert that using CarouselViews?
The problem I am having with the CarouselPage is that it does not always behave consistently, which is why I imagine they are slowly deprecating it.
But my content for each slide is entirely different and does not
easily lend itself to an item template.
You need to create different DataTemplate for each slide and Choose item appearance at runtime, for example:
<ContentPage ...
xmlns:controls="clr-namespace:CarouselViewDemos.Controls"
x:Class="CarouselViewDemos.Views.HorizontalLayoutDataTemplateSelectorPage">
<ContentPage.Resources>
<DataTemplate x:Key="StyleATemplate">
...
</DataTemplate>
<DataTemplate x:Key="StyleBTemplate">
...
</DataTemplate>
<controls:ContentSelector x:Key="myContentSelector"
AmericanMonkey="{StaticResource StyleATemplate}"
OtherMonkey="{StaticResource StyleBTemplate}" />
</ContentPage.Resources>
<CarouselView ItemsSource="{Binding Monkeys}"
ItemTemplate="{StaticResource myContentSelector}" />
</ContentPage>
And in code behind, use DataTemplateSelector to select different template for each slide:
public class ContentSelector: DataTemplateSelector
{
public DataTemplate StyleATemplate{ get; set; }
public DataTemplate StyleBTemplate{ get; set; }
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
if(item.style == A){
return StyleATemplate;
}else{
return StyleBTemplate;
}
}
}
Yes of course you can add different content on every view.
It's necessary to create a CustomViewSelector.
Check my GitHub for a full sample code https://github.com/georgemichailou/ShaXam
Pay attention to these files
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/UI%20Helper/CustomViewSelector.cs
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/MainPage.xaml
https://github.com/georgemichailou/ShaXam/blob/master/ShaXam/MainPage.xaml.cs (Line 17-25-27)
Im trying to bind DogImage source which in my Contentview. Im trying to build reusable view objects like frame buttons. I just need to give them Image and Text from outside of contentview. Im using Images from resources folder.
this is my contentView
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
BindingContext="{Binding .}">
<ContentView.Content>
<StackLayout x:Name="breedStack" Margin="30,2,30,2">
<Frame HeightRequest="64" CornerRadius="8" BorderColor="White" HasShadow="False" BackgroundColor="White"
VerticalOptions="FillAndExpand" Padding="18,0,18,0">
<Frame.Content>
<StackLayout Orientation="Vertical" VerticalOptions="CenterAndExpand">
<Grid ColumnSpacing="18">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="9*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image x:Name="DogImage" Source="{Binding ImageSourceOf}" Grid.Row="0" Grid.Column="0" ></Image>
<Label x:Name="breedSelector" Text="Breed" FontSize="Medium" TextColor="#5f5d70" Grid.Row="0" Grid.Column="1">
</Label>
</Grid>
</StackLayout>
</Frame.Content>
</Frame>
</StackLayout>
</ContentView.Content>
</ContentView>
And this is CS file
public partial class DogsBreedPicker : ContentView
{
public static readonly BindableProperty ImageSourceProperty =
BindableProperty.Create("ImageSourceOf", typeof(ImageSource), typeof(DogsBreedPicker));
public ImageSource ImageSourceOf
{
get { return GetValue(ImageSourceProperty) as ImageSource; }
set { SetValue(ImageSourceProperty, value);}
}
public DogsBreedPicker()
{
InitializeComponent ();
BindingContext = ImageSourceOf;
}
}
And this is how i want to use it.
<views:DogsBreedPicker ImageSourceOf="dog" TextOf="Breed Selector" x:Name="DogBreed" ></views:DogsBreedPicker>
You are setting your BindingContext to ImageSourceOf in your constructor. Anyway, the properties are not set at this moment, hence ImageSourceOf is still null. However, you do not need to use BindingContext in this case anyway, since you can directly bind to the ImageSourceOf property:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="PawsApp.Views.AboutMyDogViews.DogsBreedPicker"
x:Name="View">
<!-- Elided all the other stuff -->
<Image x:Name="DogImage" Source="{Binding ImageSourceOf, Source={x:Reference View}}" Grid.Row="0" Grid.Column="0" />
</ContentView>
remove the assignment of BindingContext from your constructor.
Why does this work?
The default source of all bindings is the BindingContext (of the view, which is propagated to all child views). Anyway, you are by no means restricted to the BindingContext as the source of a binding, but can set the Source of the binding to another object. In this case we are referencing the view (which we have given the name View with x:Name="View") by its name and use it as the source of the binding of the Source of the Image. Since the Path of the binding is ImageSourceOf, the Image.Source will be bound to View.ImageSourceOf.
Content.BindingContext = this;
Solved my issue. But is there any way to make it better? Add comment please.
I use the same XAML many times in my application with two minor differences which are the value of the Text and Selected that I pass in:
<ViewCell Tapped="selectValue" >
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding [1].Name}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding [1].IsSelected}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
Does Xamarin forms have any template feature where I could for example shorten this to something like:
<local:SwitchViewCell Text="{Binding [1].Name}" Selected="{Binding [1].IsSelected}" />
Here's what I have so far:
<?xml version="1.0" encoding="utf-8" ?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
mlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.SwitchViewCell""
Tapped="selectValue" >
<Grid VerticalOptions="CenterAndExpand" Padding="20,0" >
<local:StyledLabel Text="{Binding Text, Source={x:Reference this}}" HorizontalOptions="StartAndExpand" />
<local:StyledLabel IsVisible="{Binding IsVisible, Source={x:Reference this}}" TextColor="Gray" HorizontalOptions="End" Text="✓" />
</Grid>
</ViewCell>
With this code behind right now:
namespace Japanese.Templates
{
public partial class SwitchViewCell : ViewCell
{
public SwitchViewCell()
{
InitializeComponent();
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(SwitchViewCell));
public static readonly BindableProperty IsVisibleProperty = BindableProperty.Create(nameof(IsVisible), typeof(bool), typeof(SwitchViewCell));
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public bool IsVisible
{
get
{
return (bool)GetValue(IsVisibleProperty);
}
set
{
SetValue(IsVisibleProperty, value);
}
}
}
}
I'm not sure if this is 100% the way to go but when I try to implement this I get the message:
EventHandler "selectValue" not found in type "Japanese.Templates.SwitchViewCell" (Japanese)
ListView Cell:
For the ListView cell, you can define the ViewCell layout of ListView item.
Ex. PersonCell.xaml
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DataTemplates.PersonCell">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.3*" />
<ColumnDefinition Width="0.4*" />
</Grid.ColumnDefinitions>
<Label Text="{Binding FirstName}" />
<Label Grid.Column="1" Text="{Binding LastName}" />
<Label Grid.Column="2" Text="{Binding Email}" />
</Grid>
</ViewCell>
Then you can use this into ListView's DataTemplate as:
<ListView ItemSource="{Binding PersonList}">
<ListView.ItemTemplate>
<DataTemplate>
<local:PersonCell />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This way ListView resuses the cell design for each item.
Reusable View:
You can also make a reusable view which can be simply included in your page.
Ex. MyCustomView.xaml:
<Grid xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Xam.Control.MyCustomView">
<StackLayout>
<Label Text="Hello"/>
<Label Text="How Are You?"/>
</StackLayout>
</Grid>
Page:
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customView="clr-namespace:Xam.Control;assembly=Xam"
x:Class="Xam.View.HomePage">
Here, notice that you have to include the namespace and assembly where your custom view resides.
And then in this page, simply add it like:
<customView:MyCustomView />
Sure, just put it in a separate XAML file and define a the bindable properties that you need and map the values onto the controls in your custom control. In fact, the latter is not absolutely needed but is nicer to make it completely reusable.
If you just want to reuse it in your project and always data bind it to the same fields, you can leave it as-is, as the BindingContext will be inherited.
To get you started, you might want to have a look at a blog post of mine about this: https://blog.verslu.is/xamarin/xamarin-forms-xamarin/reusable-custom-usercontrols-with-bindableproperty/
I'm trying to rewrite the sample "SimpleThemeWithTemplateBinding" that uses ControlTemplates, with Prism.
I also added a simple
HeaderText = "changed";
in the button to change the HeaderText in the ControlTemplate, and it works, in the original sample.
So I copied the template in my app.xaml:
<ControlTemplate x:Key="TealTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="0.8*" />
<RowDefinition Height="0.1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.05*" />
<ColumnDefinition Width="0.95*" />
</Grid.ColumnDefinitions>
<BoxView Grid.ColumnSpan="2" Color="Teal" />
<Label Grid.Column="1" Text="{TemplateBinding BindingContext.HeaderText}" TextColor="White" VerticalOptions="Center" />
<ContentPresenter Grid.Row="1" Grid.ColumnSpan="2" />
<BoxView Grid.Row="2" Grid.ColumnSpan="2" Color="Teal" />
<Label Grid.Row="2" Grid.Column="1" Text="(c) Xamarin 2016" TextColor="White" VerticalOptions="Center" />
</Grid>
</ControlTemplate>
and just changed
{TemplateBinding HeaderText}
to
{TemplateBinding BindingContext.HeaderText}
VIEW:
<?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:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="TestAppNSPrism7.Views.PrismContentPage9">
<ContentPage.Content>
<ContentView x:Name="contentView" Padding="0,0,0,0" ControlTemplate="{StaticResource TealTemplate}" >
<StackLayout>
<Label Text="Welcome to Xamarin.Forms! page1"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Label Text="Buazz"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
<Button Command="{Binding ButtonChangeValueCommand}" Text="Change value" ></Button>
</StackLayout>
</ContentView>
</ContentPage.Content>
</ContentPage>
VIEWMODEL:
public class PrismContentPage9ViewModel : ViewModelBase
{
ControlTemplate tealTemplate;
private string _headerText = "test";
public string HeaderText
{
get
{
return _headerText;
}
set { SetProperty(ref _headerText, value); }
}
public PrismContentPage9ViewModel(INavigationService navigationService) : base(navigationService)
{
tealTemplate = (ControlTemplate)Application.Current.Resources["TealTemplate"];
}
private DelegateCommand _buttonChangeValueCommand;
public DelegateCommand ButtonChangeValueCommand =>
_buttonChangeValueCommand ?? (_buttonChangeValueCommand = new DelegateCommand(ExecuteButtonChangeValueCommand));
void ExecuteButtonChangeValueCommand()
{
HeaderText = "changed";
}
}
The Page gets loaded correctly, with the ControlTemplate, and the HeaderText is "test".
So it seems the HeaderText binding with the ControlTemplate is working.
But when I set the HeaderText to "changed", the Label doesn't get updated.
I debugged and checked that once I press the button it goes through ExecuteButtonChangeValueCommand() and SetProperty(ref _headerText, value)
Any suggestion?
Thanks!
I changed the TemplateBinding from :
Text="{TemplateBinding BindingContext.HeaderText}"
to:
Text="{TemplateBinding Parent.BindingContext.HeaderText}"
and it updates now when I press your changed button.
I believe its due to the template not having a binding context automatically set but the template's parent (PrismContentPage9) has its BindingContext auto-wired from Prism's AutowireViewModel property, e.g.,
prism:ViewModelLocator.AutowireViewModel="True"
Let me know if that works for you.
I m using Vs for mac: 7.5.1(Build 22). I try to build a Master Detail page with ListView binding to a simple class.
Problem encountered: Specified cast is not valid.
In the code behind: I have red underline for InitializeComponent as well as for the ListView. Why?
In the SideMenu.xaml: Do I need to add this?
xmlns:local="clr-namespace:MyNavigationSideMenu"
Here the code:
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MyNavigationSideMenu;assembly=MyNavigationSideMenu"
x:Class="MyNavigationSideMenu.MySideMenu">
<MasterDetailPage.Master>
<ContentPage Title="Menu">
<Grid BackgroundColor ="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height ="200"/>
<RowDefinition Height ="*"/>
</Grid.RowDefinitions>
<Grid>
<Image Source="bg.png" Aspect="AspectFill" />
<StackLayout Padding="0,20,0,0" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand">
<Image Source="home.png" Aspect="AspectFit" WidthRequest="60" HeightRequest="60" />
<Label Text="Xamarin Buddy" TextColor="White" FontSize="Large" />
</StackLayout>
</Grid>
<StackLayout Grid.Row="1" Spacing="15">
<ListView x:Name ="navigationLV"
RowHeight ="60"
SeparatorVisibility ="None"
BackgroundColor ="#e8e8e8"
ItemSelected="OnMenuItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout VerticalOptions="FillAndExpand"
Orientation="Horizontal"
Spacing="20">
<Image Source="{Binding Icon}"
WidthRequest="30"
HeightRequest="30"
VerticalOptions="Center"/>
<Label Text="{Binding Title}"
FontSize="Medium"
VerticalOptions="Center"
TextColor="Black"/>
</StackLayout>
<BoxView
HeightRequest="1"
BackgroundColor="Gray"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</Grid>
</ContentPage>
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
<NavigationPage>
</NavigationPage>
</MasterDetailPage.Detail>
here the code behind of SideMenu:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using MyNavigationSideMenu.MenuItems;
namespace MyNavigationSideMenu
{
public partial class MySideMenu : MasterDetailPage
{
public List<MasterPageItem> menuList { get; set; }
public MySideMenu()
{
InitializeComponent();
menuList = new List<MasterPageItem>();
// Adding menu items to menuList and you can define title ,page and icon
menuList.Add(new MasterPageItem() { Title = "Home", Icon = "home.png", TargetType = typeof(HomePage) } );
navigationLV.ItemsSource = menuList;
// Initial navigation, this can be used for our home page
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(HomePage)));
}
private void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (MasterPageItem)e.SelectedItem;
Type page = item.TargetType;
Detail = new NavigationPage((Page)Activator.CreateInstance(page));
IsPresented = false;
}
}
}
You need to check all these 3 steps:
1.Save the MySideMenu.xaml file & check red underline has gone.
2.Right click on MySideMenu.xaml & click on Properties & check Build Action should be set to "Embedded resource".
3.Add Try Catch for Initialize component in MySideMenu.xaml file & check which line the error is getting displayed.