Change image in code with Xamarin.Forms - xamarin.forms

I've tried a few different methods based on the official documentation and from debugging my if/else condition is firing correctly, but I can't seem to get my image to change.
As you can see, I've tried two methods here but my understanding is that ImageSource.FromFile isn't needed because of implicit conversion.
Here's the XAML:
<TableView Intent="Settings">
<TableRoot>
<TableSection Title="Getting Started">
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="10" >
<Image Source="bulb.png" x:Name="imgBulb" />
<Switch x:Name="bulbSwitch" IsToggled="false" Toggled="bulbSwitchToggled" HorizontalOptions="EndAndExpand" VerticalOptions="Center" />
</StackLayout>
</ViewCell>
</TableSection>
<TableSection Title="Time to slide....">
<ViewCell>
<StackLayout Orientation="Vertical" Padding="10" >
<Slider Maximum="100" Minimum="0" Value="50" ValueChanged="sliderChanged" ></Slider>
<Label x:Name="lblSliderOutput" HorizontalOptions="CenterAndExpand" Text="Placeholder" />
</StackLayout>
</ViewCell>
</TableSection>
</TableRoot>
</TableView>
And the code behind:
public partial class TableViewsPage : ContentPage
{
public TableViewsPage ()
{
InitializeComponent ();
lblSliderOutput.Text = "50";
}
public void sliderChanged(object sender, ValueChangedEventArgs args) {
var simpleValue = Convert.ToInt32 (args.NewValue);
lblSliderOutput.Text = simpleValue.ToString();
}
public void bulbSwitchToggled(object sender, ToggledEventArgs args) {
if (imgBulb.Source == ImageSource.FromFile("bulb.png"))
{
imgBulb.Source = ImageSource.FromFile ("biggerBulb.png");
Debug.WriteLine ("imgbulb = bulb.png");
}
else
{
imgBulb.Source = "bulb.png";
Debug.WriteLine ("imgbulb = biggerBulb.png");
}
}
}

Related

Command RelativeSource not working in Xamarin

