Custom control in Xamarin Forms doesn't work - c#

I'm trying to create a custom control of an ImageButton to which I can assign a property that is to give it a color and another an image with Source. I have tried to do it this way but the image and the color that I am giving it does not work for me. This is my code:
View1.xaml.cs:
public partial class View1 : ContentView
{
public static readonly BindableProperty ImageSourceProperty =
BindableProperty.Create("Source", typeof(ImageSource),typeof(ImageButton),default(ImageSource));
public ImageSource Source {
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
//============================================
public static readonly BindableProperty ButtonColorProperty =
BindableProperty.Create("ButtonColor", typeof(Color), typeof(ImageButton), Color.White);
public Color ButtonColor
{
get { return (Color)GetValue(ButtonColorProperty); }
set { SetValue(ButtonColorProperty, value); }
}
public View1()
{
InitializeComponent();
}
}
View1.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomContro.View1">
<StackLayout>
<ImageButton CornerRadius="20" WidthRequest="70" HeightRequest="70" />
</StackLayout>
</ContentView>
MainPage.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="CustomContro.MainPage"
xmlns:local="clr-namespace:CustomContro"
>
<AbsoluteLayout>
<local:View1
Source="dieciseis.png"
ButtonColor="Green"
/>
</AbsoluteLayout>
</ContentPage>

You could try to change like below:
the custom control View1:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class View1 : ContentView
{
public static readonly BindableProperty ImageSourceProperty =
BindableProperty.Create("ImageSource", typeof(ImageSource), typeof(View1), null);
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
public static readonly BindableProperty ButtonColorProperty =
BindableProperty.Create("ButtonColor", typeof(Color), typeof(View1), null);
public Color ButtonColor
{
get { return (Color)GetValue(ButtonColorProperty); }
set { SetValue(ButtonColorProperty, value); }
}
public View1()
{
InitializeComponent();
BindingContext = this;
}
}
the View1.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView 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="EntryCa.View1">
<ContentView.Content>
<StackLayout>
<ImageButton x:Name="image" CornerRadius="20" WidthRequest="70" HeightRequest="70" Source="{Binding ImageSource}" BackgroundColor="{Binding ButtonColor}" />
</StackLayout>
</ContentView.Content>
</ContentView>
use it like:
<local:View1 ImageSource="dieciseis.png" ButtonColor="Green"></local:View1>

You have to set the values for the ImageButton like this:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomContro.View1"
x:Name="MyAwesomeContentView">
<StackLayout>
<ImageButton CornerRadius="20"
WidthRequest="70"
HeightRequest="70"
ImageSource="{Binding Path=Source, Source={x:Reference MyAwesomeContentView}}"
Color="{Binding Path=ButtonColor, Source={x:Reference MyAwesomeContentView}}"
/>
</StackLayout>
</ContentView>

Related

.net MAUI, android System.InvalidOperationException: 'Native View not set' on navigation

Can't find source of evil, have two pages, added as Singleton service, when i navigate to the second page for a first time everything great, when i navigate second time i get:
System.InvalidOperationException: 'Native View not set'
MAUI version 6.0.300-rc.2.5513+sha.8a017cf73-azdo.6048189
Emulator Android11 API 30
Tested on Desktop everything seems ok...
MauiProgram.cs
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
builder.Services.AddSingleton<ShellViewModel>();
builder.Services.AddSingleton<AppShell>();
builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<SettingsPage>();
return builder.Build();
}
}
App.xaml - default to new project
App.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new AppShell();
Routing.RegisterRoute(nameof(MainPage), typeof(MainPage));
Routing.RegisterRoute(nameof(SettingsPage), typeof(SettingsPage));
}
}
AppShell.xaml
<?xml version="1.0" encoding="UTF-8" ?>
<Shell
x:Class="MauiAppTest.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:MauiAppTest"
xmlns:vm="clr-namespace:MauiAppTest.ViewModels"
x:DataType="vm:ShellViewModel"
Shell.FlyoutBehavior="Disabled">
<Shell.ToolbarItems>
<ToolbarItem Command="{Binding GoToSettings}" Text="Settings"/>
</Shell.ToolbarItems>
<ShellContent
Title="Hello, World!"
ContentTemplate="{DataTemplate local:MainPage}"
Route="MainPage" />
</Shell>
AppShell.cs
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
BindingContext = new ShellViewModel();
}
}
ShellViewModel.cs
public class ShellViewModel: ObservableObject
{
public AsyncRelayCommand GoToSettings { get; private set; }
public ShellViewModel()
{
GoToSettings = new AsyncRelayCommand(async () => await Shell.Current.GoToAsync("SettingsPage"));
}
}
MainPage and SettingsPage just two default blank pages
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppTest.MainPage"
Title="MainPage">
<StackLayout>
<Label Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppTest.SettingsPage"
Title="SettingsPage">
<StackLayout>
<Label Text="Welcome to .NET MAUI!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</ContentPage>

