Changing Label which is nested inside a CollectionView - c#

In my RecentProductsCV CollectionView, I have two <Label>s named PPriceLabel and PLastPriceLabel:
<CollectionView x:Name="RecentProductsCv" SelectionMode="Single">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical" Span="2"/>
</CollectionView.ItemsLayout>
<CollectionView.EmptyView>
<Label Text="No Product found." HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"/>
</CollectionView.EmptyView>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Frame CornerRadius="10" HeightRequest="90" WidthRequest="90" Grid.Row="0">
<Image Source="{Binding ProductImage}" Aspect="AspectFit" HeightRequest="90" WidthRequest="90"/>
</Frame>
<Label Text="{Binding ProductName}" TextColor="Black" FontSize="Subtitle" Grid.Row="1"/>
<Label x:Name="PPriceLabel" Text="{Binding ProductPrice, StringFormat='BDT {0}'}" TextColor="#e67e22" FontSize="Caption" Grid.Row="2"/>
<Label x:Name="PLastPriceLabel" Text="{Binding ProductLastPrice, StringFormat='BDT {0}'}" TextDecorations="Strikethrough" FontSize="Micro" Grid.Row="3"/>
<StackLayout Orientation="Horizontal" Grid.Row="4">
<Label Text="{Binding ProductRatings, StringFormat='({0}/5)'}" TextColor="LightGray" FontSize="Caption"/>
<Image Source="ratingStar.png" Aspect="AspectFit" HeightRequest="25" WidthRequest="25"/>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
I want to disable PLastPriceLabel if the values of PPriceLabel and PLastPriceLabel are the same.

In your ViewModel, you can add a new property to control whether the PLastPriceLabel is visible or not:
public class myViewModel
{
public bool isAvailable { get; set; }
public string ProductPrice { get; set; }
public string ProductLastPrice { get; set; }
public myViewModel()
{
isAvailable = true;
getData();
}
void getData()
{
if (ProductPrice == ProductLastPrice)
{
isAvailable = false;
}
}
}
In your collectionView, bind the isAvailable to the isVisible property in the Xaml:
<Label x:Name="PLastPriceLabel" IsVisible="{Binding isAvailable}" Text="{Binding ProductLastPrice, StringFormat='BDT {0}'}" TextDecorations="Strikethrough" FontSize="Micro" Grid.Row="3"/>
Then PLastPriceLabel will not be visible when PPriceLabel & PLastPriceLabel value is the same.

Xamarin Community Toolkit has a NotEqualConverter you can use to do this
<Label Text="{Binding ProductLastPrice, StringFormat='BDT {0}'}"
TextDecorations="Strikethrough" FontSize="Micro" Grid.Row="3"
IsVisible="{Binding ProductLastPrice, Converter={StaticResource NotEqualConverter},
ConverterParameter={Binding ProductPrice}}" />

Related

Xamarin Forms: How to make dynamically created items clickable?

In my application I am adding menu items dynamically.
Is it possible to make items clickable so that it can take me to, for example, "Page2" or "Page3"?
Here is my code in AppShell.xaml.cs
public ObservableCollection<dynamic> FlyoutItems { get; set; }
public AppShell()
{
FlyoutItems = new ObservableCollection<dynamic>()
{
new { MenuTitle="MenuTitle1" },
new { MenuTitle="MenuTitle2" },
new { MenuTitle="MenuTitle3" },
new { MenuTitle="MenuTitle4" }
};
InitializeComponent();
BindingContext = this;
}
AppShell.xaml
<Shell.FlyoutContentTemplate>
<DataTemplate>
<StackLayout>
<ListView x:Name="MenuItemsListView"
SeparatorVisibility="None"
HasUnevenRows="true"
ItemsSource="{Binding FlyoutItems}">
<ListView.Header>
<Grid BackgroundColor="CornflowerBlue">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="80"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Label Grid.Row="2" Grid.Column="1"
TextColor="#d7d9b4"
Text="{Binding Title}"
FontSize="24"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="15,10"
HorizontalOptions="FillAndExpand">
<Label VerticalOptions="FillAndExpand"
VerticalTextAlignment="Center"
Text="{Binding Type}"
TextColor="Black"
FontSize="20"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</DataTemplate>
</Shell.FlyoutContentTemplate>
I think I need to create a button but I am not quite sure how to do it.
Hope someone can help.
Thanks!

