Add indicators to horizontal scroll in Xamarin - xamarin

I have the following Horizontal scroll
<ScrollView HorizontalOptions="FillAndExpand" Padding="0" Orientation="Horizontal" HorizontalScrollBarVisibility="Never">
<ContentView Content="{Binding DocumentTypeContent}"/>
</ScrollView>
But I want that when there's more content to the right there would be an arrow pointing to the right displaying, and when there's more content to the left I'll have the same thing pointing to the left, so for example, the right arrow should always be displaying unless the user scrolled all the way to the right, and the opposite with the left arrow. How can I detect when the scrolls is all the way to the left or all the way to the right to display an image?

You can detect if scroll touch to end or start like this:
<ScrollView HorizontalOptions="FillAndExpand" Padding="0" Orientation="Horizontal" HorizontalScrollBarVisibility="Never" Scrolled="ScrollView_Scrolled">
And your code:
private void ScrollView_Scrolled(object sender, ScrolledEventArgs e)
{
double scroll = (sender as ScrollView).ContentSize.Width - (sender as ScrollView).Width;
if (scroll <= e.ScrollX)
{
right_button.IsEnabled = false;
}
else
{
right_button.IsEnabled = true;
}
if (e.ScrollX == 0)
{
left_button.IsEnabled = false;
}
else
{
left_button.IsEnabled = true;
}
}

You can try to use converter in this situation
<ContentPage
...
xmlns:Helpers="clr-namespace:YOURNAMESPACE">
<ScrollView HorizontalOptions="FillAndExpand" Padding="0" Orientation="Horizontal" HorizontalScrollBarVisibility="{Binding NUMBEROFITENS,Converter={Helpers:IsBarVisible}}">
<ContentView Content="{Binding DocumentTypeContent}"/>
</ScrollView>
IsBarVisible.cs
public class IsBarVisible : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value > 3 ? "Always" : "Never";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}

Related

Frame CornerRadius just bottom corners from code xamarin [duplicate]

Simple question. I need a frame with only one rounded corner, instead of all four. How can I only round one of the corners of a frame (top right in my case)?
Another way to phrase it: How can I set the cornerradius of only one corner of a frame?
The easy way is to use the Nuget PancakeView.
You can specify the CornerRadius in each vertice, achieving the desired effect:
Example:
<yummy:PancakeView BackgroundColor="Orange"CornerRadius="60,0,0,60"/>
You can read more in the official page.
Another way it to use custom render for frame.
1.Create class name CustomFrame, inherit Frame class, add BindableProperty CornerRadiusProperty in PCL.
public class CustomFrame: Frame
{
public static new readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CustomFrame), typeof(CornerRadius), typeof(CustomFrame));
public CustomFrame()
{
// MK Clearing default values (e.g. on iOS it's 5)
base.CornerRadius = 0;
}
public new CornerRadius CornerRadius
{
get => (CornerRadius)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
}
create CustomFrameRender in Android.
using FrameRenderer = Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer;
[assembly: ExportRenderer(typeof(CustomFrame), typeof(CustomFrameRenderer))]
namespace Demo1.Droid
{
class CustomFrameRenderer : FrameRenderer
{
public CustomFrameRenderer(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control != null)
{
UpdateCornerRadius();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
e.PropertyName == nameof(CustomFrame))
{
UpdateCornerRadius();
}
}
private void UpdateCornerRadius()
{
if (Control.Background is GradientDrawable backgroundGradient)
{
var cornerRadius = (Element as CustomFrame)?.CornerRadius;
if (!cornerRadius.HasValue)
{
return;
}
var topLeftCorner = Context.ToPixels(cornerRadius.Value.TopLeft);
var topRightCorner = Context.ToPixels(cornerRadius.Value.TopRight);
var bottomLeftCorner = Context.ToPixels(cornerRadius.Value.BottomLeft);
var bottomRightCorner = Context.ToPixels(cornerRadius.Value.BottomRight);
var cornerRadii = new[]
{
topLeftCorner,
topLeftCorner,
topRightCorner,
topRightCorner,
bottomRightCorner,
bottomRightCorner,
bottomLeftCorner,
bottomLeftCorner,
};
backgroundGradient.SetCornerRadii(cornerRadii);
}
}
}
}
3.using custonframe in forms.
<StackLayout>
<controls:CustomFrame
BackgroundColor="Red"
CornerRadius="0,30,0,0"
HeightRequest="100"
HorizontalOptions="Center"
VerticalOptions="Center"
WidthRequest="100" />
</StackLayout>
More detailed info about this, please refer to:
https://progrunning.net/customizing-corner-radius/
Use the nuget package Xamarin.Forms.PancakeView.
Look at this answer for a similar question:
https://stackoverflow.com/a/59650125/5869384
This is for UWP renderer
I've used the solutions from Cherry Bu - MSFT and changed it for UWP. In my project im using it in Android, iOS and UWP and it is working fine.
using System.ComponentModel;
using Windows.UI.Xaml.Media;
using Xamarin.Forms;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(CustomFrame), typeof(yourNamespace.UWP.CustomFrameRenderer))]
namespace yourNamespace.UWP
{
public class CustomFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control != null)
{
UpdateCornerRadius();
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomFrame.CornerRadius) ||
e.PropertyName == nameof(CustomFrame))
{
UpdateCornerRadius();
}
}
private void UpdateCornerRadius()
{
var radius = ((CustomFrame)this.Element).CornerRadius;
Control.CornerRadius = new Windows.UI.Xaml.CornerRadius(radius.TopLeft, radius.TopRight, radius.BottomRight, radius.BottomLeft);
}
}
}
You can use BoxView instead of Frame
<Grid Margin="10,10,80,10">
<BoxView Color="#CCE4FF"
CornerRadius="10,10,10,0"
HorizontalOptions="Fill"
VerticalOptions="Fill" />
<Grid Padding="10">
<Label Text="This is my message"
FontSize="14"
TextColor="#434343"/>
</Grid>
</Grid>
result view
simple solution i have used is to set another frame behind the rounded frame something like this
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.8*"/>
<RowDefinition Height="0.05*"/>
<RowDefinition Height="0.05*"/>
</Grid.RowDefinitions>
<Frame
Grid.Row="4"
Padding="0"
BackgroundColor="Green"
CornerRadius="0"/>
<Frame
Grid.Row="3"
Grid.RowSpan="2"
Padding="0"
BackgroundColor="Green"
HasShadow="True"
CornerRadius="20">
</Frame>
</Grid>