I have Binding Data problem in subpages. I have a Page that shows a list of products. When clicking on the product it will go to the Product Details subpage. Everything works fine:
Product.xaml
<RefreshView x:DataType="locals:DashboardViewModel" Padding="0" Command="{Binding LoadDashboardCommand}" IsRefreshing="{Binding IsBusy, Mode=OneWay}">
<StackLayout BindableLayout.ItemsSource="{Binding ProductNew}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame x:DataType="model:Product">
<StackLayout>
<Label FontSize="14" MaxLines="2" LineBreakMode="TailTruncation" HeightRequest="40" Text="{Binding Name}"/>
</StackLayout>
<Frame.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type locals:DashboardViewModel}}, Path=ProductTappedView}"
CommandParameter="{Binding .}" />
</Frame.GestureRecognizers>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</RefreshView>
Product.xaml.cs
DashboardViewModel dashboardViewModel;
public Product()
{
BindingContext = dashboardViewModel = new DashboardViewModel(Navigation);
dashboardViewModel.OnAppearing();
}
DashboardViewModel.cs
public class DashboardViewModel : BaseDashboardViewModel
{
public Command LoadDashboardCommand { get; }
public Command ProductTappedView { get; }
public ObservableCollection<Product> ProductNew { get; }
public DashboardViewModel(INavigation _navigation)
{
LoadDashboardCommand = new Command(async () => await ExecuteLoadDashboardCommand());
ProductNew = new ObservableCollection<Product>();
ProductTappedView = new Command<Product>(OnViewDetailProduct);
Navigation = _navigation;
}
private async void OnViewDetailProduct(Product detailProduct)
{
await Navigation.PushAsync(new ProductDetail(detailProduct));
}
............
}
Next in my Productdetail page show product details. I have Read more. When clicked it will redirect to another subpage: ContentDetailProd.xaml
ProductDetail.xaml
<ContentPage.BindingContext>
<locals:ViewDetailProductViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout Padding="15">
<Label Text="{Binding ProductNews.Name}" FontFamily="RobotoMedium" FontSize="18" TextTransform="Uppercase"/>
<Label Text="Read more"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Command="{Binding Source={RelativeSource AncestorType={x:Type locals:ViewDetailProductViewModel}}, Path=ContentProductTappedView}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>
</StackLayout>
</ContentPage.Content>
ViewDetailProductViewModel.cs
public class ViewDetailProductViewModel : BaseDashboardViewModel
{
public Command ContentProductTappedView { get;}
public ViewDetailProductViewModel()
{
ProductNews = new Product();
ContentProductTappedView = new Command<Product>(OnViewContentDetailProduct);
}
private async void OnViewContentDetailProduct(Product detailProduct)
{
await Navigation.PushAsync(new ContentDetailProd(detailProduct));
}
}
ContentDetailProd.xaml.cs
public ContentDetailProd(Product detailProduct)
{
InitializeComponent();
//Load Readmore
}
However when I Debug it actually doesn't run on the event: ContentProductTappedView and as a result it doesn't redirect to the ContentDetailProd.xaml page. I've tried everything but still can't solve the problem.
Looking forward to everyone's help. Thanks
If you want to pass the data through a Page Constructor, you could refer to the link below. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/navigation/hierarchical#passing-data-when-navigating
I changed like this based on the documentation #Wendy Zang - MSFT provided. Everything looks good.
ProductDetail.xaml
<StackLayout x:Name="_readmore" Padding="15">
<Label Text="{Binding ProductNews.Name}" FontFamily="RobotoMedium" FontSize="18" TextTransform="Uppercase"/>
<Label x:Name="_content" MaxLines="2" LineBreakMode="TailTruncation" Text="{Binding ProductNews.Contents}" FontFamily="RobotoMedium" FontSize="18" TextTransform="Uppercase"/>
<Label Text="Read more"/>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="_readmore_Tapped" />
</StackLayout.GestureRecognizers>
</StackLayout>
ProductDetail.xaml.cs
private async void _readmore_Tapped(object sender, EventArgs e)
{
var contentget = _content.Text;
var detailProduct = new Product
{
Contents = contentget,
};
var detailContentPage = new ContentDetailProd(detailProduct);
detailContentPage.BindingContext = detailProduct;
await Navigation.PushAsync(detailContentPage);
}
ContentDetailProd.xaml.cs
public ContentDetailProd(Product detailProduct)
{
InitializeComponent();
//Load Readmore
if (detailProduct != null)
{
_contentmore.Text = detailProduct.Contents;
}
}

Xamarin Forms Switch inside Stacklayout BindableLayout get toggled event using MVVM (Prism.Forms)

I have the following list:
<StackLayout BindableLayout.ItemsSource="{Binding NotificationList}" Margin="0,10,0,10" >
<BindableLayout.ItemTemplate>
<DataTemplate> <StackLayout Style="{StaticResource StacklayoutAStyle}">
<Label Text="{Binding notificationLabel}" VerticalOptions="Center" VerticalTextAlignment="Center"/>
<Switch IsToggled="{Binding isNotificationToggled, Mode=TwoWay}" VerticalOptions="Center" HorizontalOptions="End" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
I'd like to get the notificationLabel text when I toggle the corresponding switch in the list. My ViewModel looks like this:
private ObservableCollection<NotificationModel> _notificationList = new ObservableCollection<NotificationModel>
{
new NotificationModel { notificationLabel = "Notification1", isNotificationToggled = true },
new NotificationModel { notificationLabel = "Notification2", isNotificationToggled = false },
new NotificationModel { notificationLabel = "Notification3", isNotificationToggled = false },
new NotificationModel { notificationLabel = "Notification4", isNotificationToggled = true },
};
public ObservableCollection<NotificationModel> NotificationList
{
get { return _notificationList; }
set { SetProperty(ref _notificationList, value); }
}
When I toggled Notification 3 for example, it switches to on (true), but how can I capture that event with "Notification 3"?
You can use Switch.Toggled to listen for toggled event,then get the BindingContext (NotificationModel),then determine which Switch is turned on according to NotificationModel.notificationLabel.
<StackLayout BindableLayout.ItemsSource="{Binding NotificationList}" Margin="0,10,0,10" >
<BindableLayout.ItemTemplate>
<DataTemplate>
<StackLayout Style="{StaticResource StacklayoutAStyle}">
<Label Text="{Binding notificationLabel}" VerticalOptions="Center" VerticalTextAlignment="Center"/>
<Switch IsToggled="{Binding isNotificationToggled, Mode=TwoWay}" VerticalOptions="Center" HorizontalOptions="End" Toggled="Switch_Toggled" />
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
in your page.xaml.cs:
private void Switch_Toggled(object sender, ToggledEventArgs e)
{
Switch sw = sender as Switch;
NotificationModel notificationModel = (NotificationModel)sw.BindingContext;
if (e.Value)
{
switch (notificationModel.notificationLabel)
{
case "Notification1":
//do something
break;
case "Notification2":
//do something
break;
case "Notification3":
//do something
break;
case "Notification4":
//do something
break;
}
}
}