Binding to a parent's context in xamarin forms

I just can't get my head around it. My brain melts trying to understand how to bind to a parent's context.
So here I have a (relatively) simple object:
public class BinDefinition
{
public string labelAdresse { get; set; }
public InventoryItem inventoryItem { get; set; }
public List<BinContent> binContents { get; set; }
public List<InventoryProduct> productsinBinContent { get; set; }
public bool showDeleteButton { get; set; }
public bool showTransferButton { get; set; }
public ObservableCollection<ListItem> Items { get; set; }
}
then I have my view model that has two of these bindefinitions.
public class viewModel
{
public binDefinition primaryBin { get; set; }
public binDefinition secondaryBin { get; set; }
}
And finally, the relevant xaml part:
<ListView x:Name="ItemsListView" Grid.Row="1"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HasUnevenRows="true"
RefreshCommand="{Binding LoadItemsCommand}"
IsPullToRefreshEnabled="true"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
CachingStrategy="RecycleElement"
ItemTapped="ItemsListView_ItemTapped"
Grid.RowSpan="2"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid BackgroundColor="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
<ColumnDefinition Width="60"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Text="{Binding Name}" LineBreakMode="WordWrap" FontAttributes="Bold" FontSize="16" HorizontalOptions="FillAndExpand" />
<Label Grid.Row="1" Text="{Binding EANReference}" LineBreakMode="WordWrap" TextColor="Gray" FontSize="16" HorizontalOptions="FillAndExpand" />
<Label Grid.Row="2" Text="{Binding QuantityDisplay}" LineBreakMode="WordWrap" TextColor="DarkBlue" FontSize="16" HorizontalOptions="FillAndExpand" />
<Button Grid.RowSpan="3" Grid.Column="1" Text="Info" Clicked="btnInfos_Clicked" CommandParameter="{Binding EAN}"/>
<Button Grid.RowSpan="3" Grid.Column="2" Text="Del" Clicked="btnDel_Clicked" CommandParameter="{Binding Id}" IsVisible="{Binding BindingContext.primaryBin.showDeleteButton, Source={x:Reference Inv2PageMainContent}}"/>
<Button Grid.RowSpan="3" Grid.Column="2" Text="Tra" Clicked="btnTransfer_Clicked" CommandParameter="{Binding Id}" IsVisible="{Binding BindingContext.primaryBin.showTransferButton, Source={x:Reference Inv2PageMainContent}}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I am trying to bind the two final button so that the show up or hide depending on the state of the boolean available in BinDefinition. But at that point, the datacontext of said button is a ListItem (from the Items in the viewModel).
I just don't know how to call back the direct parent to use its property.
edit: apologies, it seems I failed to provide sufficient informations.
the inv2MainpageContent is the name of the page. x:Name = inv2MainPageContent.
The BindingContext of the page is an instance of the viewModel. That viewModel contain two binDefinitions. And in that bin Definition are multiple properties. The three that are important to me are the bool ShowDelete/ShowTransfer and the Collection Items.
The List view in my view has dug into the primaryBin and uses "primaryBin.Items" as its ItemSource. The thing is, I would like to bind the visibility of these objects to the the primaryBin.ShowDelete/ShowTransfer.

How to databind a class object to a ViewCell?