how to refer to item in an enumeration within a ListView

I have a class MRU which contains an ObservableCollection of type string. I would like to display the items within the collection on a PopupPage in a ListView.
[DefaultBindingProperty("Items")]
public class MRU<T> :INotifyPropertyChanged
{
public ObservableCollection<T> Items { get; } = new ObservableCollection<T>();
// This is the maximum number of items that will be saved to persistent storage,
// in use the list can grow beyond this number
private int _MaxItems = 5;
public int MaxItems
{
get { return _MaxItems; }
set {
_MaxItems = value;
TrimItems();
OnPropertyChanged();
}
}
public void Add(T t) {
int idx = Items.IndexOf(t);
if (idx > 0)
{
Items.Move(idx, 0);
}
else if (idx == -1)
{
Items.Insert(0, t);
}
}
public T Pop(int i)
{
if (i > 0 && i < Items.Count)
{
Items.Move(i, 0);
}
return Items[0];
}
private void TrimItems()
{
while (Items.Count > _MaxItems)
{
Items.RemoveAt(Items.Count - 1);
}
}
public void Save()
{
App.Current.Properties["MRU"] = SaveToJSON();
App.Current.SavePropertiesAsync();
}
public string SaveToJSON()
{
TrimItems();
string jsonString = JsonSerializer.Serialize(Items);
return jsonString;
}
public int LoadFromJSON(string jsonString)
{
Items.Clear();
ObservableCollection<T> restore = JsonSerializer.Deserialize<ObservableCollection<T>>(jsonString);
foreach (var t in restore)
{
if (Items.Count == _MaxItems) break;
Items.Add(t);
}
return Items.Count;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I can display it in my page using the following XAML
<StackLayout
VerticalOptions="Center"
HorizontalOptions="Center"
Padding="0,20">
<Frame BackgroundColor="White" CornerRadius="15">
<StackLayout>
<Label Text="Most Recently Used" FontSize="Large" BackgroundColor="LightBlue" HorizontalOptions="CenterAndExpand" Padding="20,4"/>
<StackLayout >
<ListView ItemsSource="{Binding Items}" SelectionMode="Single">
<!--<ListView.ItemTemplate>
<DataTemplate>
<StackLayout Orientation="Vertical">
<Label Text="{Binding XXXXXX}" VerticalOptions="Center" HorizontalTextAlignment="End" FontSize="Medium"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate> -->
</ListView>
</StackLayout>
</StackLayout>
</Frame>
</StackLayout>
Note the commented out ListView.ItemTemplate section. I would like to display the items within an ItemTemplate so that I can add buttons for each object, however I don't know how to declare it . What should XXXXX be? I've tried a zillion unlikely things
In your xaml page, you created a ListView. I found that you forgot to add <ViewCell></ViewCell> tags.
You should use a structure like the following:
<ListView x:Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
If you have an ObservableCollection<string>, to access the string in your binding, you want to do:
Text="{Binding}"
or
Text="{Binding .}"
Both say that you want to bind to whole object. Since the "whole object" in this case is a string, that's what you'll get.

How to prevent Editor to go behind the keyboard in Xamarin.Forms?

I have a Chat app. Currently, there is an Entry control for add chat text. Now I want to provide multiline Entry, same like Whatsapp.
If user type more than one line, it should automatic wrap the text to next line.
If user click on Nextline button in mobile keyboard, it should go to next line.
Height of Entry should be automatically increase upto 3 line and it should also decrease if user remove text.
To do that I have tried to replace Entry with Editor and implement following functionality.
1- Put an Editor in place of Entry.
2- Implement a functionality that keep keyboard open until user click on Message list screen or back button.
Now I am trying to implement auto height functioanlity but when user try to type, Editor goes behind the keyboard. Can anybody please suggest me how to keep Editor open and auto size?
Current code:
XAML:
<Grid x:Name="MessageControls" RowSpacing="1" ColumnSpacing="2" Padding="5"
Grid.Row="1" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<local:ChatEditorWithPlaceholder x:Name="txtMessage" Grid.Column="0" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>
<Frame x:Name="SendButton" Grid.Column="1" Margin= "0" Padding="0" HasShadow="false" HeightRequest="25"
BackgroundColor="Transparent" HorizontalOptions="FillAndExpand">
<Frame.GestureRecognizers>
<TapGestureRecognizer Tapped="SendMessage_Click" NumberOfTapsRequired="1" />
</Frame.GestureRecognizers>
<Label Text="Send" x:Name="sendButton" TextColor="#1f88b7" HeightRequest="20"
HorizontalOptions="Center" VerticalOptions="Center"/>
</Frame>
</Grid>
Editor
public class ChatEditorWithPlaceholder : Editor
{
public ChatEditorWithPlaceholder()
{
this.TextChanged += (sender, e) => { this.InvalidateMeasure(); };
}
}
Editor Renderer:
public class ChatEditorRenderer : EditorRenderer
{
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
}
}
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{
base.OnElementChanged(e);
if(Control != null) {
Control.ScrollEnabled = false;
}
var element = this.Element as ChatEditorWithPlaceholder;
Control.InputAccessoryView = null;
Control.ShouldEndEditing += DisableHidingKeyboard;
MessagingCenter.Subscribe<ConversationPage>(this, "FocusKeyboardStatus", (sender) =>
{
if (Control != null)
{
Control.ShouldEndEditing += EnableHidingKeyboard;
}
MessagingCenter.Unsubscribe<ConversationPage>(this, "FocusKeyboardStatus");
});
}
private bool DisableHidingKeyboard(UITextView textView)
{
return false;
}
private bool EnableHidingKeyboard(UITextView textView)
{
return true;
}
Screenshots:
Try this renderer for ios.
using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly:ExportRenderer( typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace YourNameSpace.iOS
{
public class CustomEditorRenderer: EditorRenderer
{
public ChatEntryRenderer()
{
UIKeyboard.Notifications.ObserveWillShow ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0,0,0, args.FrameEnd.Height); //push the entry up to keyboard height when keyboard is activated
}
});
UIKeyboard.Notifications.ObserveWillHide ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
}
});
}
}
}
for android add this in MainActivity
App.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().
UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
For Ios there is one plugin. You can use that. The link is Here.
For Andorid you have to just set below code in MainActivity after LoadApplication(new App()) method.
App.Current.On<Xamarin.Forms.PlatformConfiguration.Android().
UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
Updated answer for iOS :
For IOS you can use the following custom renderer to solve the keyboard overlapping issue. And please removed the keyboardoverlap nuget package from the project.
using System;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using HBIClientFacingApp;
using HBIClientFacingApp.iOS;
[assembly:ExportRenderer( typeof(CustomEditor), typeof(CustomEditorRenderer))]
namespace YourNameSpace.iOS
{
public class CustomEditorRenderer: EditorRenderer
{
public ChatEntryRenderer()
{
UIKeyboard.Notifications.ObserveWillShow ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0,0,0, args.FrameEnd.Height); //push the entry up to keyboard height when keyboard is activated
}
});
UIKeyboard.Notifications.ObserveWillHide ((sender, args) => {
if (Element != null)
{
Element.Margin = new Thickness(0); //set the margins to zero when keyboard is dismissed
}
});
}
}
}
You can try to change this line:
<local:ChatEditorWithPlaceholder x:Name="txtMessage" Grid.Column="0" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>
For this one:
<Editor x:Name="txtMessage" Grid.Column="0" AutoSize="TextChanges" TextChanged="EnableSend" Text="{Binding OutGoingText}"/>

