FFImageLoading : load into an ImageButton - xamarin

In Xamarin.Android, to load an image using FFImageLoading, a ImageViewAsync must be used instead of a standard ImageView.
I couldn't find what I can use if I wanna load my image into an ImageButton. Didn't find an "ImageButtonAsync".

Dont know if this is relevant or not. I gave my svg a tap gesture to get it working like a button.
first reference the FFImageLoading.Svg.Forms in your xaml
namespace:FFImageLoading.Svg.Forms;assembly=FFImageLoading.Svg.Forms"
second add the svgImage with a tap gesture
<ffimageloadingsvg:SvgCachedImage Source="YourImageSource" >
<ffimageloadingsvg:SvgCachedImage.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding YourCommand}"/>
</ffimageloadingsvg:SvgCachedImage.GestureRecognizers>
</ffimageloadingsvg:SvgCachedImage>
You will call this in the constructor of your ViewModel to initialize the command
YourCommand= new DelegateCommand(async () => await ExecuteYourCommandAsync());
Create the properties that bind to the xaml file
public ICommand YourCommand
{
get => yourCommand;
set => SetProperty(ref yourCommand, value);
}
private ICommand yourCommand;
The await ExecuteYourCommandAsync is a method that you create. and in there you will put your logic of what you actually want the tap gesture to do.
You can also pass through object with the command. let met know if this makes sense

AFAIK there is no particular ImageButton to use with FFImageLoading but you can use directly an ImageViewAsync set it as android:clickable="true" and add the click event.
And as stated here an ImageButton is just an ImageView that has a non-null background by default and also, ImageButton.onSetAlpha() method always returns false, scaleType is set to center and it's always inflated as focusable. So if you need that behaviour you can add it.
Another way would be you to create your custom ImageButton that can handle FFImageLoading image loading. For that you can inherit from ImageViewAsync and add the behaviours explained in the paragraph before. So that you can use the FFImageLoading API directly as this custom controls inherits ImageViewAsync.
Instead of that you can also add your custom loading logic as explained here inheriting from ImageLoaderTask and call it like ImageService.Instance.LoadImage(customImageTask)
Finally another way (but hackish and non-performant) would be to create an ImageViewAsync just to hold the result of the FFImageLoading and on the Success callback set the Drawable in the ImageButton:
var myImageButton = myView.FindViewById<ImageButton>(Resource.Id.my_image_button);
var myImageView = new ImageViewAsync(this); // this is the context
ImageService.Instance.LoadUrl(this.Source[position].LogoImagePath)
.Success(() => myImageButton.SetImageDrawable(myImageView.Drawable))
.Into(myImageView);
HIH and if you need any help let me know

Related

.net maui CarouselView is not quite compatible with Slider control: CarouselView swipe operation takes over Slider's drag action

.net maui app.
Dragging value element along the slider bar does not work if the the slider put into CarouselView's template like this:
<CarouselView ItemsSource="{Binding Items}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Slider Minimum="0" Maximum="30" WidthRequest="200" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
CarouselView takes over the swipe event for scrolling through the items, and Slider does not get the event (DragStarted is not even called). You can actually click along the slider bar to change its value, so it's not completely frozen, but not how it's supposed to work. Drag & drop is main way user deal with slider control.
Could anyone advise any workaround? I want users to be able scroll through carousel view items also. It's just if they swipe inside the control, event should not handed over to its parent container, if it's possible to do so.
If I add it outside of the corouselview, combine both in Grid and use padding to align slider inside the corouselview, it works as expected, but I need to add lots of additional code, calculate the desirable location and redirect all bindings, which ends up to be an awkward workaround.
At first, I don't suggest you use the slider in the CarouselView. Becasue you want the same behavior has two effects. There is a conflict between them.
But for the android, you can use the custom handler to deal with the swipe event.
Put the Handler class in the /Platform/Android:
public class MySliderHander : SliderHandler
{
protected override void ConnectHandler(SeekBar platformView)
{
base.ConnectHandler(platformView);
platformView.SetOnTouchListener(new SliderListener());
// the listener will make the slider deal with the swip event not the CarouselView.
}
}
Put the SliderListener class in the /Platform/Android
public class SliderListener : Java.Lang.Object, IOnTouchListener
{
public bool OnTouch(global::Android.Views.View v, MotionEvent e)
{
if (e.Action == MotionEventActions.Down || e.Action == MotionEventActions.Move)
{
v.Parent.RequestDisallowInterceptTouchEvent(true);
}
else
{
v.Parent.RequestDisallowInterceptTouchEvent(false);
}
return false;
}
}
And in the MauiProgram.cs:
builder
                  .UseMauiApp<App>()      
                  .ConfigureMauiHandlers(handlers => {
#if ANDROID
                        handlers.AddHandler(typeof(Slider), typeof(YourProjectName.Platforms.Android.MySliderHander));
                  #endif
                  })
In addition, the Slider's height is same as the CarouselView. So you can use a frame to contain the Slider and swipe the CarouselView by swiping the frame.

How to redraw ZXing QR code on TextChanged Event - Xamarin

