Carousal view autoslide after content load - Xamarin.forms - xamarin

I have a carousal view in my xamarin.forms app. I am trying to implement a feature just like stories in instagram. That is when we tap any of story, it will show inside a carousal sort of view and auto slide to next story after certain seconds.
I can auto slide the carousal view after certain seconds using timer. The problem I am facing is since the content of carousal items is loaded from URL (eg: image), the view will auto slide after the predefined time without the loading the content from URL. How can I tackle this problem, ie: the carousal should only slide after the content is loaded. Any help is really appreciated.
Current auto slide implementation
Device.StartTimer(TimeSpan.FromSeconds(7), (Func<bool>)(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
StatusCarousal.Position = (StatusCarousal.Position + 1) % statusList.Count;
});
return false;
}));
EDIT
My current progress.
I have created an auto sliding carousalview using Behaviors like this.
public class AutoscrollCarouselBehavior : Behavior<CarouselView.FormsPlugin.Abstractions.CarouselViewControl>
{
public int Delay { get; set; } = 7000;
private bool runTimer;
private CarouselViewControl attachedCarousel;
protected override void OnAttachedTo(CarouselViewControl bindable)
{
base.OnAttachedTo(bindable);
runTimer = true;
attachedCarousel = bindable;
Device.StartTimer(TimeSpan.FromMilliseconds(Delay), () =>
{
MoveCarousel();
return runTimer;
});
}
protected override void OnDetachingFrom(CarouselViewControl bindable)
{
runTimer = false;
base.OnDetachingFrom(bindable);
}
void MoveCarousel()
{
if (attachedCarousel.ItemsSource != null)
{
if (attachedCarousel.Position < attachedCarousel.ItemsSource.GetCount() - 1)
{
attachedCarousel.Position++;
}
else
{
attachedCarousel.Position = 0;
}
}
}
}
Xaml
<cv:CarouselViewControl x:Name="StatusCarousal"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource personDataTemplateSelector}"
HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<cv:CarouselViewControl.Behaviors>
<local:AutoscrollCarouselBehavior />
</cv:CarouselViewControl.Behaviors>
</cv:CarouselViewControl>
And also added additional properties for progressbar to progress.
public static class AttachedProperties
{
public static BindableProperty AnimatedProgressProperty =
BindableProperty.CreateAttached("AnimatedProgress",
typeof(double),
typeof(ProgressBar),
0.0d,
BindingMode.OneWay,
propertyChanged: (b, o, n) =>
ProgressBarProgressChanged((ProgressBar)b, (double)n));
private static void ProgressBarProgressChanged(ProgressBar progressBar, double progress)
{
ViewExtensions.CancelAnimations(progressBar);
progressBar.ProgressTo((double)progress, 800, Easing.SinOut);
}
}
Xaml
<ProgressBar HeightRequest="1.5"
x:Name="StatusProgressBar"
local:AttachedProperties.AnimatedProgressAnimationTime=""
local:AttachedProperties.AnimatedProgress=""
HorizontalOptions="FillAndExpand" VerticalOptions="Start"
ProgressColor="Snow" Margin="10,5,10,0"></ProgressBar>
The two problem still I am facing is
How to connect the carousal view auto scroll according to the ffimage loading
from api? ie; only start the auto scroll after loading the image properly ?
How to connect the carousalview to the progressbar progress preoperty? so
that both the progressbar will load into full and the carousal gets slide?

Related

How to hide clear button Icon inside SearchBar control Xamarin forms

I am using xamarin forms SearchBar control. I want to remove clear button x icon without using custom renderer.
<controls:ExSearchBar
x:Name="entrySearch"
BackgroundColor="White"
CornerRadius="6"
BorderWidth="1"
HeightRequest="45"
Text="{Binding SearchText}"
HorizontalOptions="FillAndExpand"
Placeholder="search">
</controls:ExSearchBar>
This is ExSearchBar control in shared project
public class ExSearchBar : SearchBar
{
public static readonly BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(ExFrame), default(float));
public float Elevation
{
get { return (float)GetValue(ElevationProperty); }
set { SetValue(ElevationProperty, value); }
}
}
How can I do that?
The situation you are describing is the exact reason why Xamarin Forms ships with the ability to create custom renderers. The forms team define the UI elements in abstract (seperate from their native implementation) and when there is a specific feature that is not defined in their API, you must go down to the platform level to change it.
You can also use an Effect to achieve the same result, I have provided a custom renderer for iOS & Android to show you how you would go about achieving the UI you desire:
iOS:
public class SearchBarButtonRenderer : SearchBarRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Control.SearchTextField.ClearButtonMode = UITextFieldViewMode.Never;
}
}
}
Really simple, just remove the clear button from the underlying UITextField
Android
public class SearchBarButtonRenderer : SearchBarRenderer
{
private readonly Context _context;
public SearchBarButtonRenderer(Context context)
: base(context)
{
_context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<SearchBar> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
// Get Search Close Button Drawable
var closeButtonId = Resources.GetIdentifier("android:id/search_close_btn", null, null);
var searchEditText = Control.FindViewById<ImageView>(closeButtonId);
// Get Close Button Drawable To Replace Existing Drawable
var closeDrawable = GetCloseButtonDrawable() as VectorDrawable;
if (closeDrawable is null) return;
// Apply Transparent Color To Drawable (To Make Invisible)
var buttonColor = Xamarin.Forms.Color.Transparent.ToAndroid();
closeDrawable.SetTint(buttonColor);
// Set Drawable On Control
searchEditText.SetImageDrawable(closeDrawable);
}
}
private Drawable GetCloseButtonDrawable()
{
return ContextCompat.GetDrawable(_context, Resource.Drawable.abc_ic_clear_material);
}
}
A little bit of a fiddle, find the close button drawable and replace it with a custom styled drawable