How to change Label Text color of masterPageitem after selection

I'm new to Xamarin framework and want to create an app using Master-Detail Page
I did simple Master-Detail Navigation page demo from xamarin websit
master-detail-page xamarin webise
only difference is I used ViewCell inside DataTemplate.In ViewCell I have Label
instead of Image.
after clicking on MasterPageItems navigation is working fine but now I want to change the label Text color also .
<ListView x:Name="listView" VerticalOptions="FillAndExpand" SeparatorVisibility="None" RowHeight="50" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Title}" TextColor="#1ca7ec" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = e.SelectedItem as MasterPageItem;
if (item != null)
{
Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(ContactsPage)));
masterPage.ListView.SelectedItem = null;
IsPresented = false;
}
}
I think you can do in this way:
1- in your model you should have a "TextColor" property and a "Selected" property
public bool Selected { get; set; }
// I think you should not return "Color" type (for strong MVVM) but, for example, a value that you can convert in XAML with a IValueConverter...
public Color TextColor
{
get
{
if (Selected)
return Color.Black;
else
return Color.Green;
}
}
2- In your XAML you should have something like
<ListView SelectedItem="{Binding SelectedItem}" ItemsSource="{Binding List}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Name}" TextColor="{Binding TextColor}" FontSize="18"></Label>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
3- and in your ViewModel something like
MyModel _selectedItem { get; set; }
public ObservableCollection<MyModel> List { get; set; } = new ObservableCollection<MyModel>();
public MyModel SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != null)
_selectedItem.Selected = false;
_selectedItem = value;
if (_selectedItem != null)
_selectedItem.Selected = true;
}
}
When your item in the list is selected , SelectedItem property change and Selected property in your model became True or False, changing the TextColor property (I use PropertyChanged.Fody for INPC).
Hope this help
You can find the repo on GitHub
Instead of use a TextColor Property in your Model, I think you can also use only Selected property and an IValueConverter that convert Selected property to a color