I need to databind a class object to a custom ViewCell. I used the {Binding propName} for the fields on the controls inside the ViewCell. I'm not sure if I am adding the ViewCell correctly since it shows as [AppName.GridView] and does not show the control I built. What is the correct way to bind this control with the custom class?
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Height="50">
<Frame HeightRequest="50" Padding="0" Margin="1" BorderColor="blue" CornerRadius="4" BackgroundColor="lightBlue">
<Grid BindingContext="Binding local:di" >
<Grid.RowDefinitions >
<RowDefinition Height="34"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="Transparent" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Button x:Name="btnItem" Text="{Binding itemName}" Clicked="BtnClicked" Padding="0" Grid.Column="1" FontSize="20" />
<Label x:Name="lbAmt" Text="{Binding amt}" Grid.Column="2" Margin="2" VerticalTextAlignment="Center" HorizontalTextAlignment="End" FontSize="20" BackgroundColor="lightBlue" />
<Label x:Name="lbType" Text="{Binding amtType}" Grid.Column="3" Margin="2" VerticalTextAlignment="Center" FontSize="20" BackgroundColor="lightBlue" />
</Grid>
...
</Grid>
</Frame>
The code to add the object.
DItem di = new DItem() { itemName = "someName", amt = 1, amtType = xf };
GridItems gi = new GridItems() { di = di };
ObservableCollection<GridItems> lv = new ObservableCollection<GridItems>();
lv.Add(gi);
lvItems.ItemsSource = lv;
If you want to achieve the databinding with listview(MVVM),
Here is running screenshot.
First of all, you can create a model DItem.I achieve the INotifyPropertyChanged interface, layout will changed when the value was changed
public class DItem: INotifyPropertyChanged
{
string _itemName;
public string itemName
{
set
{
if (_itemName != value)
{
_itemName = value;
OnPropertyChanged("itemName");
}
}
get
{
return _itemName;
}
}
int _amt;
public int amt
{
set
{
if (_amt != value)
{
_amt = value;
OnPropertyChanged("amt");
}
}
get
{
return _amt;
}
}
string _amtType;
public string amtType
{
set
{
if (_amtType != value)
{
_amtType = value;
OnPropertyChanged("amtType");
}
}
get
{
return _amtType;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then, achieve the ModelView.DItemViewModel, We do not need create GridItems just add the DItem to the ObservableCollection<DItem>
public class DItemViewModel
{
public ObservableCollection<DItem> lv { get; set; }
public DItemViewModel()
{
DItem di = new DItem() { itemName = "someName", amt = 1, amtType = "xf" };
lv = new ObservableCollection<DItem>();
lv.Add(di);
}
}
Here is layout of listview. add the ItemsSource="{Binding lv}" for listview.
<ListView ItemsSource="{Binding lv}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame HeightRequest="50" Padding="0" Margin="1" BorderColor="blue" CornerRadius="4" BackgroundColor="lightBlue">
<Grid >
<Grid.RowDefinitions >
<RowDefinition Height="34"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" BackgroundColor="Transparent" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions >
<RowDefinition Height="40"></RowDefinition>
</Grid.RowDefinitions>
<Button x:Name="btnItem" Text="{Binding itemName}" Clicked="btnItem_Clicked" Padding="0" Grid.Column="1" FontSize="20" />
<Label x:Name="lbAmt" Text="{Binding amt}" Grid.Column="2" Margin="2" VerticalTextAlignment="Center" HorizontalTextAlignment="End" FontSize="20" BackgroundColor="lightBlue" />
<Label x:Name="lbType" Text="{Binding amtType}" Grid.Column="3" Margin="2" VerticalTextAlignment="Center" FontSize="20" BackgroundColor="lightBlue" />
</Grid>
</Grid>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And here is background code for the listview.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new DItemViewModel();
}
private void btnItem_Clicked(object sender, EventArgs e)
{
}
}
Here are helpful articles about MVVM in Xamarin forms.
https://almirvuk.blogspot.com/2017/02/xamarinforms-listview-simple-mvvm.html
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm

XAML Binding issues in Xamarin.Forms

I have created a ViewCell that can be reused throughout my project:
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:C="using:****.Converters"
x:Class="****.Views.Settings.SettingCell">
<Grid BackgroundColor="{StaticResource BlueGray}">
<Grid.Resources>
<C:DebugConverter x:Key="debugConverter"/>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="{Binding ImageSource}"/>
<Label Grid.Column="1"
Text="{Binding Path=Title,
Converter={StaticResource debugConverter},
FallbackValue=Title}"
HorizontalOptions="Start"
VerticalOptions="Center"/>
</Grid>
</ViewCell>
Here is ViewModel:
public partial class SettingCell : ViewCell
{
public SettingModel Model
{
get
{
return model;
}
set
{
model = value;
BindingContext = value;
}
}
public SettingModel model;
public static readonly BindableProperty SettingTypeProperty = BindableProperty.Create(nameof(SettingType), typeof(Setting), typeof(SettingModel));
public Setting SettingType
{
get
{
return (Setting)GetValue(SettingTypeProperty);
}
set
{
SetValue(SettingTypeProperty, value);
}
}
public SettingCell()
{
InitializeComponent();
switch (SettingType)
{
case Setting.Name:
Model = new NameSettingModel();
break;
default:
throw new NotImplementedException("Unknown Setting Type");
}
}
}
The Model is a subtype of an abstract class that is selected through a class discriminator. The class is very simple at this point since I'm just getting it wired up, but here is the base class along with on sub-type:
public class NameSettingModel : SettingModel
{
public NameSettingModel()
{
Title = "Name";
IconSource = "";
}
public override void ClickCommand()
{
Debug.WriteLine("Setting Command Run");
}
}
public abstract class SettingModel : BindableBase
{
public string Title
{
get
{
return GetProperty<string>();
}
set
{
SetProperty(value);
}
}
public string IconSource
{
get
{
return GetProperty<string>();
}
set
{
SetProperty(value);
}
}
public abstract void ClickCommand();
}
BindableBase is just a base binding class that implements INotifyPropertyChanged and stores property values in a dictionary so I don't have to make a field for each property.
When I run the project; the fallback value shows up each time. The thing that strikes me as odd is that when I put a break in my debugConverter which is as follows:
public class DebugConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
I see the value "Name" coming through the converter but the UI only ever shows the fallback value. Any ideas? Any reason that the binding would get the correct value but just not refresh to display it? I'm at a loss here. I'm happy to post any more code that could be helpful, just leave me a comment.
Thanks!
=========================== Edit =============================
The plot thickens. I now have a viewcell that is directly in my listview and the template I have inserted is just a Grid.
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Images="clr-namespace:MyApp.Images;assembly=MyApp"
xmlns:Converters="clr-namespace:MyApp.Views.Converters"
x:Class="MyApp.Views.Templates.SettingTemplate"
BackgroundColor="White" VerticalOptions="Center">
<Grid.Resources>
<Converters:SettingToIconConverter x:Key="SettingToIconConverter"/>
<Converters:InverseBoolConverter x:Key="InverseBoolConverter"/>
</Grid.Resources>
<StackLayout Orientation="Horizontal" VerticalOptions="Center" HorizontalOptions="Center">
<Images:VectorImage ResourceId="{Binding Converter={StaticResource SettingToIconConverter}}"
WidthRequest="30" HeightRequest="30" Margin="2" VerticalOptions="Center"/>
<Label Text="{Binding Name, FallbackValue=Name}" FontAttributes="Bold" FontSize="Medium" Margin="2"
VerticalOptions="Center"/>
<ContentPresenter Content="{Binding BonusContent}" VerticalOptions="Center" Margin="2"/>
</StackLayout>
<Frame BackgroundColor="{StaticResource Slate}" Opacity="0.25" IsVisible="{Binding IsEnabled, UpdateSourceEventName=ValueChanged, Converter={StaticResource InverseBoolConverter}, FallbackValue=false}"/>
Here's the containing listpage that has two templates in it:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition Height="1"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Templates:PeripheralDetailsTemplate/>
<Frame BackgroundColor="{StaticResource Slate}" Grid.Row="1"/>
<Frame BackgroundColor="White" Grid.Row="2">
<ListView x:Name="lvPeripheralSettings" ItemsSource="{Binding Settings}"
SelectionMode="None" ItemTapped="Setting_Tapped" RowHeight="40"
ios:ListView.SeparatorStyle="FullWidth">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell IsEnabled="{Binding IsEnabled, UpdateSourceEventName=ValueChanged, Mode=TwoWay, Converter={StaticResource DebugConverter}}">
<Templates:SettingTemplate/>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
</Grid>
Take note of the PeripheralDetailsTemplate:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="75"/>
</Grid.ColumnDefinitions>
<!--Icon-->
<Frame Padding="5">
<Images:VectorImage ResourceId="{Binding HardwareType, Converter={StaticResource hardwareTypeToSVGPathConverter}}"
WidthRequest="150" HeightRequest="150"
HorizontalOptions="Center" VerticalOptions="Center"/>
</Frame>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Frame>
<Label Text="{Binding Name, FallbackValue=Peripheral Name}" FontSize="Medium" FontAttributes="Bold" HorizontalOptions="Start" VerticalOptions="Center"/>
</Frame>
<Frame Grid.Row="1">
<Label Text="{Binding HardwareType, FallbackValue=Peripheral Type}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
</Frame>
<Frame Grid.Row="2">
<Label Text="{Binding MacAddress, StringFormat='Mac: {0}', FallbackValue=000111222333}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
</Frame>
<Frame Grid.Row="3">
<Label Text="{Binding FirmwareRevision, StringFormat='Firmware: {0}', FallbackValue=01AB}" FontSize="Micro" HorizontalOptions="Start" VerticalOptions="Center"/>
</Frame>
</Grid>
<Frame Grid.Column="2">
<Label Text="Conn" HorizontalOptions="Center" VerticalOptions="Center" TextColor="{Binding IsConnected, Converter={StaticResource BoolToColorConverter}, FallbackValue={StaticResource LynkdBlue}}"/>
</Frame>
</Grid>
It is bound to EXACTLY THE SAME PROPERTY AS THE ViewCell!!! and it works fine. I'm looking for the difference and haven't found it yet. I just know that I have properly set the bindingcontext because it databinds once and I know I'm properly firing NotifyPropertyChanged because the other template which is bound to the same property updates as I would expect. I have attached a debugconverter and it is never run. I think it's interesting that in both situations I was working with a ViewCell. These are two separate code bases with the same issue. :/
Simple answer; I was binding to a read-only property in my model that was based off of another property. When the other property changed I was not firing OnPropertyChanged for the read-only property.

