C# Xaml - Use Custom Class - Works programatically but not in Xaml - c#

I'm pretty new with Xaml and i'm facing an issue . I want to use FontAwesome Icons in my app and after following a tutorial , i can use the icons programmatically (Code Below) .
Content = new StackLayout
{
Children = {
new FontIcon(FontIcon.Icon.Globe) {TextColor=Color.Red }
},
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};
However , when i try to implement this in Xaml - it crashes my app.
Code for Shared class extending label :
using Xamarin.Forms;
namespace myApp.Fonts
{
public class FontIcon : Label
{
public const string Typeface = "FontAwesome";
public FontIcon(string faIcon = null)
{
FontFamily = Typeface;
Text = faIcon;
}
public static class Icon
{
public static readonly string Gear = "";
public static readonly string Globe = "\uf000";
}
}
}
Xaml code ...Note that i'm already using the xmlns:local for another class
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="myApp.TestPage"
xmlns:ctt="clr-namespace:myApp.Fonts">
<ctt:FontIcon FontIcon ="\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
I'm guessing the issue is with this line :
<ctt:FontIcon FontIcon ="\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
I'm not sure how to access that class via xaml or if its even possible to use xlmns:ctt
EDIT-------------------------------------------------------------------------
I used debug and this is the actual error :
System.MissingMethodException: Default constructor not found for type myApp.Fonts.FontIcon
Edit 2 :
I did this :
public FAIcon()
{
}
And in xaml :
<custom:FAIcon FontFamily = "Typeface" Text = "\uf000" VerticalOptions="Center" HorizontalOptions="Center" />
The app doesn't crash now but it displays the plain text instead of the icon
This is my android renderer :
[assembly: ExportRenderer(typeof(FontIcon), typeof(FARenderer))]
namespace myApp.Droid.Renderers
{
public class FARenderer : LabelRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control.Typeface = Typeface.CreateFromAsset(Forms.Context.Assets, FontIcon.Typeface + ".ttf");
}
}
}
}

if you always want to use FontAwesome, set it in your constructor:
public const string Typeface = "FontAwesome";
public FAIcon()
{
FontFamily = TypeFace;
}
don't do this in your XAML, it just sets the FontFamily to "TypeFace" which is not what you want
<custom:FAIcon FontFamily = "Typeface" ...

Related

How to rotate Grid depending on a property easiest way