I'd like to redraw my QR Code every time a particular TextChanged event is triggered.
The ZXingBarcodeImageView object gets drawn in the view when the page loads with the value BarcodeValue set in the XAML file like this:
<forms:ZXingBarcodeImageView
Margin="5,5,5,0"
x:Name="QRCodeView"
BarcodeFormat="QR_CODE"
BarcodeValue="-1" //this is the value of the QR code
/>
I have an Entry with a TextChanged event attached, which triggers a function UpdateQRLabel. This function should redraw the QRCode with the new value in Entry
<Entry
x:Name="Message"
TextChanged="UpdateQRLabel"
/>
If I change the BarcodeValue parameter after the QRCode has been drawn, it DOES NOT get redrawn automatically.
I need to force the ZXingBarcodeImageView object to redraw every time the TextChanged event is triggered.
Question
How do I force the ZXingBarcodeImageView to redraw when the TextChanged event is triggered?
I'm not sure if you're using data-binding or not. Since you are using events I guess not, however, I did get it to work with data-binding. A sample repo can be found here: https://github.com/jfversluis/ZXingValueBinding
It comes down to this. Create a property which will hold your barcode value:
private string _barcodeValue = "-1";
public string BarcodeValue
{
get { return _barcodeValue; }
set
{
_barcodeValue = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(BarcodeValue)));
}
}
The object holding this property needs to implement the INotifyPropertyChanged interface. You could consider using PropertyChanged.Fody for these.
I have put this property in the code-behind of my page, this can also be a separate class. Now, change your barcode image view to this: <forms:ZXingBarcodeImageView ... BarcodeValue="{Binding BarcodeValue}">.
Whenever you set a new value to BarcodeValue, the value should change because the UI is notified because of the INotifyPropertyChanged mechanism.

How to get ContentPage's BindingContext from ContentView?

I have the following Contentpage.content, where I set certain binding context.
<StackLayout>
<local:Post />
<local:Comments />
</StackLayout>
In Post.xaml.cs (ContentView), I've tried to get the binding context of the ContentPage this way but it doesn't work.
BindingContext = (PostViewModel)Parent.BindingContext;
How can I get the binding context of the ContentPage if I'm standing in a ContentView?
By the time your constructor is called, the BindingContext might not be initialised yet.
So, you should wait for the BindingContext being changed to perform operations on it.
I think the answer is OnBindingContextChanged event.
https://developer.xamarin.com/api/member/Xamarin.Forms.View.OnBindingContextChanged()
Little sample:
protected override void OnBindingContextChanged ()
{
base.OnBindingContextChanged ();
//BindingContext should not be null at this point
// and you may add your code here.
}
Note:
If you have a ContentView inside a ContentPage, unless explicitly set by another Control (like when using an ItemTemplate for a ListView) or by your code, the BindingContext of the ContentView is the same as the ContentPage.
So, it shouldn't be necessary to call "Parent".
Let me know if more clarification is needed.
The BindingContext of the ContentView is usually also the BindingContext of the ContentPage since it is passed down from the parent.
So you should not even need to set ContentView.BindingContext if you already set the parent ContentPage.BindingContext.
If I am missing something, please let me know.

In NativeScript on Android, how do I prevent a SearchBar from gaining focus on page load?

I have a SeachBar inside a ScrollView. In iOS all is good. On Android the ScrollView automatically scrolls to the SearchBar, adds focus to it and displays the soft keyboard. I can hide the softkeyboard by adding android:windowSoftInputMode="stateHidden" as an activity in the AndroidManifest.xml file but I can't work out how to prevent the focus (and hence the auto scroll). Any help would be much appreciated.
Using Angular2:
app/my.component.html
<StackLayout (loaded)="onSearchLayoutLoaded($event)">
<SearchBar hint="search here" (loaded)="onSearchBarLoaded($event)">
</SearchBar>
</StackLayout>
app/my.component.ts
onSearchLayoutLoaded(event) {
if (event.object.android) {
event.object.android.setFocusableInTouchMode(true);
}
}
onSearchBarLoaded(event) {
if (event.object.android) {
event.object.android.clearFocus();
}
}
This eliminates unnecessarily having to use template reference variables.
For Android you need to do a couple of things. If you were using a native Android layout, lets say LinerLayout you would set android:focusableInTouchMode="true" and that should work for most use cases. So with NativeScript you're going to use the associated method on the parent of your SearchBar and then call `clearFocus() on the searchbar.
Example
function removeSearchFocus() {
// get the parent of the searchbar
var parent = page.getViewById('parentLayout');
var searchBar = page.getViewById('mySearchBar');
if (parent.android) {
parent.android.setFocusableInTouchMode(true);
parent.android.setFocusable(true);
searchBar.android.clearFocus();
}
}
Then attach this function to one of the page navigation event, or dump the important pieces into a current page event you might already have in your app. Just assign the parent an ID and the SearchBar so you can get it by the ID and this should work.
How about adding a endEditing to the page loaded or loading event?
searchBar = page.getViewById('your-searchbar');
searchBar.ios.endEditing(true);
or set focus to another element, where you want the focus to be, e.g.
somethingElse = page.getViewById('your-top-bar');
somethingElse.focus();

