It happens that I define a ResourceDictionary for the app colors to use it in XAML files, and have a static class for these colors to use in the cs code:
for example:
<ResourceDictionary
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Themes.AppTheme">
<Color x:Key="BrandColor">#ffd92e</Color>
<Color x:Key="BgColor">#343433</Color>
</ResourceDictionary>
And the opposite class:
public static class AppColors
{
public static readonly Color BrandColor = (Color)Application.Current.Resources["BrandColor"];
public static readonly Color BGColor = (Color)Application.Current.Resources["BgColor"];
}
Another scenario,I use icons font in both xaml and cs,
in XAML it looks like , and in cs it's: \ue8d5 . I want to save them in a file where I can reference them by a meaningful names in XAML and cs like:
IconBell = \ue8d5
Is it possible to define resources like those in one place and use them in both XAML and code?
About the colors issue, it's very easy to solve it. You basically have to create a class containing your colors, and reference it in your XAML, with the x:Static extension.
You can do the same for solving your icons issue, but you'll probably need to create a converter for using it in XAML. For example, the important part of your icon value is the "e8d5" part, but in C# you use the "\u" and in the XAML you use the "&#x". You'll have to create a class with the icons, reference it in the XAML using the x:Static extension and use a converter to convert from C# to XAML, for example:
public class IconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
try
{
string icon = System.Convert.ToString(value);
return icon.Replace("\u", "&#x");
}
catch (Exception)
{
throw new InvalidCastException("The value is not a valid string.");
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
With this, you can unify your styles in a unique C# class, and just reference it in your XAML.
Related
Please question might be funny, not be funny at all or confusing! But the simple goal I wanted is to changing the value of binding context in xamarin.forms on runtime!!
IvalueConverter
class LoginFrameHeight : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (double)value / 1.9;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Resource Dictionary
<ResourceDictionary>
<local:LoginFrameHeight x:Key="frameHeight"/>
</ResourceDictionary>
Setting Frame Height
<Frame CornerRadius="15"
HeightRequest="{Binding Source={x:Reference frame},
Path=Height,Converter={StaticResource frameHeight}}" Padding="0"></Frame>
Code works fine! My problem is that I have about three (3) frames to apply different HeightRequested using same process! is it possible to change the (1.9) in IvalueConverter during
runtime, so that I can use the same class LoginFrameHeight instead of creating different classes for the frames?
The answer is "YES" you can use ConverterParameter here is quick ref: This official documentation will show more
Example:<Label Text="{Binding Red,Converter={StaticResource doubleToInt}, ConverterParameter=255, StringFormat='Red = {0:X2}'}" />
A bit confused here, I seem to have followed the steps that would allow me to make use of value converters.
I have my converter defined with a key, as such:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage Title="Article"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:XamarinMobile.Controls;assembly=XamarinMobile"
xmlns:converters="clr-namespace:XamarinMobile.Converters;assembly=XamarinMobile"
x:Class="XamarinMobile.ArticlePage">
<ContentPage.Resources>
<ResourceDictionary>
<converters:FontSizeConverter x:Key="FontSizeMapper"></converters:FontSizeConverter>
</ResourceDictionary>
</ContentPage.Resources>
I then make use of my converter in my XAML, as such:
<ContentView Padding="10,-10,10,0" Grid.Row="2" Grid.Column="0">
<StackLayout>
<Label x:Name="LabelAuthor" FontSize="{Binding 20, Converter={StaticResource FontSizeMapper}, ConverterParameter=20}" />
<Label x:Name="LabelPublishDate" FontSize="{Binding 10, Converter={StaticResource FontSizeMapper}, ConverterParameter=10}"/>
</StackLayout>
</ContentView>
And here is my actual converter code:
namespace XamarinMobile.Converters
{
public class FontSizeConverter : Xamarin.Forms.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value is double)
{
return App.NormalizeFontSize((double)value);
} else
{
return value;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I then put a break point in my value converter, but it never hits. Is there something obvious that I'm missing here? I'm pretty sure I followed the directions to a tee.
Your breakpoint isn't being hit due to what Gerald Versluis said. Your binding is broken. What your binding is saying is: Bind to the property named "10" on the BindingContext, and use the Converter FontSizeMapper, passing it an extra ConverterParameter of 10. "10" isn't a valid property name, so the binding is breaking. If you look in your logs, you should see a message similar to: "Binding: '10' property not found on ..."
One way to fix it would be to remove the "Path" you're trying to bind to and only make use of the ConverterParameter (assuming you don't need to bind to any real properties):
FontSize="{Binding Converter={StaticResource FontSizeMapper}, ConverterParameter=20}"
Note that you'll need to make use of the parameter in the converter, rather than the value (eg. if (parameter is double)).
If you don't need to bind to any properties, another way to fix it would be to use a custom markup extension instead.
[ContentProperty("FontSize")]
public class FontSizeMapperExtension : IMarkupExtension
{
public double FontSize { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
return App.NormalizeFontSize(FontSize);
}
}
Then you could use it in your XAML like:
FontSize="{converters:FontSizeMapper FontSize=10}
Edit
An example of binding to a property on an object:
public class YourViewModel
{
public double VMFontSize { get; set; }
}
public partial class ArticlePage : ContentPage
{
public ArticlePage()
{
InitializeComponent();
// NOTE: You'd probably get your view-model another way
var viewModel = new YourViewModel { VMFontSize = 10 };
BindingContext = viewModel;
}
}
Now that your view-model is set as the binding context, you can set the binding like:
FontSize="{Binding VMFontSize, Converter={StaticResource FontSizeMapper}}"
What this says is: Bind the FontSize property on the label to the VMFontSize property on the current BindingContext (your view-model), using the converter to map between the view-model's VMFontSize and the Label's FontSize. I left the ConverterParameter off here as it isn't really needed in this example, but you could pass one if you need it.
I would do this a different way, using a custom attached property, see more on attached properties here https://developer.xamarin.com/guides/xamarin-forms/xaml/attached-properties/
Here is a sample for your scenario, first we need to define an attached property, it can be in any class, I called mine FontHelper
namespace App23
{
public static class FontHelper
{
public static readonly BindableProperty FontSizeProperty =
BindableProperty.CreateAttached("FontSize", typeof(double), typeof(FontHelper), 0d, propertyChanging:OnPropertyChanging);
public static bool GetFontSize(BindableObject view)
{
return (bool)view.GetValue(FontSizeProperty);
}
public static void SetFontSize(BindableObject view, bool value)
{
view.SetValue(FontSizeProperty, value);
}
private static void OnPropertyChanging(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is Label)
{
var label = bindable as Label;
double fontSize = (double)newValue;
// normalize your font size here
label.FontSize = fontSize;
}
}
}
}
Then to use it in XAML, it looks 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"
xmlns:local="clr-namespace:App23"
x:Class="App23.MainPage">
<Label Text="Welcome to Xamarin Forms!"
VerticalOptions="Center"
HorizontalOptions="Center" local:FontHelper.FontSize="50"/>
</ContentPage>
My solution consist of 3 project:
My backend project with assembly My_Test_App (portable)
My_Test_App.Android
My_Test_App.iOS
In the backend project, I have this XAML page code (Please forgive the name)
<ContentPage
x:Class="My_Test_App.Pages.LoginPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:My_Test_App.Converters;assembly=My_Test_App"
xmlns:effects="clr-namespace:My_Test_App.Effects;assembly=My_Test_App"
xmlns:viewModels="clr-namespace:My_Test_App.ViewModels;assembly=My_Test_App"
xmlns:views="clr-namespace:My_Test_App.Views;assembly=My_Test_App">
<ContentPage.Resources>
<ResourceDictionary>
<converters:Converter1 x:Key="conv1" />
<converters:Converter2 x:Key="conv2" />
<converters:Converter3 x:Key="conv3" />
</ResourceDictionary>
</ContentPage.Resources>
</ContentPage>
It works on android and iPhone simulator, but when i tested it on the real iPhone, i get this error:
Xamarin.Forms.Xaml.XamlParseException: Position 13:14. Type converters:Converter1 not found in xmlns clr-namespace:My_Test_App.Converters;assembly=My_Test_App
My code for Converter1 in the backend project:
namespace My_Test_App.Converters
{
public class Converter1: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool original = (bool)value;
return !original;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public My_Test_App()
{
}
}
}
Could you help with this please? I have a few suspect here:
Underscore on assembly name, but i need to keep the current assembly name..
In the IOS project properties, i changed the linker Options in iOS Build section from "Link SDK only" to "Link all assemblies". But, if i don't change it, i got error "Could not AOT the assembly......."
Possible bug in the current xamarin version (mine is 4.2.2.11)
Thank you for helping me!
Type converters:Converter1 not found
The Xamarin linker uses static analysis to determine what IL code can be removed from your assemblies to reduce the size and due to reflection invoke usage from Xamarin.Form, the IValueConverter-based class appears not to be used.
In your Xamarin.Forms (PCL) project, add a PreserveAttribute class:
public sealed class PreserveAttribute : System.Attribute {
public bool AllMembers;
public bool Conditional;
}
Now add the [Preserve] w/ AllMembers attribute to your IValueConverter class to inform the Linker to skip this class:
[Preserve(AllMembers = true)]
public class Converter1: IValueConverter
{
~~~
}
Re: https://developer.xamarin.com/guides/ios/advanced_topics/linker/
I want to control the visibility of an image (inside a dynamically bind longlistselector control), depending on a binding value (say if somevalue>0 then make that image visible otherwise invisible).But there is no such event like itemdatabound in the longlistselector to accomplish this task, I am new to windows phone development, and really don't have an idea how to do this.Please help me guys.
Thanks,
Use a ValueConverter
public class BoolToVisiblityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value
? Visibility.Collapsed
: Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
A common way to achieve this would be to bind the visibility property of the Image to a property on your bound data object. Often, the property on the data would be a boolean and a converter would be used to convert the boolean to a Visibility enum value. E.g.,
<Image Visibility = {Binding IsVisible, Converter={StaticResource myBoolToVisibilityConverter} />
See binding with converters example
I'm trying to use a value converter. However, VS is complaining about this XAML:
<Grid x:Name="LayoutRoot" Background="White" Height="Auto" Width="Auto">
<Grid.Resources>
<local:DateFormatter x:Key="FormatConverter" />
</Grid.Resources>
The error:
The type 'local:DateFormatter' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built.
Am I missing an assembly reference, or is my XAML incorrect? I'm trying to follow an example on MSDN.
Update: I added the following attribute to UserControl:
xmlns:local="clr-namespace:MyNamespace"
I also added a DateFormatter class.
Now Intellisense pops up with "local" and "DateFormatter". However, it still gives the same error as above. The error does not occur for other types, such as App.
DateFormatter:
using System;
...
namespace MyNamespace
{
public class DateFormatter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime date = (DateTime) value;
return date.ToShortDateString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
Yes - you need to add an xmlns:local="clr-namespace:SilverlightApplication6" to map the namespace and assembly to the XAML namespace local. If you add the converter via Expression Blend, it will put the correct namespace declaration in for you.
As Michael S. Scherotter says, you'll need to add a namespace reference in XAML.
If you're following the MSDN article you'll need to include the following:
xmlns:local="clr-namespace:YOUR-NAMESPACE-HERE"
This is similar to a using statement in C#. It tells XAML where to find your DateFormatter type.
Update:
I'm not sure what's going wrong for you.
The following works for me (applying your DateFormatter code but in a different namespace):
MainPage.xaml
<UserControl x:Class="SilverlightApplication2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SilverlightApplication2">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<local:DateFormatter x:Key="FormatConverter" />
</Grid.Resources>
<TextBlock HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Text="{Binding FooDate, Converter={StaticResource FormatConverter}}"/>
</Grid>
</UserControl>
MainPage.xaml.cs
using System;
using System.Windows.Controls;
namespace SilverlightApplication2
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var foo = new Foo { FooDate = DateTime.Now };
DataContext = foo;
}
}
public class Foo
{
public DateTime FooDate { get; set; }
}
}
DateFormmatter.cs
using System;
using System.Windows.Data;
namespace SilverlightApplication2
{
public class DateFormatter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.ToShortDateString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}