Hi I would like to know what the easies way to rotate a Grid would be.
I have 4 pages:
private static Figure[] array;
public App ()
{
Initialize(array); // Fills array with figures with ImageSources
InitializeComponent ();
MainPage = new Page(array,Color.Red);
}
class Figure
{
private ImageSource Source {get; set;}
public Figure(ImageSource source)
{
Source = source;
}
}
class Page
{
private Color Color;
private Grid Grid;
public Page (Figure[] Figures, Color color)
{
Color = color;
// Now this is where I need help...
}
}
I would like to have a Grid always the same size and always filled with the same array but depending on the Color the orientation should change. In fact the whole Grid should just rotate 90degrees depending on the Color. These Grids should have ImageButtons which bind to the Imagesource of the figure (with a Converter). I thought about Creating 4Grids in Xaml and implement everything by hand and just give every page the custom Grid. Another option I came up with was creating one Grid only and using the rotation-method of the Grid (but with this option I have to rotate back every child of the Grid as otherwise the pictures would rotate with the Grid... As I think both solutions are quite inconvenient I was wondering what other options I have. Maybe someone can help me? Thanks a lot...
Example of ImageSources that change based on a setting.
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestBugs.MainPage">
<StackLayout>
<Label Text="Test"/>
<Grid ColumnDefinitions="50,50" RowDefinitions="50,50">
<Image Grid.Row="0" Grid.Column="0" Source="{Binding Source1A}" BackgroundColor="Red"/>
<Image Grid.Row="0" Grid.Column="1" Source="{Binding Source1B}" BackgroundColor="Green"/>
<Image Grid.Row="1" Grid.Column="0" Source="{Binding Source2A}" BackgroundColor="Blue"/>
<Image Grid.Row="1" Grid.Column="1" Source="{Binding Source2B}" BackgroundColor="Yellow"/>
</Grid>
</StackLayout>
</ContentPage>
C#:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TestBugs
{
public partial class MainPage : ContentPage
{
// REPLACE "TestBugs" with your project's assembly name.
public const string AssemblyName = "TestBugs";
public enum Orientation
{
One, Two, Three, Four
}
const int NOrientations = 4;
public MainPage()
{
// Assuming stored locally in files or resources.
// If need server queries, recommend not doing this in constructor.
LoadOurImages();
InitializeComponent();
// In this simple example, the binding sources are in the page itself.
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
BackgroundTestLoop();
}
static Random Rand = new Random();
private void BackgroundTestLoop()
{
Task.Run(async () =>
{
const int NTimes = 20;
for (int i = 0; i < NTimes; i++)
{
await Task.Delay(3000);
Orientation nextOrient = (Orientation)Rand.Next(NOrientations);
// Only affect UI from main thread.
Device.BeginInvokeOnMainThread(() =>
{
Orient = nextOrient;
});
}
});
}
public Orientation Orient {
get => _orient;
set
{
_orient = value;
// When Orient changes, that affects the values of these properties.
// OnPropertyChanged is from super-class BindableObject.
OnPropertyChanged(nameof(Source1A));
OnPropertyChanged(nameof(Source1B));
OnPropertyChanged(nameof(Source2A));
OnPropertyChanged(nameof(Source2B));
}
}
private Orientation _orient = Orientation.One;
// Public getters. These change when Orient changes.
public ImageSource Source1A => Sources[Indexes1A[(int)Orient]];
public ImageSource Source1B => Sources[Indexes1B[(int)Orient]];
public ImageSource Source2A => Sources[Indexes2A[(int)Orient]];
public ImageSource Source2B => Sources[Indexes2B[(int)Orient]];
List<string> ResourcePaths = new List<string> {
"apple.png", "banana.png", "car.png", "dog.png"};
List<ImageSource> Sources = new List<ImageSource>();
// Change these as needed.
List<int> Indexes1A = new List<int> { 0, 1, 2, 3 };
List<int> Indexes1B = new List<int> { 1, 2, 3, 0 };
List<int> Indexes2A = new List<int> { 2, 3, 0, 1 };
List<int> Indexes2B = new List<int> { 3, 0, 1, 2 };
private void LoadOurImages()
{
foreach (var path in ResourcePaths)
Sources.Add(CreateOurSource(path));
}
private ImageSource CreateOurSource(string resourcePath)
{
// For embedded resources stored in project folder "Media".
var resourceID = $"{AssemblyName}.Media.{resourcePath}";
// Our media is in the cross-platform assembly. Find that from this page.
Assembly assembly = this.GetType().GetTypeInfo().Assembly;
ImageSource source = ImageSource.FromResource(resourceID, assembly);
return source;
}
}
}

Grayscale image not showing inside a CoverFlowView [Xamarin Forms iOS]

I am currently working with a coverflowview by using this nuget:
https://github.com/AndreiMisiukevich/CardView
Works very well with the binding when I use a regular image or cachedimage (ffimageloading nuget). However now i try to grayscale the image by using a custom control. I successfully run the code to grayscale it (when propertyischanged to IsSelectable true), but for some reason the image is not showing at all, if i remove the grayscale logic, the image shows nicely.
<cards:CoverFlowView PositionShiftValue="40"
ItemsSource="{Binding Items}"
VerticalOptions="FillAndExpand"
HeightRequest="360">
<cards:CoverFlowView.ItemTemplate>
<DataTemplate>
<AbsoluteLayout HeightRequest="360">
<controls:GrayScaleImage Aspect="AspectFill"
AbsoluteLayout.LayoutBounds="0.0, 0.5, 1, 1"
AbsoluteLayout.LayoutFlags="All"
Source="{Binding ProgramDeserialized.Image}"
IsSelectable="{Binding IsSelectable}"/>
</AbsoluteLayout>
</DataTemplate>
</cards:CoverFlowView.ItemTemplate>
</cards:CoverFlowView>
And custom control:
public class GrayScaleImage : CachedImage
{
public static BindableProperty IsSelectableProperty = BindableProperty.Create(nameof(IsSelectable), typeof(bool), typeof(GrayScaleImage), true, propertyChanged: UpdateImage);
public bool IsSelectable
{
get { return (bool)GetValue(IsSelectableProperty); }
set { SetValue(IsSelectableProperty, value); }
}
private static void UpdateImage (BindableObject bindable, object oldColor, object newColor)
{
var view = (GrayScaleImage)bindable;
if (!view.IsSelectable)
{
var transformations = new System.Collections.Generic.List<ITransformation>() {
new GrayscaleTransformation()
};
view.Transformations = transformations;
}
}
}
Not sure what the issue might be. When i did it on a regular stacklayout bindable list, and applied the same logic, it works, so my gutfeeling is that there is some issue with the coverflowview nuget.
How did you set binding for the Image? I created a sample to test the function code, the grayScale image works fine.
Check the screenshot:
https://us.v-cdn.net/5019960/uploads/editor/ab/jqki5zvo7cfw.gif
Here is the code about the model class and the viewModel class, you could refer to it.
public class CustomModel
{
public Xamarin.Forms.ImageSource MyImage { get; set; }
public bool IsSelectable { get; set; }
}
public class CustomViewModel
{
public ObservableCollection<CustomModel> Items { get; set; }
public CustomViewModel()
{
Items = new ObservableCollection<CustomModel>();
//add the data
}
}

Delegate was deprecated in iOS 12.0. Please adopt WKWebView. How do I fix this in Xamarin?

I am trying to create a Custom WebView Renderer for iOS and Android. My main goal is to make the WebView fit it's HTML contents;
After googling, I soon realized that this is only possible by making custom renderers for both iOS and Android.
I am using a solution that requires a delegate. You can view the solution here. However, this solution was posted in 2016 and therefore I get this compile time error message: "'Delegate' was deprecated in iOS 12.0. No longer supported; please adopt 'WKWebView'.
PostWebView.xaml
<WebView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Yoors.Views.Templates.PostWebView" x:Name="WebViewer" BackgroundColor="White" Margin="0, 10, 0, 0" VerticalOptions="FillAndExpand" HeightRequest="1000">
<WebView.Source>
<HtmlWebViewSource Html="{Binding Data.Content}" />
</WebView.Source>
</WebView>
CustomWebViewRenderer.cs
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
Delegate = new CustomUIWebViewDelegate(this);
}
}
CustomUIWebViewDelegate.cs
public class CustomUIWebViewDelegate : UIWebViewDelegate
{
CustomWebViewRenderer _webViewRenderer;
public CustomUIWebViewDelegate(CustomWebViewRenderer webViewRenderer = null)
{
_webViewRenderer = _webViewRenderer ?? new CustomWebViewRenderer();
}
public override async void LoadingFinished(UIWebView webView)
{
var wv = _webViewRenderer.Element as PostWebView;
if (wv != null)
{
await System.Threading.Tasks.Task.Delay(100); // wait here till content is rendered
wv.HeightRequest = (double)webView.ScrollView.ContentSize.Height;
}
}
}
How do I adopt WKWebView according to my code?
Its quite easy actually, Create a Custom Webview something like this:
public class MyWebView : WebView
{
public static readonly BindableProperty UrlProperty = BindableProperty.Create(
propertyName: "Url",
returnType: typeof(string),
declaringType: typeof(MyWebView),
defaultValue: default(string));
public string Url
{
get { return (string)GetValue(UrlProperty); }
set { SetValue(UrlProperty, value); }
}
}
Then in your iOS CustomRenderer do something like this:
[assembly: ExportRenderer(typeof(MyWebView), typeof(MyWebViewRenderer))]
namespace WKWebView.iOS
{
public class MyWebViewRenderer : ViewRenderer<MyWebView, WKWebView>
{
WKWebView _wkWebView;
protected override void OnElementChanged(ElementChangedEventArgs<MyWebView> e)
{
base.OnElementChanged(e);
if (Control == null)
{
var config = new WKWebViewConfiguration();
_wkWebView = new WKWebView(Frame, config);
SetNativeControl(_wkWebView);
}
if (e.NewElement != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Url)));
}
}
}
}