set custom font for alert messages for both android and ios using xamarin.forms

I am using xamarin forms for my project. I need to set custom font family(Montserrat-Medium.ttf) for the following alert messages for both android and iOS.
var ans = await App.Current.MainPage.DisplayAlert("", "Are you want to leave the application?", "Yes", "No");
Anyone help me to resolve this issue.
This is not possible in shared Xamarin project, but you can simply create a custom alert, and do what ever you want with it, like this:
Add your font to the solution
Add the font somewhere in shared project and then right click on it and choose Properties.
In Properties, choose Embedded resource as a Build Action.
Add this line
[assembly: ExportFont("Montserrat-Medium.ttf", Alias = "MyMontserrat")]
to the AssemblyInfo.cs
Create custom popup view like this
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MyApp.MainPage">
<AbsoluteLayout Padding="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Button Text="Display Alert!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="Center"
Clicked="OnButtonClicked" />
</StackLayout>
<!--Here's the custom popupView-->
<ContentView x:Name="popupView" BackgroundColor="#C0808080" Padding="10, 0" IsVisible="false" AbsoluteLayout.LayoutBounds="0, 0, 1, 1" AbsoluteLayout.LayoutFlags="All">
<StackLayout VerticalOptions="Center" HorizontalOptions="Center">
<StackLayout Orientation="Vertical" HeightRequest="150" WidthRequest="200" BackgroundColor="White">
<Label x:Name="myLabel" FontSize="16" FontFamily="MyMontserrat" Text="Are you want to leave the application?" />
<Button Text="Yes" FontSize="16" FontFamily="MyMontserrat" BackgroundColor="White" TextTransform="None" Clicked="Yes_Clicked" />
<Button Text="No" FontSize="16" FontFamily="MyMontserrat" BackgroundColor="White" TextTransform="None" Clicked="No_Clicked" />
</StackLayout>
</StackLayout>
</ContentView>
</AbsoluteLayout>
</ContentPage>
Use your popup in .cs
void OnButtonClicked(object sender, EventArgs args)
{
//This will show the pop up
popupView.IsVisible = true;
}
private void Yes_Clicked(object sender, EventArgs e)
{
popupView.IsVisible = false;
//Your stuff here
}
private void No_Clicked(object sender, EventArgs e)
{
popupView.IsVisible = false;
//Your stuff here
}
You could use DependencyService to call the native dialog,and define the font in the native dialog.
Here is a simple sample for Android (ios is similar to this,you could refer to this):
define a interface in your forms project:
public interface ICustomAlert
{
void Show(string message);
}
then in your Android project:
public class CustomAlert : ICustomAlert
{
public void Show(string message)
{
AndroidX.AppCompat.App.AlertDialog.Builder alertdialogbuilder = new AndroidX.AppCompat.App.AlertDialog.Builder(MainActivity.Instance);
alertdialogbuilder.SetMessage(message);
alertdialogbuilder.SetPositiveButton("Yes",OkEvent);
alertdialogbuilder.SetNeutralButton("No", NoEvent);
AndroidX.AppCompat.App.AlertDialog alertdialog = alertdialogbuilder.Create();
alertdialog.Show();
TextView textView = alertdialog.FindViewById<TextView>(Android.Resource.Id.Message);
textView.SetTextColor(Android.Graphics.Color.Red);
Typeface face = Typeface.CreateFromAsset(MainActivity.Instance.Assets, "fonts/Montserrat-Medium.tff");
textView.SetTypeface(face, TypefaceStyle.Normal);
}
private void NoEvent(object sender, DialogClickEventArgs e)
{
}
private void OkEvent(object sender, DialogClickEventArgs e)
{
}
}
then you could call like
DependencyService.Get<ICustomAlert>().Show("your message");