How can I prevent Scrollviewer to scroll back when scrollbarvisibility is set to disabled? wp7

I have created a Scrollviewer in WP7, which harbors 3 usercontrol, each one of which hold as their content XAML created UserControls. This works fine. This scrollviewer should be able to scroll between these items, but make this not possible for the user to scroll. So when an item in one of these contents are clicked upon, the scrollviewer slides left or right depending on the item selected, and bring into view one of the other usercontrols. I use a mediator to accomplish this:
<Grid.Resources>
<Storyboard x:Name="ItemAnimation">
<DoubleAnimation x:Name="ItemAnimationContent"
Storyboard.TargetName="Mediator"
Storyboard.TargetProperty="ScrollableWidthMultiplier"/>
</Storyboard>
</Grid.Resources>
<ScrollViewer Name="ScrollableItemPanel"
Grid.Row="2"
Grid.RowSpan="3"
Grid.ColumnSpan="3"
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Orientation="Horizontal">
<UserControl Name="NewsListBoxControl" Width="480" />
<UserControl Name="DetailedItemControl" Width="480"/>
<UserControl Name="ExternalBrowserItemControl" Width="480"/>
</StackPanel>
</ScrollViewer>
<local:ScrollableItemAnimationMediator x:Name="Mediator"
ScrollViewer="{Binding ElementName=ScrollableItemPanel}"/>
In basic, this works fine too, I can navigate between the items, and load upon them the content as usercontrols. But the problem lies in granting the user the abillity to scroll. Before the item scrolls, I set the hittestvisibilty to true, and the horizontalscrollbarvisibility to visible. After the animation is done, I want to grant back the hittestvisibility and set the horizontalscrollbarvisibility to Disabled again. This latter is where the problem is: when I set the horizontalscrollbarvisibility to Disabled, the scrollviewer automatically brings back into view the first of three items in the stackpanel. How can I stop this? This is the code I use to scroll the mediator:
private void CreateDetailedArticleItem( Dictionary<string, string> itemQuery )
{
_articleDetailPage.ItemQuery = itemQuery;
DetailedItemControl.Content = _articleDetailPage as UserControl;
Animate( _articleDetailPage, 0.0f, 0.5f, 250 );
}
private void Animate( IContentControl control, float from, float to, double milliseconds )
{
//this eventhandler will fire when the animation has completed
EventHandler handler = null;
//we take away the User Input just for the moment, so that we can animate without the user interfering. Also, we make horizontalScroll Visible
IsUserEnabled = false;
//we then set the content of the animation. Where from will it move, towards where and in what duration?
ItemAnimationContent.From = from;
ItemAnimationContent.To = to;
ItemAnimationContent.Duration = TimeSpan.FromMilliseconds( milliseconds );
//we start the animation
ItemAnimation.Begin( );
//we tell the new control that it will appear soon, so it can load its main content
control.ViewWillAppear( );
//also, we tell the currentcontrol that it will disappear soon, so it can unload its content and eventhandlers and so on
CurrentControl.ViewWillDisAppear( );
//the handler is a delegate. This way, it becomes rather easy and clean to fire the completed event, without creating a strong reference ( well, actually,
//we do create a strong reference, but as soon as it is fired, we remove it again, shhhh! ).
handler = delegate( object sender, EventArgs e )
{
//as stated, we remove the eventlistener again, so it won't keep firing all the time
ItemAnimation.Completed -= handler;
//after the animation, we tell the new control that it is now in screen, and can start downloading its data
control.ViewDidAppear( );
//at the same time, the "current" control has fully moved out of view, so it can now fully unload all its content.
CurrentControl.ViewDidDisAppear( );
//now, all we have to do is to make sure that the next time an item is being loaded, the new content is spoken to, not the old one
CurrentControl = control;
//and finally, enable the users input again, and remove the horizontal scrollbarvisibility
IsUserEnabled = true;
};
ItemAnimation.Completed += handler;
}
private bool IsUserEnabled
{
set
{
//when the user can control the scrollviewer, then the horizontal scrollvisibility is disabled, so that the user cannot move horizontally,
//otherwise, so we only make it visible when the program needs to animate.
ScrollableItemPanel.IsHitTestVisible = value;
ScrollableItemPanel.HorizontalScrollBarVisibility = value ? ScrollBarVisibility.Disabled : ScrollBarVisibility.Visible;
}
}
I had already asked this question, then regarded it as answered, as I thought it to be answered, namely using ScrollbarVisibility.Hidden instead of ScrollbarVisibility.Disabled, only the scrollbarvisibility stays visible this way, and the user can still scroll. Is there a native way to deal with this problem?
Any help would be greatly appreciated. Greetz
Rather than fight the behavior of the native control it may be easier to just manipulate the position of items yourself using a custom control (wrapping your other controls) which animates between different visual states (adjust the translate transform) depending on the "selected" item.

Resources