Create bindable properties for Treeview in Xamarin Forms

I needed to use a Treeview in my xamarin forms application, however the only existing TreeView on the net are not free (Syncfusion and Telerik).
So I found this very interesting project : https://github.com/AdaptSolutions/Xamarin.Forms-TreeView
the only problem that I found is that the ItemSource and SelectedItem properties are not bindable and therefor I can't use it on an MVVM Pattern. Which brings us to my question, How can I make them bindable.
I tried to follow this documentation : https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/bindable-properties
but still nothing. Can anyone help me with that please ? Thank you
UPDATE :
this is the TreeView class :
public class TreeView : ScrollView
{
#region Fields
private readonly StackLayout _StackLayout = new StackLayout { Orientation = StackOrientation.Vertical };
//TODO: This initialises the list, but there is nothing listening to INotifyCollectionChanged so no nodes will get rendered
private IList<TreeViewNode> _RootNodes = new ObservableCollection<TreeViewNode>();
private TreeViewNode _SelectedItem;
#endregion
#region Public Properties
public TreeViewNode SelectedItem
{
get => _SelectedItem;
set
{
if (_SelectedItem == value)
{
return;
}
if (_SelectedItem != null)
{
_SelectedItem.IsSelected = false;
}
_SelectedItem = value;
SelectedItemChanged?.Invoke(this, new EventArgs());
}
}
public IList<TreeViewNode> RootNodes
{
get => _RootNodes;
set
{
_RootNodes = value;
if (value is INotifyCollectionChanged notifyCollectionChanged)
{
notifyCollectionChanged.CollectionChanged += (s, e) =>
{
RenderNodes(_RootNodes, _StackLayout, e, null);
};
}
RenderNodes(_RootNodes, _StackLayout, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset), null);
}
}
#endregion
#region Constructor
public TreeView()
{
Content = _StackLayout;
}
#endregion
#region Private Static Methods
private static void AddItems(IEnumerable<TreeViewNode> childTreeViewItems, StackLayout parent, TreeViewNode parentTreeViewItem)
{
foreach (var childTreeNode in childTreeViewItems)
{
if (!parent.Children.Contains(childTreeNode))
{
parent.Children.Add(childTreeNode);
}
childTreeNode.ParentTreeViewItem = parentTreeViewItem;
}
}
#endregion
#region Internal Static Methods
internal static void RenderNodes(IEnumerable<TreeViewNode> childTreeViewItems, StackLayout parent, NotifyCollectionChangedEventArgs e, TreeViewNode parentTreeViewItem)
{
if (e.Action != NotifyCollectionChangedAction.Add)
{
AddItems(childTreeViewItems, parent, parentTreeViewItem);
}
else
{
AddItems(e.NewItems.Cast<TreeViewNode>(), parent, parentTreeViewItem);
}
}
#endregion
}
so what Im trying to do here is making RootNodes bindable as well as SelectedItem afterwards.
What I did is simply adding this, thinking it should work but obviously it does not :
public static readonly BindableProperty RootNodesProperty =
BindableProperty.Create(nameof(RootNodes), typeof(IList<TreeViewNode>), typeof(TreeView));
public IList<TreeViewNode> RootNodes
{
get => (IList<TreeViewNode>)GetValue(RootNodesProperty);
set
{
SetValue(RootNodesProperty, value);
_RootNodes = value;
if (value is INotifyCollectionChanged notifyCollectionChanged)
{
notifyCollectionChanged.CollectionChanged += (s, e) =>
{
RenderNodes(_RootNodes, _StackLayout, e, null);
};
}
RenderNodes(_RootNodes, _StackLayout, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset), null);
}
}
UPDATE 2 :
Here is what it looks like
Hope this helps
It seems you will not need to create custom ItemSource and SelectedItem in ScrollView, because Xamarin Foms has Bindable Layouts that contains ItemsSource and ItemTemplateSelector .
Bindable layouts enable any layout class that derives from the Layout class to generate its content by binding to a collection of items, with the option to set the appearance of each item with a DataTemplate. Bindable layouts are provided by the BindableLayout class, which exposes the following attached properties:
ItemsSource – specifies the collection of IEnumerable items to be displayed by the layout.
ItemTemplate – specifies the DataTemplate to apply to each item in the collection of items displayed by the layout.
ItemTemplateSelector – specifies the DataTemplateSelector that will be used to choose a DataTemplate for an item at runtime.
If you need to use ScrollView, sample code as follows:
<ScrollView>
<StackLayout BindableLayout.ItemsSource="{Binding User.TopFollowers}"
Orientation="Horizontal"
...>
<BindableLayout.ItemTemplate>
<DataTemplate>
<controls:CircleImage Source="{Binding}"
Aspect="AspectFill"
WidthRequest="44"
HeightRequest="44"
... />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>