make Xamarin DisplayActionSheet select, display value in Label

I have wired up my ContentPage to an instance of a class (g), and this case works fine:
open the page
enter values in the Entry boxes
make a selection from a DisplayActionSheet
click Save
OnSave all the values from the UI are in g, but the value from the DisplayActionSheet is not in the UI where I expect it.
After the DisplayActionSheet thing runs, I want a value for AisleDepthText to display in the UI.
Here is the class that I instantiate into a variable, g
public class GroceryItemForSaving
{
public GroceryItemForSaving() { }
public event PropertyChangedEventHandler PropertyChanged;
private string _AisleDepth;
public string AisleDepth
{
get
{
return _AisleDepth;
}
set
{
_AisleDepth = value;
OnPropertyChanged();
}
}
private string _AisleDepthText;
public string AisleDepthText
{
get
{
return _AisleDepthText;
}
set
{
_AisleDepthText = value;
OnPropertyChanged();
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I make g the BindingContext like this:
public NewGrocery()
{
InitializeComponent();
BindingContext = g;
}
Here is the relevant XAML.
<Label Text="GroceryName" Grid.Row="0" Grid.Column="1" ></Label>
<Entry Text="{Binding GroceryName}" Grid.Row="0" Grid.Column="2" ></Entry>
<Label Text="Aisle" Grid.Row="1" Grid.Column="1" ></Label>
<Entry Text="{Binding Aisle}" Grid.Row="1" Grid.Column="2"></Entry>
<Label Text="Aisle Depth" Grid.Row="2" Grid.Column="1" ></Label>
<Label Text="{Binding AisleDepthText, Mode=OneWay}" Grid.Row="2" Grid.Column="2" ></Label>
<Button Clicked="ShowAisleDepthChoices" Grid.Row="2" Grid.Column="3" Text="Aisle Depth" ></Button>
The button click handler ShowAisleDepthChoices, makes the ActionSheet display. In the code for that I set the values for AisleDepth and AisleDepthText like this:
public async void ShowAisleDepthChoices(object sender, EventArgs e)
{
var AisleDepth = 0;
var SelectedAisleDepth = await DisplayActionSheet("Aisle Depth", "Cancel", null, "Front", "Middle", "Back", "Back Wall");
switch (SelectedAisleDepth)
{
case "Front":
AisleDepth = 1;
break;
case "Middle":
AisleDepth = 2;
break;
case "Back":
AisleDepth = 3;
break;
case "Back Wall":
AisleDepth = 4;
break;
}
g.AisleDepthText = SelectedAisleDepth;
g.AisleDepth = AisleDepth.ToString();
}
Then after that no value appears in AisleDepthText Label, but when I click Save, the values are in g.AsileDepthText and g.AisleDept exactly where I expect them. NOTE: I can enter a GroceryName directly in the UI and it ends up in g.GroceryName on save.
What do I need to do to make the value for g.AisleDepthText appear in the UI after the DisplayActionSheet does its thing?
GroceryItemForSaving needs to implement INotifyPropertyChanged. You have to code to satisfy the implementation, but you're not declaring that the class uses the interface, so your binding isn't updating like it should.
public class GroceryItemForSaving : INotifyPropertyChanged

Resources