Carousel View in Xamarin forms not responding to Image button in the view

I cannot get the image buttons to move the carousel image to the next in the list. when I click the imagebutton and step through the code the Position is changing properly, but does not move the carousel in the view. Any help would be greatly appreciated. Here is the code for both my View and View Model:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Das_Local.Views.MainPage"
xmlns:vm="clr-namespace:Das_Local.ViewModels"
Title="{Binding Title}">
<ContentPage.BindingContext>
<vm:MainViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<Color x:Key="Accent">#96d1ff</Color>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<StackLayout VerticalOptions="Start">
<CarouselView ItemsSource="{Binding Images}" HorizontalOptions="FillAndExpand" HeightRequest="375">
<CarouselView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding ImageUrl}" Aspect="AspectFill" />
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</StackLayout>
<StackLayout>
<Label Text="Whats New in the Local" FontSize="Title" HorizontalTextAlignment="Center" Margin="0,10,0,-10"/>
<Grid HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<CarouselView Grid.Row="1" HorizontalOptions="FillAndExpand" ItemsSource="{Binding Articles}">
<CarouselView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True">
<StackLayout Margin="0,0">
<Label Text="{Binding ArticleTitle}" FontAttributes="Bold" FontSize="Title"/>
<Label Text="{Binding ArticleTextBody}" FontSize="Body" />
</StackLayout>
</Frame>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
<ImageButton Grid.Row="1" Source="left.png" Aspect="Fill" HorizontalOptions="Start" VerticalOptions="Center" BackgroundColor="Transparent"
HeightRequest="35" WidthRequest="35" Command="{Binding ChangePositionCommand}" CommandParameter="L"/>
<ImageButton Grid.Row="1" Source="right.png" Aspect="Fill" HorizontalOptions="End" VerticalOptions="Center" BackgroundColor="Transparent"
HeightRequest="35" WidthRequest="35" Command="{Binding ChangePositionCommand}" CommandParameter="R"/>
</Grid>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
--------------------------------------------------------------------------------------------------
using System.Collections.ObjectModel;
using System.Windows.Input;
using Das_Local.Models;
using Xamarin.Forms;
namespace Das_Local.ViewModels
{
class MainViewModel : BaseViewModel
{
public MainViewModel()
{
Title = "SMART 29";
images = GetCarouselImages();
articles = GetCarouselArticles();
ChangePositionCommand = new Command(ChangePosition);
}
private ObservableCollection<CarouselImages> images;
public ObservableCollection<CarouselImages> Images
{
get { return images; }
set
{
images = value;
OnPropertyChange();
}
}
private ObservableCollection<CarouselImages> GetCarouselImages()
{
return new ObservableCollection<CarouselImages>
{
new CarouselImages { ImageUrl= "anglewrappedduct.jpg" },
new CarouselImages { ImageUrl = "ductrun.jpg" },
new CarouselImages { ImageUrl = "offset.jpg" },
new CarouselImages { ImageUrl = "panels.jpg" }
};
}
ObservableCollection<CarouselArticles> articles;
public ObservableCollection<CarouselArticles> Articles
{
get { return articles; }
set
{
articles = value;
OnPropertyChange();
}
}
private ObservableCollection<CarouselArticles> GetCarouselArticles()
{
return new ObservableCollection<CarouselArticles>
{
new CarouselArticles { ArticleTitle= "This Title", ArticleTextBody = "Body of the article will go here"},
new CarouselArticles { ArticleTitle = "Another Title", ArticleTextBody = "Just what I need more text for the article body" },
new CarouselArticles { ArticleTitle = "My Third Title", ArticleTextBody = "I am getting burned out on writing random things"},
new CarouselArticles { ArticleTitle = "Bravo", ArticleTextBody = "whats the deal with these free channels"}
};
}
public ICommand ChangePositionCommand { get; set; }
private CarouselArticles selectedarticle;
public CarouselArticles SelectedArticle
{
get { return selectedarticle; }
set
{
selectedarticle = value;
OnPropertyChange();
}
}
private int position;
public int Position
{
get { return position; }
set
{
position = value;
selectedarticle = articles[position];
OnPropertyChange(nameof(SelectedArticle));
}
}
private void ChangePosition(object obj)
{
string direction = (string)obj;
if (direction == "L")
{
if (position == 0)
{
Position = articles.Count - 1;
return;
}
Position -= 1;
}
else if (direction == "R")
{
if (position == articles.Count - 1)
{
Position = 0;
return;
}
Position += 1;
}
}
}
}