Webview text field gets scrolled to top when trying to focus - Xamarin.forms iOS

I have xamarin forms iOS app contains a webview. The webview load a html page contains some text fields.The problem I am facing is whenever we click on the text field,the keyboard shows and the entire web page will scroll to top and the text field will become invisible.When we open the same page on a browser it works fine. I have used xam.plugins.forms.keyboard overlap for my other non webview pages. How can I fix this issue? I want the textview field should be there when we focus it .
I have seen this similar problem on this question. But it didn't solved my issue. Any help is appreciated.
My XAML
<ContentPage.Content>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<WebView x:Name="Webview"
HeightRequest="1000"
WidthRequest="1000"
IsVisible="False"
Navigating="OnNavigating"
Navigated="OnNavigated"
VerticalOptions="FillAndExpand"/>
</Grid>
</ContentPage.Content>
You can remove the plugin and implement it by yourself.
For example, you can check the source code and copy it to your project as a PageRenderer.
In the page renderer, I add a bool value to check whether you need to use this renderer or not. Here I use the ClassId to check:
bool useThisRenderer;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
Page p = e.NewElement as Page;
if (p.ClassId == "0")
{
useThisRenderer = false;
}
else
{
useThisRenderer = true;
}
}
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (useThisRenderer)
{
var page = Element as ContentPage;
if (page != null)
{
var contentScrollView = page.Content as ScrollView;
if (contentScrollView != null)
return;
RegisterForKeyboardNotifications();
}
}
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
if (useThisRenderer)
{
UnregisterForKeyboardNotifications();
}
}
Then in your Xamarin.forms project, if one of your content page does not need this renderer(for example the current page you have a webview), you can set the classid = "0" to avoid this:
public MainPage()
{
InitializeComponent();
this.ClassId = "0";
}
If you need this renderer, then do nothing about this.ClassId.
I upload a sample project here and feel free to ask me any question.

How to stop a Xamarin Forms behavior used to asynchronously translate an image?

In a Xamarin.Forms project, I'm trying to repeatedly translate an image from a position A(x,y) to a position B(x,y) and back, from B to A. To achieve this, I read that is possible to customize behaviors.
I extend Behavior class, overriding OnAttachedTo and OnDetachingFrom. And in the OnAttachedTo method I start a Task which repeatedly does the two translations.
This is my Behavior class:
public class MoveImageBehavior : Behavior<Image>
{
private Image _Image = null;
public static readonly BindableProperty AnimatedProperty = BindableProperty.Create("Animated", typeof(bool), typeof(ImageAnimatedBehavior), defaultValue: false);
public bool Animated
{
get { return (bool)GetValue(AnimatedProperty); }
set { SetValue(AnimatedProperty, value); }
}
protected override void OnAttachedTo(Image image)
{
base.OnAttachedTo(image);
_Image = image;
Animated = true;
Task.Run(AnimateImage);
}
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
_Image = null;
}
private async void AnimateImage()
{
while (_Image != null && Animated)
{
await _Image.TranslateTo(100, 100, 1000);
await _Image.TranslateTo(0, 0, 1000);
}
}
}
The image in the xaml file:
<ContentView>
<Grid>
<Image x:Name="image_translating" Source="my_icon" Aspect="AspectFit">
<Image.Behaviors>
<behaviors:MoveImageBehavior Animated="{Binding ImageTranslating}" BindingContext="{Binding BindingContext, Source={x:Reference image_translating}}"/>
</Image.Behaviors>
</Image>
</Grid>
</ContentView>
The Image repeatedly translates correctly as I want, but I'm not able to stop the while routine. The property binding doesn't work when Animated is set to false in the ViewModel and OnDetachingFrom is never called.
What am I doing wrong? Any suggestions?
Through the document, we can see that:
The OnDetachingFrom method is fired when the behavior is removed from
the control. This method receives a reference to the control to which
it is attached, and is used to perform any required cleanup. For
example, you could unsubscribe from an event on a control to prevent
memory leaks.
It will only fired when you remove the behavior from the image. I would give you an example about how to stop the animation:
I defined an bool property in the code behind to control stop or not stop:
public bool showA = true;
And I add a button as an example to stop the animation:
private void Button_Clicked(object sender, EventArgs e)
{
showA = !showA;
if (showA)
{
image_translating.Behaviors.Add(new MoveImageBehavior());
}
else
{
var toRemove = image_translating.Behaviors.FirstOrDefault(b => b is MoveImageBehavior);
if (toRemove != null)
{
image_translating.Behaviors.Remove(toRemove);
}
}
}
Also in your OnDetachingFrom method, do not set the image to null, it will cause a null expection, just set the Animated to false :
protected override void OnDetachingFrom(Image image)
{
base.OnDetachingFrom(image);
Animated = false;
}
You can convert my click event to some binding in your project and make it work.
Refer: creating-a-xamarinforms-behaviorer