How to send an argument to ContentView?

I'm trying to create a ContentPage that contains a TabView from XamarinCommunityToolkit.
Lets say that the Tabs define an ObservableCollection of Categories, and every TabViewItem should load a ContentView and passes a GroupId as an Argument / Property, and then I use that GroupId to filter Products list.
What's the best way to passe an argument to the ContentView ?
Update :
I've tried to use BindablePropertiy but, in the debugger, I can see the newValue recieved, but nothing shows in the Label :
MainPage.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
mc:Ignorable="d"
x:Class="mynamespace.Views.MainPage"
Title="{Binding Title}"
xmlns:local="clr-namespace:mynamespace.Views"
xmlns:vm="clr-namespace:mynamespace.ViewModels"
xmlns:model="clr-namespace:mynamespace.Models"
x:Name="MainPage">
<ContentPage.Content>
<xct:TabView Grid.Row="0"
TabStripPlacement="Top"
TabStripBackgroundColor="White"
TabStripHeight="48"
TabIndicatorColor="Orange"
TabIndicatorHeight="2"
TabItemsSource="{Binding Categories}">
<xct:TabView.TabViewItemDataTemplate>
<DataTemplate>
<Grid>
<Label Text="{Binding Name}"
FontAttributes="Bold"
VerticalOptions="Center"
Padding="6, 0"/>
</Grid>
</DataTemplate>
</xct:TabView.TabViewItemDataTemplate>
<xct:TabView.TabContentDataTemplate>
<DataTemplate>
<local:GroupView GroupId="{Binding Id}" />
</DataTemplate>
</xct:TabView.TabContentDataTemplate>
</xct:TabView>
</ContentPage.Content>
GroupView.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace mynamespace.Views
{
public partial class GroupView : ContentView
{
public string GroupId
{
get { return (string)GetValue(GroupIdProperty); }
set { SetValue(GroupIdProperty, value); }
}
public static readonly BindableProperty GroupIdProperty = BindableProperty.Create(
nameof(GroupId),
typeof(string),
typeof(GroupView),
"Default_V",
defaultBindingMode: BindingMode.OneWay,
propertyChanged: GroupIdChanged
);
private static void GroupIdChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (ProductListPage)bindable;
control.GroupId = newValue?.ToString();
}
public GroupView()
{
InitializeComponent();
BindingContext = this;
}
}
}
GroupView.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="mynamespace.Views.GroupView">
<ContentView.Content>
<StackLayout>
<Label Text="{Binding GroupId}" /> <!-- Shows nothing -->
</StackLayout>
</ContentView.Content>
</ContentView>
Category class :
public class Category
{
private string id;
private string name;
private string description;
public string Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
public string Description { get => description; set => description = value; }
}
ProductListViewModel.cs
public class ProductListViewModel : BaseViewModel
{
public string GroupId { get; set; }
public ProductListViewModel()
{
}
public ProductListViewModel(string groupId)
{
GroupId = groupId;
}
}
Update :
[0:] Binding: 'GroupId' property not found on 'mynamespace.Models.Category', target property: 'Xamarin.Forms.Label.Text'
Don't assign bindings internally inside custom controls. You could do like this:
public partial class GroupView : ContentView
{
GroupViewModel _viewModel;
public string GroupId
{
get { return (string)GetValue(GroupIdProperty); }
set { SetValue(GroupIdProperty, value); }
}
public static readonly BindableProperty GroupIdProperty = BindableProperty.Create(
nameof(GroupId),
typeof(string),
typeof(GroupView),
"Default_V",
defaultBindingMode: BindingMode.OneWay,
propertyChanged: GroupIdChanged
);
private static void GroupIdChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (GroupView)bindable;
control.GroupId = (string)newValue;
control.label.Text = control.GroupId;
}
public GroupView()
{
InitializeComponent();
}
}
then in xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="mynamespace.Views.GroupView">
<ContentView.Content>
<StackLayout>
<Label x:Name="label" />
</StackLayout>
</ContentView.Content>
</ContentView>