Xamarin passing arguments between pages

I'm trying to open a new Page on a tap event, which should display some detailed information on a certain object. For that to work, I need to pass the object itself or its ID to the new page. So I added an argument to the constructor of the detail page as follows.
void onItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item != null)
{
bool convOk = Int32.TryParse((string)e.Item, out int id);
if (convOk)
{
Navigation.PushAsync(new DetailPage(id));
}
}
}
And the DetailPage has its own DetailViewModel, which is set as the BindingContext within the code-behind.
DetailPage XAML:
<?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="Foo.Views.DetailPage">
<ContentPage.Content>
<StackLayout Orientation="Vertical">
<Label Text="FooBar" />
<Label Text="{Binding trackID}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
DetailPage code-behind
namespace Foo.Views
{
public partial class DetailPage : ContentPage
{
public DetailPage(int trackID)
{
InitializeComponent();
BindingContext = new DetailViewModel(trackID);
}
}
}
DetailViewModel:
namespace Foo.ViewModels
{
public class DetailViewModel : BaseViewModel
{
// trackID prop
int _trackID;
int trackID
{
get { return _trackID; }
set
{
_trackID = value;
notifyPropertyChanged(nameof(trackID));
}
}
public TargetDetailViewModel(int tid)
{
trackID = tid;
}
}
}
However, the binding between the DetailPage and the DetailViewModel doesn't seem to work, the page doesn't show anything. The id itself is passed correctly all the way down to the DetailViewModel.
Is this due to the order of initialization? I presume that everything written in XAML will be executed in the DetailPage.InitializeComponent() method? If that's correct, is it safe/correct to instantiate the ViewModel before the DetailPage.InitializeComponent()?
Any hint appreciated.
Your trackID property is not public.
Note: if you watch the application log output you can catch binding problems like this (filter it by the string Binding:)
Log example of a private variable not being bound:
Binding: 'trackID' property not found on 'XXXX.VM', target property: 'Xamarin.Forms.Label.Text'
DetailViewModel Fix:
public class DetailViewModel : BaseViewModel
{
int _trackID;
public int trackID;
{
get { return _trackID; }
set
{
_trackID = value;
notifyPropertyChanged(nameof(trackID));
}
}
~~~~
}

