Xamarin Forms: Custom View (Single Implementation) - xamarin

I've been searching high and low to find an example of how to write a custom UI element that has one implementation (single renderer).
For example, is there a way to simply implement a class that inherits from StackLayout (written in C#) and can then be slotted into other layouts (xaml) as a UI element (similar to how its done using xml views in Android)?
The following is what I have right now, but I am getting errors:
ButtonCustom.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace DWalker
{
public partial class ButtonCustom : StackLayout
{
public ButtonCustom()
{
InitializeComponent();
}
public Color TextColor
{
get { return this.Text.TextColor; }
set { this.Text.TextColor = value; }
}
public Label TextControl
{
get { return this.Text; }
set { this.Text = value; }
}
}
}
ButtonCustom.xaml
<?xml version="1.0" encoding="UTF-8"?>
<StackLayout
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:common="clr-namespace:Dwalker;assembly=DWalker"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DWalker.ButtonCustom">
<Image
x:Name="Icon"
Source="Icon.png" />
<Label
x:Name="Text"
Text="Hello"
HorizontalOptions="Center"
LineBreakMode="NoWrap"
Font="Small"
TextColor="Black"/>
</StackLayout>
Then I'd like to add my custom element to a xaml layouts ContentPage like so:
ViewStart.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"
xmlns:common="clr-namespace:DWalker;assembly=DWalker"
xmlns:controls="clr-namespace:DWalker;assembly=DWalker"
x:Class="DWalker.ViewStart"
Title="DWalker" >
<AbsoluteLayout>
<controls:ButtonCustom />
</AbsoluteLayout>
</ContentPage>

Related

Xamarin.Forms ViewCell / Visual Studio 2019 - "Partial declarations of 'someType' must not specify different base classes"

In my Xamarin.Forms project I am attempting to create a reusable ViewCell. I can't figure out, though, why when I simply create a ViewCell XAML and CS file using the VS-supplied template, it will not compile due to the following error:
"Partial declarations of 'DateViewCell' must not specify different base classes"
This error pops the second I create the file (as you'll see in the code below, I haven't added any code yet and this is all boilerplate) and I have a fully functional Xamarin.Forms project that compiles with no issues if I remove the extension.
DateViewCell.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell 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="HelloWorld.Extensions.DateViewCell">
<ViewCell.View>
<StackLayout>
<Label Text="Hello Xamarin.Forms!" />
</StackLayout>
</ViewCell.View>
</ViewCell>
DateViewCell.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace HelloWorld.Extensions
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DateViewCell : ViewCell
{
public DateViewCell()
{
InitializeComponent();
}
}
}
DateViewCell.xaml.g.cs:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Xamarin.Forms.Xaml.XamlResourceIdAttribute("HelloWorld.Extensions.DateViewCell.xaml", "Extensions/DateViewCell.xaml", typeof(global::HelloWorld.Extensions.DateViewCell))]
namespace HelloWorld.Extensions {
[global::Xamarin.Forms.Xaml.XamlFilePathAttribute("Extensions\\DateViewCell.xaml")]
public partial class DateViewCell : global::Xamarin.Forms.ViewCell {
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "2.0.0.0")]
private void InitializeComponent() {
global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(DateViewCell));
}
}
}
Sort of at a dead end here. I tried creating this "DateViewCell" extension manually and I get the same error that I did when I let VS create the file for me. I'm starting to wonder if this is a bug in Xamarin.
Anyone have any suggestions? The SO articles I could find that referenced this particular error didn't really apply and I'm out of ideas on better ways to search this... Thanks everybody.

Is it possible to give a Xamarin Frame different border colors?

Here is the XAML that I have:
<Frame CornerRadius="1" HasShadow="false" Margin="10"
BackgroundColor="White" BorderColor="Silver" Padding="0" >
I saw on the Google Translate that's on iOS that they use something like this kind of a frame to surround different rows in settings. However they have a different border color on the top and bottom.
Does anyone know if there is a way to do the with a frame?
You could achieve that with a component, like this
BorderEntryComponent.xaml
<?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="X.Y.Z.BorderEntryComponent"
Spacing="0">
<BoxView
x:Name="TopBorder"
HeightRequest="2"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand" />
<Entry x:Name="Entry" />
<BoxView
x:Name="BottomBorder"
HeightRequest="2"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand" />
</StackLayout>
And, in your BorderEntryComponent.xaml.cs
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace X.Y.Z
{
public partial class BorderEntryComponent : StackLayout
{
public static readonly BindableProperty TopColorProperty =
BindableProperty.Create(nameof(TopColor), typeof(Color), typeof(BorderEntryComponent), default(Color), BindingMode.OneWay);
public static readonly BindableProperty BottomColorProperty =
BindableProperty.Create(nameof(BottomColor), typeof(Color), typeof(BorderEntryComponent), default(Color), BindingMode.OneWay);
public UnderlineEntryComponent()
{
InitializeComponent();
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == TopColorProperty.PropertyName)
{
this.TopBorder.Color = TopColor;
}
else if (propertyName == BottomColorProperty.PropertyName)
{
this.BottomBorder.Color = BottomColor;
}
}
public Color TopColor
{
get => (Color)GetValue(TopColorProperty);
set => SetValue(TopColorProperty, value);
}
public Color BottomColor
{
get => (Color)GetValue(BottomColorProperty);
set => SetValue(BottomColorProperty, value);
}
}
}
Then, you just do this on your .xaml
<components:UnderlineEntryComponent
TopColor = "Blue"
BottomColor = "Black" />
You can read more on Bindable Properties here
AFAIK, you don't have a built in option for what you are looking for.
You could play around by drawing multiple frames on top of each other with different colors and properties, but it is a bit too "hacky" for my taste.
I suggest you create a Custom Render for your own Frame control. This way, you will be able to draw the frame however you want and reuse your control in any other place.