Size of the Custom popup window using Interaction request

I used a custom confirmation popup window, this the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="{Binding Content}" TextWrapping="Wrap" Width="150"/>
<StackPanel Orientation="Horizontal" Margin="6" Grid.Row="1">
<Button x:Name="YesBtn" Width="100" Content="OK" Click="OnOk_Click"/>
<Button x:Name="NoBtn" Width="100" Content="No" Click="OnNo_Click"/>
</StackPanel>
</Grid>
and this is the code behide:
public partial class CustomConfirmation : IInteractionRequestAware
{
public CustomConfirmation()
{
InitializeComponent();
}
public IConfirmation Confirmation
{
get { return this.DataContext as IConfirmation; }
set { this.DataContext = value; }
}
public string Title { get; set; }
public bool Confirmed { get; set; }
public INotification Notification { get; set; }
public Action FinishInteraction { get; set; }
private void OnOk_Click(object sender, RoutedEventArgs e)
{
if (FinishInteraction != null)
{
Confirmation.Confirmed= true;
FinishInteraction();
}
}
private void OnNo_Click(object sender, RoutedEventArgs e)
{
if (FinishInteraction != null)
{
Confirmation.Confirmed = false;
FinishInteraction();
}
}
}
In view model class i have :
two commands(DispalyLongTextCommand and DispalyShortTextCommand): one
to display long message and the other to display a short message
and i have InteractionRequest ConfirmationRequest
object initialized in ctor to raise intercations.
if I display the long message first my custom window resize its content to the hole message, it is OK!
but if a want to display the short message, my window keeps the previous size!
note : even i set the window SizeToContent style to WidthAndHeight but it not working.
<ei:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding ConfirmationRequest, Mode=TwoWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="SizeToContent" Value="WidthAndHeight"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<local:CustomConfirmation/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</ei:Interaction.Triggers>
can you guide me,
thanks in advance
SOLUTION:
I fixed the problem by adding this code in the code behind of the custom popup window, :
public CustomConfirmationView()
{
InitializeComponent();
Loaded += CustomPopupView_Loaded;
}
private void CustomPopupView_Loaded(object sender, RoutedEventArgs e)
{
var parentWindow = this.Parent as Window;
if (parentWindow != null)
{
parentWindow.Measure(parentWindow.DesiredSize);
}
}
The WindowContent property is reused each time you show a new popup. So, what happens is that when you first show a popup, the CustomPopupView is visualized and the height is set based on the current content. Now, when you close the popup, and change the content to a larger message and then show it again, the CustomPopupView.Height has already been set by the previous action and isn't updated in time for the new Window to get the correct height. So you must now resize the Window to match the new size of the CustomPopupView height. So just add a little code to handle this in your code-behind like this:
public CustomPopupView()
{
InitializeComponent();
Loaded += CustomPopupView_Loaded;
}
private void CustomPopupView_Loaded(object sender, RoutedEventArgs e)
{
var parentWindow = this.Parent as Window;
if (parentWindow != null)
parentWindow.MinHeight = _txt.ActualHeight + 75;
}
Note: '_txt' is the name of the TextBlock with the Content binding.
I think this has to do with the default confirmation window that ships with Prism. The MinWidth and MinHeight are set in the XAML to 300 and 150, respectively. So, the window width/weight will never get any smaller no matter what the window content is. Overriding the window style will not be enough to do what you need.
You could download the Prism code and remove that limitation if you are comfortable enough with that. The source path to the file you would want to start with is below.
\Source\Wpf\Prism.Wpf\Interactivity\DefaultPopupWindows\DefaultConfirmationWindow.xaml
Either that, or ask the Prism team to see if they can make this more flexible, which is probably a better suggestion. You can post this as an issue on their GitHub page. https://github.com/PrismLibrary/Prism/issues

Resources