Visual Studio Project Image Brand new to Xamarin and I have wasted more than a day trying to get a simple image to display on a simple page.
I am using Visual Studio 2017.
The image is set as an embedded resource (see the attached image). There are no errors when run, the image simply doesn't display.
I have tried the following:
<Image Source="XF0003.Assets.logo.png" />
<Image Source="Assets.logo.png" />
<Image Source="logo.png" />
<Image Source="{local:ImageResource XF0003.Assets.logo.png}" />
<Image Source="{local:ImageResource Assets.logo.png}" />
<Image Source="{local:ImageResource logo.png}" />
XAML Page Code:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XF0003"
x:Class="XF0003.MainPage">
<StackLayout>
<Image Source="Assets.logo.png" />
<Label Text="Username" />
<Entry x:Name="usernameEntry" Placeholder="username" />
<Label Text="Password" />
<Entry x:Name="passwordEntry" IsPassword="true" />
<Button Text="Login" Clicked="PageOne" />
<Label x:Name="messageLabel" />
</StackLayout>
</ContentPage>
XAML.CS Page Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace XF0003
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private async void PageOne(object sender, EventArgs e)
{
Navigation.InsertPageBefore(new Page1(), this);
await Navigation.PopAsync();
}
}
[ContentProperty("Source")]
public class ImageResourceExtension : Xamarin.Forms.Xaml.IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource(Source);
return imageSource;
}
}
}
In the image you linked, it looks like you only saved the image in your xamarin.forms project. I believe the images need to be saved in their respective projects.
i.e.
XF0003.Droid/Resources/Drawable (For Android)
XF0003.iOS/Resources (For iOS)
Make sure your images have the same name.
For the Android pictures make the build action AndroidResource.
For the iOS pictures make the build action BundleResource.
DoNotCopy for both.
When you refer to the image, just use the [name].[filetype]
<Image Source="logo.png" />
It's also explained in great detail here.
Related
I have a Label within my CollectionView that I need to populate with a value outside the ItemsSource List that populates the view.
The following code is an example of what I am trying to accomplish but it seems that the CollectionView is limiting the binding context to just the Items list. I have tried naming the label and setting it in my c# code but I cant seem to access the label in c#. I suppose I could build the whole page in c# rather than using the .xaml but unlike this example my actual code uses multiple templates and a template selector. If I could figure this out without rewriting hours of code I would prefer it.
ItemsPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="TabTest.Views.ItemsPage"
Title="{Binding Title}"
x:Name="BrowseItemsPage">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Clicked="AddItem_Clicked" />
</ContentPage.ToolbarItems>
<StackLayout Padding="10">
<Label Text="{Binding TestVal}" FontSize="16" HeightRequest="20" />
<!-- ^^^^ This label displays just as expected -->
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadItemsCommand}">
<CollectionView x:Name="ItemsCollectionView"
ItemsSource="{Binding Items}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10">
<Label x:Name="TestV" Text="{Binding Path=BindingContext.TestVal}" />
<!-- ^^^^ I want this Label to display the TestVal string in the ViewModel -->
<Label Text="{Binding Text}"
d:Text="{Binding .}"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<Label Text="{Binding Description}"
d:Text="Item descripton"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemDetailTextStyle}"
FontSize="13" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="OnItemSelected"></TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</StackLayout>
</ContentPage>
ItemsViewModel.cs
namespace TabTest.ViewModels
{
public class ItemsViewModel : BaseViewModel
{
public ObservableCollection<Item> Items { get; set; }
public Command LoadItemsCommand { get; set; }
private string testVal;
public string TestVal // I want the value of this variable in that Label
{
get
{
return testVal;
}
set
{
testVal = value;
}
}
public ItemsViewModel()
{
Title = "Browse";
TestVal = "Value123";
Items = new ObservableCollection<Item>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
MessagingCenter.Subscribe<NewItemPage, Item>(this, "AddItem", async (obj, item) =>
{
var newItem = item as Item;
Items.Add(newItem);
await DataStore.AddItemAsync(newItem);
});
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Items.Clear();
var items = await DataStore.GetItemsAsync(true);
foreach (var item in items)
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
}
I ended up using a dynamic resource in Xaml and used code behind to modify the resource when it needed to change.
Xaml:
<Label x:Name="TestV" Text="{DynamicResource TestValue}" />
Code Behind:
Application.Current.Resources["TestValue"] = NewValue;
App.xaml:
<x:String x:Key="TestValue">Value123</x:String>
I am implementing a Xamarin app with FreshMVVM framework and I want to use a BasePage to share some code among Pages.
The problem is that when I need to bind some Properties in the MainPage.xaml I have to specify the Source in this way to make it working: Text="{Binding Title, Source={x:Reference mainPage}}". Otherwise without Source Binding doesn't work.
Ok, I get it but is this the right way? Is there another way to achieve the same result? What about when I have plenty of bindings in a page? For instance, is it possible "setting" the Source at un upper level, because in my opinion setting the same Source for each Binding is very annoying.
BasePage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestXamarin.BasePage"
x:Name="basePage">
<ContentView>
<StackLayout Orientation="Vertical">
<Label Text="HEADER" FontSize="Large"/>
<Label Text="{Binding Text, Source={x:Reference basePage}}" FontSize="Large"/>
<ContentPresenter BindingContext="{Binding Parent.BindingContext}"
Content="{Binding PageContent, Source={x:Reference basePage}}" />
</StackLayout>
</ContentView>
</ContentPage>
BasePage.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TestXamarin
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class BasePage : ContentPage
{
public static readonly BindableProperty TextProperty = BindableProperty.Create(
nameof(Text),
typeof(string),
typeof(BasePage));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly BindableProperty PageContentProperty = BindableProperty.Create(
nameof(PageContent),
typeof(object),
typeof(BasePage));
public object PageContent
{
get { return GetValue(PageContentProperty); }
set { SetValue(PageContentProperty, value); }
}
public BasePage()
{
InitializeComponent();
}
}
}
MainPage.xaml
<local:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestXamarin"
x:Class="TestXamarin.MainPage"
Text="FROM MAIN PAGE"
x:Name="mainPage">
<local:BasePage.PageContent>
<StackLayout>
<Label Text="Body" FontSize="Large"/>
<Label Text="{Binding Title, Source={x:Reference mainPage}}" FontSize="Large"/>
</StackLayout>
</local:BasePage.PageContent>
</local:BasePage>
MainPage.xaml.cs
public partial class MainPage : BasePage
{
public MainPage()
{
Title = "MAIN PAGE";
InitializeComponent();
}
}
Another way to achieve what you are trying to do would be with control templates.
Here I have defined a template in App.xaml
<ControlTemplate x:Key="ActivityIndicatorTemplate">
<Grid>
<ContentPresenter />
<StackLayout Style="{StaticResource BlockingPanel}"
IsVisible="{TemplateBinding BindingContext.IsBusy}">
<ActivityIndicator Style="{StaticResource ActivityIndicatorStyle}"
IsVisible="{TemplateBinding BindingContext.IsBusy}"
IsRunning="{TemplateBinding BindingContext.IsBusy}" />
</StackLayout>
</Grid>
</ControlTemplate>
Note the content presenter and the TemplateBinding.
I use it on a page like this.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.MyTestPage"
ControlTemplate="{StaticResource ActivityIndicatorTemplate}"
Title="{Binding Title}">
<Grid>
...
</Grid>
</ContentPage>
The page content replaces the content presenter in the template. Looks simpler than a base page.
I have a strange problem with bindings on a custom control. I created a custom toolbar:
public partial class TopToolbar
{
public static readonly BindableProperty BackCommandProperty =
BindableProperty.Create(nameof(BackCommand), typeof(ICommand), typeof(TopToolbar), propertyChanged: BackCommandChanged);
public ICommand BackCommand
{
get => (ICommand) GetValue(BackCommandProperty);
set => SetValue(BackCommandProperty, value);
}
public TopToolbar()
{
InitializeComponent();
}
// for debug purposes only
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
Debug.WriteLine(BindingContext);
}
// for debug purposes only
private static void BackCommandChanged(BindableObject bindable, object oldvalue, object newvalue)
{
Debug.WriteLine($"old: {oldvalue}, new: {newvalue}");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Core.Controls.TopToolbar"
x:Name="TopToolbarView"
BindingContext="{x:Reference TopToolbarView}"
Orientation="Vertical">
<StackLayout Orientation="Horizontal"
HorizontalOptions="FillAndExpand"
<Image Source="{StaticResource Image.Toolbar.LeftArrow}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BackCommand}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
I use it on a page in this way:
<pages:ContentPage.Content>
<StackLayout BackgroundColor="{StaticResource LightGrayColor}"
Spacing="0"
Padding="0">
<controls:TopToolbar Title="Master Data" BackCommand="{Binding MyBackCommand}" />
BindingContext of the page is a view model:
public class MyCustomersPageModel
{
public RelayCommand MyBackCommand { get; set; }
public MyCustomersPageModel()
{
MyBackCommand = // command creation;
}
}
From the debugging I know that BindingContext of a control is set (OnBindingContextChanged called) properly to itself (TopToolbar object) twice - first time when there's no child views and second time after they are added. I've checked that BindingContext is correctly propagated in all child controls.
Unfortunately the BackCommand is not bind at all. The setter of the TopToolbar.BackCommand is not called even once.
Interestingly when I replace setting the BindingContext on a control to setting the Souce directly in bindings everything works fine:
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Core.Controls.TopToolbar"
x:Name="TopToolbarView"
Orientation="Vertical">
<StackLayout Orientation="Horizontal"
HorizontalOptions="FillAndExpand"
<Image Source="{StaticResource Image.Toolbar.LeftArrow}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Source={x:Reference TopToolbarView}, Path=BackCommand}" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
Any clue what I do wrong?
It is working as expected. I would recommend using Source.
In first case, when you set BindingContext on TopToolbar to itself, then I would imagine this would be the sequence of events:
Custom control is constructed, BindingContext is assigned to self through reference.
Page instance is created, control is added to it.
When page's BindingContext is set, through power of property inheritance, all it's child controls' BindingContext is updated.
At this point your custom control's BindingContext is still referencing itself as value-propagation doesn't override manually set context.
Therefore, binding <controls:TopToolbar BackCommand="{Binding MyBackCommand}" fails, as this binding will try to look for MyBackCommand on it's binding-context which is TopToolbar.
But, in second case, when you specify binding Source as TopToolbar on Tapped command, then this should be the sequence of events:
Custom control is constructed, BindingContext is null.
Page instance is created, control is added to it.
When page's BindingContext is set, through power of property inheritance, all it's child controls' BindingContext is updated, including your custom control.
At this point your custom control's BindingContext is now referencing MyCustomersPageModel. So binding in <controls:TopToolbar BackCommand="{Binding MyBackCommand}" is appropriately set.
Now the Tapped binding doesn't care about BindingContext as it's source is explicitly specified, which is parent control TopToolbar - whose BackCommand in turn is bound to the view model's command. Hence, the view-model command is now bound to gesture-recognizer's Tapped command. And it works!
I've a AppResources.resx file where I store all strings that I use across my app. My Xamarin project contains two solutions one for Android and another for iOS. The Android app is working perfectly. But the iOS app is throwing Xaml errors that say something like "Cannot found static member AppResources".
Here's my xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:HalliganTL;assembly=HalliganTL"
x:Class="HalliganTL.View.LoginPage">
<Grid Style="{StaticResource backgroundColor}">
<Grid.RowDefinitions>
<RowDefinition Height="4*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Entry x:Name="entryEmail"
Placeholder="{x:Static local:AppResources.login_page_email_placeholder}" Text="{Binding Username}" Grid.Row="1" Grid.Column="1" />
<Entry x:Name="entryPassword"
Placeholder="{x:Static local:AppResources.login_page_password_placeholder}"
Text="{Binding Password}" IsPassword="true" Grid.Row="2" Grid.Column="1"/>
<ActivityIndicator x:Name="activityIndicator" IsRunning="False" Grid.Row="3" Grid.Column="1" />
<Button Text="{x:Static local:AppResources.login_page_login_button_text}"
Style="{StaticResource redButton}" Clicked="OnLoginButtonClicked" Grid.Row="4" Grid.Column="1"/>
<Button Text="{x:Static local:AppResources.login_page_forgot_password_button_text}"
Style="{StaticResource blueButton}" Clicked="OnForgotPasswordClicked" Grid.Row="5" Grid.Column="1"/>
</Grid>
</ContentPage>
Any tip about what can be causing this error?
UPDATE
I removed all AppResources.resx references in the XAML file, and yet It throws an error telling me that the "OnLoginButtonClicked" method doesn't exist in the .cs code. But It clearly does!
Here's my .cs code:
using Newtonsoft.Json;
using HalliganTL.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using XLabs.Ioc;
namespace HalliganTL.View
{
public partial class LoginPage : ContentPage
{
public LoginPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
Title = AppResources.login_page_title;
BindingContext = new MainViewModel();
}
async void OnForgotPasswordClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new ForgotPasswordPage());
}
async void OnLoginButtonClicked(object sender, EventArgs e)
{
//Validates email
if (!HalliganUtils.IsValidEmail(entryEmail.Text))
{
await DisplayAlert(AppResources.error_title,
AppResources.login_page_email_validation_error,
AppResources.accept_button_text);
return;
}
//Validates password
if (!HalliganUtils.IsValidPassword(entryPassword.Text))
{
await DisplayAlert(AppResources.error_title,
AppResources.login_page_password_validation_error,
AppResources.accept_button_text);
return;
}
HalliganCredential credentials = new HalliganCredential
{
Email = entryEmail.Text,
Password = entryPassword.Text
};
await App.RestClient.GetUserAuthAsync(credentials)
.OnSuccess((result) =>
{
Navigation.InsertPageBefore(new MainPage(), this);
Navigation.PopAsync();
})
.OnRequestStarted(() =>
{
activityIndicator.IsRunning = true;
entryEmail.IsEnabled = false;
entryPassword.IsEnabled = false;
})
.OnRequestCompleted(() =>
{
activityIndicator.IsRunning = false;
entryEmail.IsEnabled = true;
entryPassword.IsEnabled = true;
})
.OnHttpError((exception) =>
{
DisplayAlert(AppResources.error_title,
AppResources.login_page_login_failed_message,
AppResources.accept_button_text);
})
.OnUnknownError((exception) =>
{
DisplayAlert(AppResources.error_title,
AppResources.unknown_error,
AppResources.accept_button_text);
})
.Start();
}
}
}
UPDATE
I tried changing the method signatures but didn't work. What I tried:
public async void OnForgotPasswordClicked(object sender, EventArgs e)
and
public void OnForgotPasswordClicked(object sender, EventArgs e)
I am working with Silverlight 3 beta, and am having an issue. I have a page that has a user control that I worte on it. The user control has a dependency property on it. If the user control does not define a data context (hence using the parent's data context), all works well. But if the user control has its own data context, the dependency property's OnPropertyChanged method never gets called.
Here is a sample:
My Main Page:
<UserControl x:Class="TestDepProp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:app="clr-namespace:TestDepProp"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="100">
<Grid x:Name="LayoutRoot" Background="White">
<Border BorderBrush="Blue" BorderThickness="3" CornerRadius="3">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Text="Enter text here:" />
<TextBox x:Name="entryBlock" Text="{Binding Data, Mode=TwoWay}"/>
<Button Content="Go!" Click="Button_Click" />
<TextBlock Text="{Binding Data}" />
</StackPanel>
<Border BorderBrush="Blue" BorderThickness="3" CornerRadius="3" Margin="5">
<app:TestControl PropOnControl="{Binding Data}" />
</Border>
</StackPanel>
</Border>
</Grid>
</UserControl>
Main Page code:
using System.Windows;
using System.Windows.Controls;
namespace TestDepProp
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
MainPageData data = new MainPageData();
this.DataContext = data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
int i = 1;
i++;
}
}
}
Main Page's data context:
using System.ComponentModel;
namespace TestDepProp
{
public class MainPageData:INotifyPropertyChanged
{
string _data;
public string Data
{
get
{
return _data;
}
set
{
_data = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Data"));
}
}
public MainPageData()
{
Data = "Initial Value";
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Control XAML:
<UserControl x:Class="TestDepProp.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:app="clr-namespace:TestDepProp"
>
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel Orientation="Vertical" Margin="10" >
<TextBlock Text="This should change:" />
<TextBlock x:Name="ControlValue" Text="Not Set" />
</StackPanel>
</Grid>
</UserControl>
Contol code:
using System.Windows;
using System.Windows.Controls;
namespace TestDepProp
{
public partial class TestControl : UserControl
{
public TestControl()
{
InitializeComponent();
// Comment out next line for DP to work
DataContext = new MyDataContext();
}
#region PropOnControl Dependency Property
public string PropOnControl
{
get { return (string)GetValue(PropOnControlProperty); }
set { SetValue(PropOnControlProperty, value); }
}
public static readonly DependencyProperty PropOnControlProperty =
DependencyProperty.Register("PropOnControl", typeof(string), typeof(TestControl), new PropertyMetadata(OnPropOnControlPropertyChanged));
private static void OnPropOnControlPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TestControl _TestControl = d as TestControl;
if (_TestControl != null)
{
_TestControl.ControlValue.Text = e.NewValue.ToString();
}
}
#endregion PropOnControl Dependency Property
}
}
Control's data context:
using System.ComponentModel;
namespace TestDepProp
{
public class MyDataContext : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
To try it out, type something in the text box, and hit the Go button. Comment out the data context in the controls code to see that it starts to work.
Hope someone has an idea as to what is going on.
The user control's datacontext does not have a Data property.
Because it doesn't have a data property the databinding returns null which is already the default value so the property change never fires.