How can I change the color of a Label in a template in the C# back end?

I have this template that's simplified for the question:
<?xml version="1.0" encoding="UTF-8"?>
<Frame xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:t="clr-namespace:Japanese.Templates"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.RoundButtonText" x:Name="this">
<Label Text="ABC" />
</Frame>
and this C#
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class RoundButtonText : BaseFrameButtonTemplate
{
public RoundButtonText()
{
InitializeComponent();
?? = Color.Red;
}
}
}
Can someone help me by telling me how I can change the TextColor of the label in the XAML inside the constructor of the back-end C#. Note that there's much more to this but I'm simplifying what I need for the question as if I know this then I can do the rest.
Specify x:Name="MyLabel" on the Label you want to access.
Then you can access that Label in your back-end C# file like so:
public RoundButtonText()
{
InitializeComponent();
MyLabel.TextColor = Color.Red; //OR
MyLabel.BackgroundColor = Color.Red;
}
That allows you to be able access any public property on the Label

ZXing view with FreshMVVM not triggering OnScanResult

Resoution TL;DR : https://gist.github.com/rupe120/78f8a57f0ed7ecacbdc13fa2da8d931a
I created my own scan page, converting the built-in ZXingScannerPage code (https://github.com/Redth/ZXing.Net.Mobile/blob/master/Source/ZXing.Net.Mobile.Forms/ZXingScannerPage.cs) to a Page + PageModel/View concept. The page code is below.
The problem is that OnScanResult is never triggered.
I was using ZXingScannerPage directly previously, and the OnScanResult event was successfully triggering but I wanted the page to follow the same format as the rest of the application. So the QR code I'm using should trigger it.
I must be missing a setup piece in the ZXingScannerView, but I can't see it.
Any thoughts?
SearchQrPage.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="MyApp.Pages.SearchQrPage"
xmlns:zxing="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms">
<ContentPage.Content>
<Grid>
<zxing:ZXingScannerView x:Name="scannerView" />
<zxing:ZXingDefaultOverlay x:Name="scannerOverlay"
TopText="Hold your phone up to the QR code"
BottomText="Scanning will happen automatically"
ShowFlashButton="True"/>
</Grid>
</ContentPage.Content>
</ContentPage>
SearchQrPage.xaml.cs
using MyApp.PageModels;
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace MyApp.Pages
{
public partial class SearchQrPage : ContentPage
{
public SearchQrPage()
{
InitializeComponent();
scannerView.Options = new ZXing.Mobile.MobileBarcodeScanningOptions
{
PossibleFormats =
new List<ZXing.BarcodeFormat>
{
ZXing.BarcodeFormat.QR_CODE
}
};
scannerView.OnScanResult += ScannerView_OnScanResult;
scannerOverlay.FlashButtonClicked += ScannerOverlay_FlashButtonClicked;
}
private void ScannerOverlay_FlashButtonClicked(Button sender, EventArgs e)
{
scannerView.ToggleTorch();
}
private void ScannerView_OnScanResult(ZXing.Result result)
{
var model = this.BindingContext as SearchQrPageModel;
if (model == null)
return;
scannerView.IsScanning = false;
if (model.ScanResultCommand.CanExecute(result))
model.ScanResultCommand.Execute(result);
}
}
}
Just set IsScanning = true at the constructor, for example. On the original ZXing's page, they do it on the OnAppearing event.
You've missed it up.

Xamarin.Forms XAML Image as root element

I am new to XAML and everywhere I read about it one would usually use some sort of container as the root view (StackLayout, ContentPage, ContentView, RelativeLayout etc).
But, I run into this XAML that uses Image as its root. Why would we do that?
<?xml version="1.0" encoding="utf-8" ?>
<Image
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:r="clr-namespace:My.Resources;assembly=My.Resources"
x:Class="My.Views.Buttons.MyView"
Source="MyImage.png"/>
I would like to replace this Image with FFImageLoading.CachedImage but for that I need to add FFImageLoading xmlns namespace but I cannot since namespaces are inside the Image tag, not outside.
You can specify the namespace in root tag and use it in root tag itself in XAML.
For e.g.:
<ffimageloading:CachedImage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:ffimageloading="clr-namespace:FFImageLoading.Forms;assembly=FFImageLoading.Forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SomeAppNameSpace.CustomCachedImage"
Source="http://loremflickr.com/600/600/nature?filename=simple.jpg">
</ffimageloading:CachedImage>
Just make sure you derive from the right class in code behind:
namespace SomeAppNameSpace
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomCachedImage : CachedImage
{
public CustomCachedImage()
{
InitializeComponent();
}
}
}

Resources