changing xamarin.forms colors at runtime

i am making an app using xamarin.forms and i have set up a color scheme that i want to be able to change within the settings to either a dark style or a light style right now it all works except i have to restart the app everytime after i select a different color scheme.
here is where i am trying to change it at runtime
private void DarkThemeClick(object sender, EventArgs e)
{
database.DropTable(new StyleModel());
database.CreateTable(new StyleModel());
database.SaveItem(new StyleModel() { ThemeNum = 1 });
App.ActiveStyle = new DarkStyle();
}
private void LightThemeClick(object sender, EventArgs e)
{
database.DropTable(new StyleModel());
database.CreateTable(new StyleModel());
database.SaveItem(new StyleModel() { ThemeNum = 0 });
App.ActiveStyle = new LightStyle();
}
here is an example of an item that im using that i want to change the colors on
using System;
using TestXamForms.Style;
using Xamarin.Forms;
namespace TestXamForms.Helpers
{
class EntryValueCell : StackLayout
{
public EntryValueCell(string key,int FieldIdx, string value = "", bool isNumber = false)
{
Entry entry;
Label label = new Label()
{
TextColor = App.ActiveStyle.LabelTextColor,
Text = key,
HorizontalOptions = LayoutOptions.End
};
if (isNumber)
{
entry = new Entry()
{
ClassId = FieldIdx.ToString(),
TextColor = App.ActiveStyle.LabelTextColor,
HorizontalOptions = LayoutOptions.FillAndExpand,
Keyboard = Keyboard.Numeric,
Text = value,
};
}
else
{
entry = new Entry()
{
ClassId = FieldIdx.ToString(),
TextColor = App.ActiveStyle.LabelTextColor,
HorizontalOptions = LayoutOptions.FillAndExpand,
Keyboard = Keyboard.Text,
Text = value
};
}
BackgroundColor = App.ActiveStyle.StackLayoutBackground;
Orientation = StackOrientation.Horizontal;
VerticalOptions = LayoutOptions.FillAndExpand;
Children.Add(label);
Children.Add(entry);
}
}
}
here is an example of one of the color schemes
using Xamarin.Forms;
namespace TestXamForms.Style
{
public class LightStyle : StyleBase
{
public LightStyle()
{
LabelTextColor = Color.Black;
ButtonColor = Color.FromHex("337ab7");
StackLayoutBackground = Color.FromHex("eff0f1");
InputBackgroundColor = Color.White;
PlaceHolderColor = Color.Gray;
TableColor = Color.FromHex("e6e6e6");
StacklayoutBorderColor = Color.Black;
}
}
}
here is styleBase that the file above is inheriting
using TestXamForms.Models;
using Xamarin.Forms;
namespace TestXamForms.Style
{
public class StyleBase : ModelBase
{
public enum ThemeNum : int
{
Light = 0, Dark = 1
}
public Color LabelTextColor { get; set; }
public Color ButtonColor { get; set; }
public Color StackLayoutBackground { get; set; }
public Color InputBackgroundColor { get; set; }
public Color PlaceHolderColor { get; set; }
public Color StacklayoutBorderColor { get; set; }
public Color TableColor { get; set; }
public int ThemeNums { get; set; }
}
}
here is the part of App.cs file that loads the color scheme when the app starts
static StyleBase activeStyle { get; set; }
public static StyleBase ActiveStyle
{
get
{
if (activeStyle == null)
{
StyleModel styleBase = database.GetItems(new StyleModel()).First();
if (styleBase == null)
{
database.SaveItem(new StyleModel() { ThemeNum = 0 }); //sets the default color scheme to light style
styleBase = database.GetItems(new StyleModel()).First();
}
int themeNum = styleBase.ThemeNum;
switch (themeNum)
{
case (int)StyleBase.ThemeNum.Dark:
activeStyle = new DarkStyle();
break;
case (int)StyleBase.ThemeNum.Light:
activeStyle = new LightStyle();
break;
}
}
return activeStyle;
}
set { } }
Have a look at this blog post. Particularly the bit about DynamicResources and Styles.
<Application
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Your.App">
<Application.Resources>
<ResourceDictionary>
<Color x:Key="backgroundColor">#33302E</Color>
<Color x:Key="textColor">White</Color>
</ResourceDictionary>
</Application.Resources>
</Application>
Now set your resources
<Label Text="{Binding Name}" FontSize="Medium" FontAttributes = "Bold" TextColor = "{DynamicResource textColor}" LineBreakMode="NoWrap"/>
<Label Text="{Binding Text}" FontSize="Small" LineBreakMode="WordWrap" TextColor = "{DynamicResource textColor}"/>
Now in code you can change your resource on the fly
App.Current.Resources ["backgroundColor"] = Color.White;
App.Current.Resources ["textColor"] = Color.Black;
If you're using 2.3 you could also try the built in themes
The problem you are facing is that everything is already rendered and you have not bound to any property which will re render the changes you made in the code to the UI. You can take the MVVM approach and create properties and bind to them and notify when they are changed in the UI thread.
If you are interested only in dark and light themes then you can use the in built Light Theme and Dark Theme. You can also create Custom Themes.
A theme is added to a Xamarin.Forms application by including the
Xamarin.Forms.Theme.Base Nuget package, plus an additional package
that defines a specific theme (eg. Xamarin.Forms.Theme.Light) or else
a local theme can be defined for the application.
In addition to automatically styling the common controls the Light and Dark themes currently support the following classes that can be applied by setting the StyleClass on these controls:
BoxView -
HorizontalRule,
Circle,
Rounded
Image -
Circle,
Rounded,
Thumbnail
Button -
Default,
Primary,
Success,
Info,
Warning,
Danger,
Link,
Small,
Large
Label -
Header,
Subheader,
Body,
Link,
Inverse
To add a theme to your application, do the following :
Add the Nuget packages to your project.
Add theme to Resource Dictionary in App.xaml
Use the Style class to apply predefined style classes in the theme.
<Button Text="Button Class Default" StyleClass="Default" />
<Button Text="Button Class Primary" StyleClass="Primary" />
<Button Text="Button Class Success" StyleClass="Success" />
Read more about themes here.

Categories

Resources