How to implement a Placeholder in an Editor in Xamarin Forms?

I have read that the Editor control doesn't have a placeholder, so I have been trying to do a workaround without success. I know that the Entry control has a Placeholder, but I need multiline field as I am going to use it as a field where users can write a comment, and not just a single line.
This is my approach:
I have tried to put an Editor and a Label into a Grid control, with the Label being on top of the Editor. The Editor's InputTransparent flag is set to true. Then I just toggle the IsVisible property of the label depending on the whether the Editor has text or not. However, the problem is that I am using the MVVM pattern, so I don't know how to control the TextChanged event in the ViewModel. I have also tried to code behind, but the name of the Label can't be found.
This is my XAML code - Have only posted the relevant code:
<Grid Grid.Row="1">
<Editor Text="{Binding UserComment, Mode=TwoWay}" TextChanged="EditorTextChanged" HorizontalOptions="FillAndExpand"/>
<Label x:Name="PlaceholderLabel" Text="Write a comment" InputTransparent="True" HorizontalOptions="StartAndExpand"/>
</Grid>
For now, in the code-behind, I only have the EditorTextChanged event, which works fine, but it can't find PlaceholderLabel. I have binded the whole View to my ViewModel, is that the reason? How would approach it, if you had to follow the MVVM pattern?
It's worth mentioning that I have tried this approach. However, it didn't work as expected. The Placeholder would only appear when I clicked on the Editor and then unclicked it. It should appear in the beginning as on Facebook.
EDIT
This is the whole XAML code:
<ContentPage.Resources>
<ResourceDictionary>
<local:TeamAlignmentConverter x:Key="teamConverter"/>
<local:ImageAlignmentConverter x:Key="imageConverter"/>
<local:BooleanReverser x:Key="booleanReverser"/>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" Spacing="5">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="180"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.BindingContext>
<viewModel:MatchPageVM/>
</Grid.BindingContext>
<Grid BackgroundColor="White" RowSpacing="0" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*" />
<RowDefinition Height="2*" />
<RowDefinition Height="0.8*" />
</Grid.RowDefinitions>
<Label Text="{Binding Teams}" FontSize="26" HorizontalOptions="CenterAndExpand" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" VerticalOptions="CenterAndExpand" Grid.Row="0"/>
<Image Source="notificationsbell.png" Margin="25,10,0,0" HorizontalOptions="StartAndExpand" VerticalOptions="CenterAndExpand" Grid.Row="1"/>
<Label Text="{Binding Score}" FontSize="80" HorizontalOptions="CenterAndExpand" HorizontalTextAlignment="Center" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" Grid.Row="1"/>
<Label Text="Live" FontSize="24" HorizontalOptions="CenterAndExpand" HorizontalTextAlignment="Center" VerticalOptions="CenterAndExpand" VerticalTextAlignment="Center" Grid.Row="2"/>
</Grid>
</Grid>
<Grid RowSpacing="0" VerticalOptions="FillAndExpand">
<Grid.BindingContext>
<viewModel:MatchPageVM/>
</Grid.BindingContext>
<cv:CarouselView ItemsSource="{Binding CollectionList}">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<flv:FlowListView x:Name="flowListView" FlowColumnCount="1" Grid.Row="0"
SeparatorVisibility="None" HasUnevenRows="True" IsVisible="{Binding ListSwitch}"
FlowItemsSource="{Binding CollectionList}" BackgroundColor="White" >
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Grid ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="0.005*"/>
<ColumnDefinition Width="0.5*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="{Binding PlayerName}" Grid.Column="{Binding Team}" VerticalOptions="Center"
HorizontalOptions="FillAndExpand"
HorizontalTextAlignment="{Binding Team, Converter={StaticResource teamConverter}}"/>
<Image Source="{Binding ImageURL}" HorizontalOptions="Center" Grid.Column="{Binding Team, Converter={StaticResource imageConverter}}" Aspect="AspectFill" VerticalOptions="Center"/>
<BoxView BackgroundColor="Black" Grid.Column="2" HeightRequest="20" VerticalOptions="Center"/>
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<flv:FlowListView x:Name="flowListView2" FlowColumnCount="1" BackgroundColor="White"
HasUnevenRows="True" HeightRequest="180" IsVisible="{Binding ListSwitch, Converter={StaticResource booleanReverser}}" Grid.Row="0"
FlowItemsSource="{Binding CollectionList}" SeparatorVisibility="Default" SeparatorColor="Black">
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Grid RowSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*"/>
<ColumnDefinition Width="20*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding ImageURL}" HorizontalOptions="Start"
Grid.Row="0" Grid.Column="0" Aspect="AspectFit" Margin="0,10,0,0" VerticalOptions="Start"/>
<Label Text="{Binding UserName}" Grid.Row="0" Grid.Column="1" VerticalOptions="Start"
HorizontalOptions="Start" FontSize="Medium" FontAttributes="Bold" HorizontalTextAlignment="Start" Margin="0,10,0,5" />
<Label Text="{Binding UserComment}" Grid.Row="1" Grid.Column="0" VerticalOptions="Start"
HorizontalOptions="StartAndExpand" FontSize="Medium" HorizontalTextAlignment="Start" Grid.ColumnSpan="2" Margin="0,0,0,10" />
</Grid>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
<Grid Grid.Row="1" IsVisible="{Binding ListSwitch, Converter={StaticResource booleanReverser}}">
<Editor Text="{Binding UserComment, Mode=TwoWay}"
HorizontalOptions="FillAndExpand">
<Editor.BindingContext>
<viewModel:MatchPageVM/>
</Editor.BindingContext>
</Editor>
<Label Text="Skriv en kommentar"
HorizontalOptions="StartAndExpand" IsVisible="{Binding LabelIsVisible}" InputTransparent="True">
<Label.BindingContext>
<viewModel:MatchPageVM/>
</Label.BindingContext>
</Label>
</Grid>
</Grid>
</Grid>
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
</Grid>
</StackLayout>
</ContentPage.Content>
Here is your own mentioned approach that does work:
<AbsoluteLayout
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand">
<Editor
Text="{Binding Address, Mode=TwoWay}"
HorizontalOptions="FillAndExpand"
AbsoluteLayout.LayoutFlags="PositionProportional, WidthProportional"
AbsoluteLayout.LayoutBounds="0,0,1.01,100">
</Editor>
<Label Text="MyPlaceHolder" IsVisible="{Binding IsAddrerssPlaceHolderVisible}" HorizontalTextAlignment="Center"
AbsoluteLayout.LayoutBounds="0.5,0.5, 1, 0.5" VerticalTextAlignment="Center"
AbsoluteLayout.LayoutFlags="All" InputTransparent="True"/>
</AbsoluteLayout>
Here is Viewmodel part:
public bool IsAddrerssPlaceHolderVisible
{
get => _isAddrerssPlaceHolderVisible;
set
{
_isAddrerssPlaceHolderVisible= value;
RaisePropertyChanged();
}
}
public string Address
{
get => _address;
set
{
_address = value;
if (value.Length > 0)
{
IsAddrerssPlaceHolderVisible= false;
}
else
{
IsAddrerssPlaceHolderVisible= true;
}
RaisePropertyChanged();
}
}
It doesn't even need anything else which I previously mentioned by mistake! It works as simple as that :D
What you are looking for is a Custom Renderer, you see since Xamarin.Forms is just another level from the native elements, you can "access" the native elements using a CustomRenderer:
https://developer.xamarin.com/guides/xamarin-forms/application-fundamentals/custom-renderer/
You can create the base Custom Editor:
using Xamarin.Forms;
namespace EditorWithPlaceholder
{
public class PlaceholderEditor : Editor
{
public static BindableProperty PlaceholderProperty
= BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(PlaceholderEditor));
public static BindableProperty PlaceholderColorProperty
= BindableProperty.Create(nameof(PlaceholderColor), typeof(Color), typeof(PlaceholderEditor), Color.Gray);
public string Placeholder
{
get { return (string) GetValue(PlaceholderProperty); }
set { SetValue(PlaceholderProperty, value); }
}
public Color PlaceholderColor
{
get { return (Color) GetValue(PlaceholderColorProperty); }
set { SetValue(PlaceholderColorProperty, value); }
}
}
}
And the Renderers on each platform:
Android:
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(PlaceholderEditor), typeof(PlaceholderEditorRenderer))]
namespace EditorWithPlaceholder.Droid.Renderers
{
public class PlacehoderEditorRenderer : EditorRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
var element = (PlaceholderEditor) Element;
Control.Hint = element.Placeholder;
Control.SetHintTextColor(element.PlaceholderColor.ToAndroid());
}
}
}
iOS:
using System;
using Cirrious.FluentLayouts.Touch;
using Foundation;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(PlaceholderEditor), typeof(PlaceholderEditorRenderer))]
namespace EditorWithPlaceholder.iOS.Renderers
{
public class PlaceholderEditorRenderer : EditorRenderer
{
private UILabel _placeholderLabel;
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
CreatePlaceholderLabel((PlaceholderEditor) Element, Control);
Control.Ended += OnEnded;
Control.TextChanged += OnChanged;
}
private void CreatePlaceholderLabel(PlaceholderEditor element, UITextView parent)
{
_placeholderLabel = new UILabel
{
Text = element.Placeholder,
TextColor = element.PlaceholderColor.ToUIColor(),
BackgroundColor = UIColor.Clear,
Font = UIFont.FromName(element.FontFamily, (nfloat)element.FontSize)
};
_placeholderLabel.SizeToFit();
parent.AddSubview(_placeholderLabel);
parent.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
parent.AddConstraints(
_placeholderLabel.AtLeftOf(parent, 7),
_placeholderLabel.WithSameCenterY(parent)
);
parent.LayoutIfNeeded();
_placeholderLabel.Hidden = parent.HasText;
}
private void OnEnded(object sender, EventArgs args)
{
if (!((UITextView) sender).HasText && _placeholderLabel != null)
_placeholderLabel.Hidden = false;
}
private void OnChanged(object sender, EventArgs args)
{
if (_placeholderLabel != null)
_placeholderLabel.Hidden = ((UITextView) sender).HasText;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
Control.Ended -= OnEnded;
Control.Changed -= OnChanged;
_placeholderLabel?.Dispose();
_placeholderLabel = null;
}
base.Dispose(disposing);
}
}
}
I hope this information helps.
For reference:
https://solidbrain.com/2017/07/10/placeholder-text-in-xamarin-forms-editor/

Categories

Resources