Xamarin WPF .NET 5 Displaying WebView2

I'm trying to display a WebView2 within a Xamarin WPF app on .NET 5.
So at startup I set the MainPage to Navigator which looks like so:
Navigator.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:local="clr-namespace:XamTestNET5.Views"
x:Class="XamTestNET5.Navigator" Title="Here is a title">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackLayout Grid.Row="0" Grid.Column="0" Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Label FontSize="Medium">Hello</Label>
<!--<local:PlaceHolderView></local:PlaceHolderView>-->
<local:CustomWebBrowserView x:Name="browser" Source="https://www.google.com"></local:CustomWebBrowserView>
</StackLayout>
<StackLayout Grid.Row="1" Grid.Column="0" Orientation="Horizontal" HorizontalOptions="FillAndExpand" BackgroundColor="{StaticResource Primary}">
<Button FontSize="Large" Text="Tab 1" BorderWidth="0" BorderColor="Transparent"></Button>
<Button FontSize="Large" Text="Tab 2" BorderWidth="0" BorderColor="Transparent"></Button>
<Button FontSize="Large" Text="Tab 3" BorderWidth="0" BorderColor="Transparent"></Button>
</StackLayout>
</Grid>
</ContentPage>
CustomWebBrowserView.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamTestNET5.Views.CustomWebBrowserView">
<ContentView.Content>
<Grid></Grid>
</ContentView.Content>
</ContentView>
CustomWebBrowserView.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 XamTestNET5.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomWebBrowserView : ContentView
{
public static readonly BindableProperty SourceProperty = BindableProperty.Create(
propertyName: "Source",
returnType: typeof(string),
declaringType: typeof(string),
defaultValue: "");
public string Source
{
get { return (string)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public CustomWebBrowserView()
{
InitializeComponent();
}
}
}
WebBrowserRenderer.cs in the WPF project:
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms.Platform.WPF;
using XamTestNET5.Views;
using XamTestNET5WPF.Renderers;
[assembly: ExportRenderer(typeof(CustomWebBrowserView), typeof(WebBrowserRenderer))]
namespace XamTestNET5WPF.Renderers
{
public class WebBrowserRenderer : ViewRenderer<CustomWebBrowserView, WebView2>
{
WebView2 webView;
CoreWebView2EnvironmentOptions options = new CoreWebView2EnvironmentOptions();
CoreWebView2Environment env;
String userDataFolder = Path.GetTempPath();
public WebBrowserRenderer() : base()
{
}
private void WebView_CoreWebView2Ready(object sender, EventArgs e)
{
this.webView.CoreWebView2.Navigate(#"https://www.bing.com");
}
protected async override void OnElementChanged(ElementChangedEventArgs<CustomWebBrowserView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
webView = new WebView2();
env = await CoreWebView2Environment.CreateAsync("", userDataFolder, options);
webView.CoreWebView2Ready += WebView_CoreWebView2Ready;
webView.Source = new Uri(#"https://www.bing.com");
webView.Visibility = System.Windows.Visibility.Visible;
this.SetNativeControl(webView);
await webView.EnsureCoreWebView2Async(env);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == nameof(CustomWebBrowserView.SourceProperty))
{
this.webView.Source = new Uri(Element.Source);
this.webView.CoreWebView2.Navigate(Element.Source);
}
}
}
}
I can see by debugging that I'm getting the WebView2 but it obliterates the content page leaving only the title.
If I comment out the CustomWebBrowserView and put in a PlaceHolderView stub, which just looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamTestNET5.Views.PlaceHolderView">
<ContentView.Content>
<StackLayout>
<Label Text="Placeholder" />
</StackLayout>
</ContentView.Content>
</ContentView>
The layout at least isn't obliterated, but of course no WebView2.
I can't get Shell to work right either but that's to be expected: https://github.com/xamarin/Xamarin.Forms/issues/7377
So I thought that the workaround in the meantime might be just to create my own custom navigation within the app, but I want to provide WebView2 controls in WPF. Am I doing something wrong in the WebBrowserRenderer or Xaml?
Change CustomWebBrowserView to the following, basing off of View rather than ContentView and everything is working now with no obliteration of the view:
CustomWebBrowserView.Xaml:
<?xml version="1.0" encoding="UTF-8"?>
<View xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamTestNET5.Views.CustomWebBrowserView">
</View>
CustomWebBrowserView.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 XamTestNET5.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomWebBrowserView : View
{
public static readonly BindableProperty SourceProperty = BindableProperty.Create(
propertyName: "Source",
returnType: typeof(string),
declaringType: typeof(string),
defaultValue: "");
public string Source
{
get { return (string)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
public CustomWebBrowserView()
{
InitializeComponent();
}
}
}
Navigator.xaml:
<StackLayout Grid.Row="0" Grid.Column="0" Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<Label FontSize="Medium">Hello</Label>
<!--<local:PlaceHolderView></local:PlaceHolderView>-->
<local:CustomWebBrowserView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" x:Name="browser" Source="https://www.sneauxkones.com"></local:CustomWebBrowserView>
</StackLayout>

xamarin mvvmcross TabbedPage inside TabbedPage

How do I easily implement a tabbed page inside a tabbed page in xamarin forms with mvvmcross?
TabbedPage1;
[MvxTabbedPagePresentationAttribute(Position = TabbedPosition.Root, WrapInNavigationPage = true, NoHistory = false)]
public partial class TabbedPage1: MvxTabbedPage<ViewModels.TabbedPage1ViewModel>
{
public TabbedPage1()
{
InitializeComponent();
}
}
TempPage;
[MvxTabbedPagePresentationAttribute(Position = TabbedPosition.Tab, Icon = "map_outline", WrapInNavigationPage = true, NoHistory = false)]
public partial class TempPage: MvxContentPage<ViewModels.TempPageViewModel>
{
public TempPage()
{
InitializeComponent();
}
}
TabbedPage2;
[MvxTabbedPagePresentationAttribute(Position = TabbedPosition.Root, WrapInNavigationPage = true, NoHistory = false)]
public partial class TabbedPage2 : MvxTabbedPage<ViewModels.TabbedPage2ViewModel>
{
public TabbedPage2 ()
{
InitializeComponent();
}
}
my current situation is, that the tabbedpage2 shows like a modal page.
You could nest a TabView in Tabbed page. Install Xam.Plugin.TabView via NuGet. https://www.nuget.org/packages/Xam.Plugin.TabView
Create three tab pages in views folder.
Tabbed Page:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage
x:Class="TabbedPageDemo.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Views="clr-namespace:TabbedPageDemo.Views"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
BarBackgroundColor="Blue"
BarTextColor="White"
mc:Ignorable="d">
<Views:Tab1_Page Title="TAB1" />
<Views:Tab2_Page Title="TAB2" />
<Views:Tab3_Page Title="TAB3" />
</TabbedPage>
And then use TabView in you tab1 page.
Tab1_Page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="TabbedPageDemo.Views.Tab1_Page"
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:local="clr-namespace:Xam.Plugin.TabView;assembly=Xam.Plugin.TabView"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<ContentPage.Content>
<local:TabViewControl>
<local:TabViewControl.ItemSource>
<local:TabItem HeaderText="SUBTAB1">
<StackLayout VerticalOptions="Start" Padding="10">
<Button
BackgroundColor="White"
Text="List Item"
TextColor="Black"/>
</StackLayout>
</local:TabItem>
<local:TabItem HeaderText="SUBTAB2">
<Image Source="pink.jpg" />
</local:TabItem>
</local:TabViewControl.ItemSource>
</local:TabViewControl>
</ContentPage.Content>
</ContentPage>
Please note, if you want to make the tabs in tabbed page in the bottom. Add the code below in your MainPage.
On<Android>().SetToolbarPlacement(ToolbarPlacement.Bottom);
You could download the code sample from GitHub in TabbedPage_NestedTabView/TabbedPageDemo
https://github.com/WendyZang/Test.git

Xamarin Dependent Binding

I'm struggling to figure out something that I feel should be very basic and I hope someone can help me.
I'm trying to make a custom control visible depending on whether or not data has been provided to it.
For example:
I have a main page that uses a custom control as follows:
<?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:controls="clr-namespace:XamarinDependantProperty.Controls;assembly=XamarinDependantProperty"
x:Class="XamarinDependantProperty.Pages.MainPage">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0"/>
</OnPlatform>
</ContentPage.Padding>
<StackLayout>
<Label Text="Welcome to Xamarin Forms!" VerticalOptions="Center" HorizontalOptions="Center" />
<controls:CustomEntry TextValue="Test" VerticalOptions="Center" HorizontalOptions="Center"></controls:CustomEntry>
</StackLayout>
</ContentPage>
The custom control looks as follows:
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="XamarinDependantProperty.Controls.CustomEntry" IsVisible="{Binding TextIsVisible}">
<StackLayout>
<Label Text="{Binding TextIsVisible}" />
<Label Text="{Binding TextValue}" />
</StackLayout>
</ContentView>
And the code behind is as follows:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace XamarinDependantProperty.Controls
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomEntry : ContentView
{
public CustomEntry()
{
InitializeComponent();
BindingContext = this;
}
public string TextValue
{
get { return (string)GetValue(TextValueProperty); }
set { SetValue(TextValueProperty, value); }
}
public static BindableProperty TextValueProperty = BindableProperty.Create(nameof(TextValue), typeof(string), typeof(CustomEntry));
public bool TextIsVisible => !String.IsNullOrEmpty(TextValue);
}
}
If I set the CustomEntry's TextValue to "Test" the the output is:
Welcome to Xamarin Forms!
False
Test
If I put in an empty string, then there is no output at all, the application starts but nothing is displayed.
If I set the default value of the TextValueProperty to null then the output is:
Welcome to Xamarin Forms!
From the outputs it appears that when I set the TextValue, the TextIsVisible value is working in the first binding (IsVisible) even though the second binding (Text) outputs False, but why is it false?
If I don't provide a value and I don't tell it that null is an acceptable empty value then it screws up completely but it doesn't say anything to that regard. No error, not output, nothing. Is there a way to see that something went wrong? (testing on a iphone simulator)
And then if I take this concept out of this test situation and put it into a real situation. Then setting TextValue and outputting TextIsVisible is still false but it isn't displaying.
What am I doing wrong? What am I not understanding?
You need to raise the propertychanged event for TextIsVisible to notify the View that this property has changed.
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class CustomEntry : ContentView
{
public CustomEntry()
{
InitializeComponent();
BindingContext = this;
}
public string TextValue
{
get { return (string)GetValue(TextValueProperty); }
set{SetValue(TextValueProperty, value);OnPropertyChanged(nameof(TextIsVisible));}
}
public static BindableProperty TextValueProperty = BindableProperty.Create(nameof(TextValue), typeof(string), typeof(CustomEntry),propertyChanged:OnTextChanged);
private static void OnTextChanged(BindableObject bindable, object oldvalue, object newvalue)
{
var entry = bindable as CustomEntry;
entry?.OnPropertyChanged(nameof(TextIsVisible));
}
public bool TextIsVisible => !String.IsNullOrEmpty(TextValue);
}

Categories

Resources