Xamarin Forms: Dynamically creating a Listview item -> Problem with BindingContext

as I was already describing in another post here on Stackoverflow, I was trying to get a different layout (one frame spanning multiple listview items). Now I decided to try the following approach: My ViewModel is a List of Lists (just like for a grouped listview). However instead of using a grouped listview, I have a normal ListView in which the single Items of the child list will be created in Code-behind as soon as the bindingContext of the ParentViewCell is available:
private void CommentViewCell_BindingContextChanged(object sender, EventArgs e)
{
if (this.BindingContext == null) return;
var model = this.BindingContext as CommentViewModel;
DateCommentViewCell dateCell = new DateCommentViewCell
{
BindingContext = model
};
ParentCommentViewCell parentCell = new ParentCommentViewCell
{
BindingContext = model
};
ContentStackView.Children.Add(dateCell.View);
ContentStackView.Children.Add(parentCell.View);
foreach (CommentBaseViewModel cbvm in model)
{
if (cbvm is CommentViewModel)
{
ChildCommentViewCell childCell = new ChildCommentViewCell
{
BindingContext = cbvm
};
ContentStackView.Children.Add(childCell.View);
}
}
}
When I run this, the visuals are actually ok and look how I intended them to.
However the BindingContext is wrong: The ChildCommentViewCell BindingContext does not reference the CommentViewModel of the child, but that of the parent when being displayed. I checked the BindingContext of the ChildCommentViewCell like this
public ChildCommentViewCell ()
{
InitializeComponent ();
BindingContextChanged += ChildCommentViewCell_BindingContextChanged;
}
private void ChildCommentViewCell_BindingContextChanged(object sender, EventArgs e)
{
Debug.WriteLine("### ChildCommentViewCell BindingContext Changed");
test();
}
public void test()
{
var context = this.BindingContext as CommentViewModel;
Debug.WriteLine("### Instance: " + this.GetHashCode());
Debug.WriteLine("### \tBinding Context: " + context.CommentModel.Text);
Debug.WriteLine("### \tLabel: " + ChildCommentText.Text);
}
and the output on the console is just fine. However when running on my phone, the actual content is (as written above) that of the ParentCommentViewModel. Any ideas?
The XAML code of the ChildCommentViewCell element is the following:
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="App.View.ViewCell.ChildCommentViewCell">
<StackLayout Padding="10,0" Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand">
<StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
<StackLayout Grid.Column="0" VerticalOptions="FillAndExpand" Orientation="Vertical" Spacing="0">
<Label Text="{Binding CommentModel.AuthorName}" Style="{StaticResource CommentAuthor}"/>
</StackLayout>
<Frame IsClippedToBounds="True" HasShadow="False" Margin="5" Padding="3" BackgroundColor="LightGray" CornerRadius="3.0">
<StackLayout Grid.Column="1" VerticalOptions="FillAndExpand" Orientation="Vertical" Spacing="0">
<Label x:Name="ChildCommentText" Text="{Binding Path=CommentModel.Text, StringFormat=' {0}'}" Style="{StaticResource CommentContent}"/>
<Label Text="{Binding CommentTimeAgo}" Style="{StaticResource CommentTime}" HorizontalOptions="Start"/>
</StackLayout>
</Frame>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
One additional thing: I tried to debug the "Appearing"-Event, however this does not even get called once...?!
Thank you very much in advance!
Found my problem in the BindingContextChanged method: I had to explicitly bind the BindingContext to the view, not only to the ViewCell:
foreach (CommentBaseViewModel cbvm in model)
{
if (cbvm is CommentViewModel)
{
ChildCommentViewCell childCell = new ChildCommentViewCell
{
BindingContext = cbvm
};
childCell.View.BindingContext = cbvm;
ContentStackView.Children.Add(childCell.View);
}
}

Resources