XAML: How set properties of an user control directly - c#

I created a new class library in MAUI in which a user control is defined. It is a simple user control composed by a checkbox and a label. It should be noted that in MAUI checkbox have only the box and not the associated label. The following is the XAML code
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiLib_CustomControls.CheckBoxExt">
<HorizontalStackLayout>
<CheckBox x:Name="myCheckBox"/>
<Label
x:Name="myLabel"
VerticalOptions="Center"
HorizontalOptions="Center" />
</HorizontalStackLayout>
</ContentView>
and this is the related c# code:
namespace MauiLib_CustomControls;
public partial class CheckBoxExt : ContentView
{
public CheckBoxExt()
{
InitializeComponent();
}
public CheckBox CheckBox => myCheckBox;
public Label Label => myLabel;
}
I compiled this project and generated the dll which I imported in my main project. Once added in the references, I add the namespace in the xaml file
xmlns:MauiLib_CustomControls = "clr-namespace:MauiLib_CustomControls;assembly=MauiLib_CustomControls"
and use the user control in a grid
<MauiLib_CustomControls:CheckBoxExt x:Name="myCheckBoxExt"/>
Currently, if I want to set some properties or events I have to write them in the C# file because they are not available in the xaml. For example
this.myCheckBoxExt.Label.Text = "FROM";
this.myCheckBoxExt.CheckBox.CheckedChanged += myCheckBoxExt_CheckedChanged;
So, how can I set the properties of this user control directly from the xaml code as for the native controls? Consider that I would like to have available all properties and events associated to the two children controls (the checkbox and the label).

One way of doing this would be as follows using BindableProperty. You can modify your custom control like this:
Code behind
public partial class CheckBoxExt : ContentView
{
public bool Checked
{
get => (bool)GetValue(CheckedProperty);
set => SetValue(CheckedProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public event EventHandler<CheckedChangedEventArgs> CheckedChanged;
public static readonly BindableProperty CheckedProperty = BindableProperty.Create(nameof(Checked), typeof(bool), typeof(CheckBoxExt), propertyChanged: OnCheckedPropertyChanged);
private static void OnCheckedPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
((CheckBoxExt)bindable).CheckedChanged?.Invoke((CheckBoxExt)bindable, new CheckedChangedEventArgs((bool)newValue));
}
public static readonly BindableProperty TextProperty = BindableProperty.Create(nameof(Text), typeof(string), typeof(CheckBoxExt));
public CustomCheckBox()
{
InitializeComponent();
}
}
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customControls="clr-namespace:MauiLib_CustomControls"
x:Class="MauiLib_CustomControls.CheckBoxExt">
<HorizontalStackLayout>
<CheckBox
IsChecked="{Binding Checked, Source={RelativeSource AncestorType={x:Type customControls:CustomCheckBox}}}"/>
<Label
Text="{Binding Text, Source={RelativeSource AncestorType={x:Type customControls:CustomCheckBox}}}"
VerticalOptions="Center"
HorizontalOptions="Center" />
</HorizontalStackLayout>
</ContentView>
Usage
In the using XAML code you would simply add your control:
<MauiLib_CustomControls:CheckBoxExt
Text="My Text"
CheckedChanged="OnCheckedChanged"/>
and then just add the event handler:
private async void OnCheckedChanged(object sender, CheckedChangedEventArgs e)
{
await DisplayAlert("Checkbox changed", $"new value: {e.Value}", "OK");
}
Note: Depending on the type of control you are developing, it may be necessary to register a MAUI handler for your control. If you need assistance with that, let me know.

Related

How to pass object as bindable property to Xamarin.Forms custom control

As the title suggests, I'm trying to pass in a Person object to a custom control instead of passing in each property separately.
So this:
<controls:PersonControl
Person="{Binding Person}"
ControlTemplate="{StaticResource PersonControlTemplate}">
</controls:PersonControl>
instead of this (which works, based on this implementation)
<controls:PersonControl
Name="{Binding Person.Name}"
Age="{Binding Person.Age}"
ControlTemplate="{StaticResource PersonControlTemplate}">
</controls:PersonControl>
I've tried changing the bindable property signature on the PersonControl code behind but it's not working. I actually just get a blank screen.
So:
1 - Is this even possible (i know it's called a bindable property but does it take objects as well?
and
2 - If not what is the recommended approach?
The reason I want to do this is the person object may grow over time and I would rather just update the custom control instead of the consuming page AND it's view model.
Update:
Here's the PersonControl Code:
public partial class PersonControl : ContentView
{
public static readonly BindableProperty PersonProperty = BindableProperty.Create(
nameof(Person),
typeof(Person),
typeof(PersonControl),
string.Empty);
public string Name
{
get { return this.Person.Name; }
}
public Person Person
{
get { return (Person)GetValue(PersonProperty); }
set { SetValue(PersonProperty, value); }
}
public PersonControl()
{
InitializeComponent();
}
}
And here's the PersonControl xaml:
<ContentView.Content>
<StackLayout>
<Label Text="{TemplateBinding Person.Name, Mode=OneWay}"/>
</StackLayout>
</ContentView.Content>
and lastly the consuming page:
<ContentPage.Resources>
<ControlTemplate x:Key="PersonControlTemplate">
<controls:PersonControl></controls:PersonControl>
</ControlTemplate>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Spacing="10" x:Name="layout">
<controls:PersonControl
Person="{Binding Person}"
ControlTemplate="{StaticResource PersonControlTemplate}"></controls:PersonControl>
</StackLayout>
</ContentPage.Content>
The person object is a property on the page's viewmodel as per mvvm pattern.
Thanks in advance for your help.
Update: Ive followed this tutorial and tried to replace the bindable string type with an object but still no joy
Yes you can, I used a Entry, type some text, then transfer text to the Label in ContentView. Here is running GIF.
First of all I add a property called PersonName like following code
public partial class PersonControl : ContentView
{
public PersonControl()
{
InitializeComponent();
}
public static BindableProperty PersonNameProperty = BindableProperty.Create(
propertyName: "PersonName",
returnType: typeof(string),
declaringType: typeof(PersonControl),
defaultValue: "",
defaultBindingMode: BindingMode.OneWay);
public string PersonName
{
get { return (string)GetValue(PersonNameProperty); }
set { SetValue(PersonNameProperty, value); }
}
}
Then, here is code about layout of ContentView. Please do not forget to add x:Name="ParentControl" in ContentView tab.
<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:Name="ParentControl"
x:Class="CustomDemoControl.PersonControl">
<ContentView.Content>
<StackLayout>
<Label Text="{Binding Source={x:Reference ParentControl}, Path=PersonName}"/>
</StackLayout>
</ContentView.Content>
</ContentView>
Then I use it in the other page.
<StackLayout>
<Entry x:Name="myEntry" Text="1"/>
<local:PersonControl
BindingContext="{x:Reference Name=myEntry}"
PersonName="{Binding Path=Text, StringFormat='Welcome Mr {0}'}"
></local:PersonControl>
</StackLayout>
So, it turns out the answer was to pass a default object in the bindable property signature (in the custom control's cs) like so:
public static readonly BindableProperty PersonProperty =
BindableProperty.Create(
nameof(Person),
typeof(Person),
typeof(PersonProperty ),
default(Person)); //this was the fix
Now, I missed this because I wasn't aware of errors in the control. It just wasn't showing up in the output window. Can anyone point me in the direction of how to comprehensively debug xamarin.forms apps properly? Or at least catch these kind of errors in future?
Thank you

How to bind an ItemSelected Event from a Re-Usable Listview to a ViewModal Command?

I have a UserList that I want to Reuse on multiple pages in my Xamarin Application, but on each of the different pages a ItemSelected event should do something different.
I know about Bindable properties. I use them to bind a list from my Viewmodel to the Reusable Component. But I don't know how to do this with events
Let me show you some code!
This is the XAML of my Reusable ListView. It contains a list of Users
<?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="KineAppForms.Views.ReusableViews.UserTable"
x:Name="this">
<ContentView.Content>
<ListView ItemsSource="{Binding Source={x:Reference this},Path=Users, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name.GivenName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentView.Content>
</ContentView>
The .cs file where I do the binding looks as follows
namespace KineAppForms.Views.ReusableViews
{
public partial class UserTable : ContentView
{
public ObservableCollection<Patient> Users
{
get
{
return (ObservableCollection<Patient>)GetValue(UsersProperty);
}
set
{
SetValue(UsersProperty, value);
}
}
public static readonly BindableProperty UsersProperty =
BindableProperty.Create("Users", typeof(ObservableCollection<Patient>), typeof(UserTable), null,
BindingMode.Default, null, OnItemsSourceChanged);
static void OnItemsSourceChanged(BindableObject bindable, object oldvalue, object newvalue)
{
System.Diagnostics.Debug.WriteLine("source changed");
}
public UserTable()
{
InitializeComponent();
}
}
}
Now the way how I Use my Reusable Component
<views:UserTable Users="{Binding PatientList ,Mode=TwoWay}" />
The Binding PatientList comes from a ViewModel.
No to conclude the question:
How Can I Bind an ItemSelected Event To a Command in a ViewModel.
Say I have 2 pages. 1 page with Patients and 1 page with Doctors. They both use the Same table but the Patients Table should go to a Detailed page of Patients ( Link it to goToPatientDetailCommand in PatientListViewModel) and the doctors table should go to a Detailed Page of a Doctor ( Link it to goToPatientDetailCommand in DoctorListViewModel)
It should be something like this
<views:UserTable Users="{Binding PatientList ,Mode=TwoWay}" OnItemSelected="{Binding GoToPatientDetailed, Mode=TwoWay }/>
or
<views:UserTable Users="{Binding DoctorList ,Mode=TwoWay}" OnItemSelected="{Binding GoToDoctorDetailed, Mode=TwoWay }/>
Thank you!
There are several approaches in here.
You can add ItemSelectedCommand to UserTable class
public partial class UserTable : ContentView
{
public ObservableCollection<Patient> Users
{
get
{
return (ObservableCollection<Patient>)GetValue(UsersProperty);
}
set
{
SetValue(UsersProperty, value);
}
}
public static readonly BindableProperty UsersProperty =
BindableProperty.Create("Users", typeof(ObservableCollection<Patient>), typeof(UserTable), null,
BindingMode.Default, null, OnItemsSourceChanged);
public static BindableProperty ItemSelectedCommandProperty = BindableProperty.Create(
propertyName: nameof(ItemSelectedCommand),
returnType: typeof(ICommand),
declaringType: typeof(UserTable),
defaultValue: null);
public ICommand ItemSelectedCommand
{
get { return (ICommand)GetValue(ItemSelectedCommandProperty); }
set { SetValue(ItemSelectedCommandProperty, value); }
}
static void OnItemsSourceChanged(BindableObject bindable, object oldvalue, object newvalue)
{
System.Diagnostics.Debug.WriteLine("source changed");
}
public UserTable()
{
InitializeComponent();
}
}
Then in Xaml you can either use EventToCommandBehaviour by adding
<ListView.Behaviors>
<behaviors:EventToCommandBehavior EventName="ItemSelected" Command="{Binding Source={x:Reference this},Path=ItemSelectedCommand}"/>
</ListView.Behaviors>
or you can create your own CustomListView (that inherites from ListView) that has ItemSelected in it like in this example
Second approach is that instead of creating a control with ListView you can create a data template. Here you have documentation on how to do it.
If you ask for my opinion, I would say go with second approach - later you can reuse that View in other places.

Xamarin Forms User Control Binding inside ListView

Scenario - There is a ListView binded to a ObservableCollection of string. Listview has one label and one UserControl (containing nothing but a label). Both are binded to the same collection.
Also, there is a button which generate some random data for the collection.
Problem is when I run the app and click on Generate Data button the label gets updated but not the UserControl.
Below is the sample code.
MainPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:TestSample"
xmlns:controls="clr-namespace:TestSample.Controls"
x:Class="TestSample.MainPage">
<StackLayout>
<Button Text="Generate Data" Clicked="Button_Clicked"/>
<ListView Grid.Row="1" HorizontalOptions="Center" ItemsSource="{Binding Collection}" SeparatorVisibility="None">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
<Label Text="{Binding}"/>
<Label Text=" - "/>
<controls:MagicBox Text="{Binding}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
public ObservableCollection<string> Collection { get; set; }
public MainPage()
{
InitializeComponent();
Collection = new ObservableCollection<string>
{
"XX",
"XX",
"XX"
};
this.BindingContext = this;
}
public void Button_Clicked(object sender, EventArgs e)
{
var rand = new Random();
for (int i = 0; i < 3; i++)
{
Collection[i] = rand.Next(10, 100).ToString();
}
}
}
UserControl
<ContentView.Content>
<Grid>
<Label Text="{Binding Text}" />
</Grid>
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(MagicBox), "XX");
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public MagicBox ()
{
InitializeComponent ();
this.BindingContext = this;
}
}
I also tried with ObservableCollection of a POCO class instead of string after implementing INotifyPropertyChanged, didn't worked.
If I bind the MagicBox Text to a string directly it works but not if I bind it to some property.
doing
this.BindingContext = this;
in MagicBox.xaml.cs forces the BindingContext to the current object. It also means that the BindingContext from the parent is no longer inherited.
in order to make it work, change your code behind to
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(string), typeof(MagicBox), default(string));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public MagicBox ()
{
InitializeComponent ();
}
}
and your xaml to
<?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="TestSample.Controls.MagicBox"
x:Name="box">
<ContentView.Content>
<Grid>
<Label Text="{Binding Text, Source={x:Reference box}}" />
</Grid>
</ContentView.Content>
</ContentView>
I tested it. it works.
I think the problem is the line "this.BindingContext = this;" in your custom control.
You should Bind like this:
Text="{Binding Path=BindingContext, Source={x:Reference ListViewName}}"
Make sure add x:Name to your Listview. No tested, but hope it help you.
First, update your custom control:
Change your "Text" dependency property definition => Set the binding mode to "OneWay" and add propertyChanged event handler like this:
public partial class MagicBox : ContentView
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text", typeof(TextVM), typeof(MagicBox), "XX", BindingMode.OneWay, null, new BindableProperty.BindingPropertyChangedDelegate(TextPropertyChanged));
public TextVM Text
{
get { return (TextVM)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
Then add the 'Text' propertyChanged method into your custom control like this:
private static void TextPropertyChanged(BindableObject sender, object oldValue, object newValue )
{
Label updatedLabel = sender as Label;
if(updatedLabel == null) return;
updatedLabel.Text = (newValue as TextVM)?.MyText;
}
Make an Observable object that embed the text property, in order to throw the 'PropertyChanged' event:
public class TextVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _myText;
public string MyText
{
get => _myText;
set
{
_myText = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyText"));
}
}
}
Then in your XAML, update the text binding:
<controls:MagicBox Text="{Binding MyText}"/>
Don't forget to update your collection type and the random number generation process...
It should be good !

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);
}

Xamarin - Binding Command to property of object inside User Control

I have began learning XAML a few days ago and I have a trouble with wrapping my head around this problem.
In Xamarin Forms, I want to create a user control which will contain a label and a button and be able to bind a command to usercontrol in XAML from another page which uses my user control.
I am currently getting exception:
Xamarin.Forms.Xaml.XamlParseException: 'Position 8:24. Cannot assign property "Test1": Property does not exists, or is not assignable, or mismatching type between value and property'
Here is a dumbed down version of it I am currently trying to get to work.
My Custom Control 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="App3.Controls.Control1">
<StackLayout>
<Button Command="{Binding Test1}" Text="Test"/>
</StackLayout>
</ContentView>
Control using my Custom Control 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:App3"
xmlns:controls="clr-namespace:App3.Controls"
x:Class="App3.MainPage">
<controls:Control1 Test1="{Binding Test2}" />
</ContentPage>
My Custom Control Codebehind
using System.Windows.Input;
using Xamarin.Forms;
namespace App3.Controls
{
public partial class Control1 : ContentView
{
private static readonly BindableProperty controlProperty = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);
public Control1()
{
InitializeComponent();
}
public ICommand Test1
{
get
{
return (ICommand)GetValue(controlProperty);
}
set
{
SetValue(controlProperty, value);
}
}
}
}
View Model of page using Custom Control
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;
namespace App3
{
public class MainPageViewModel : INotifyPropertyChanged
{
public MainPageViewModel()
{
this.Test2 = new Command(test);
}
public void test()
{
Application.Current.MainPage.DisplayAlert("it works", "it works", "it works");
}
public ICommand Test2
{
get; set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
There are a few naming convention to follow if you want your BindableProperty detected, and bindable, from Xaml:
When you create the identifier field, name this field by the name of the property as you registered it, plus the suffix Property. This field is your identifier for the dependency property, and it will be used later as an input for the SetValue and GetValue calls you will make in the wrappers, by any other code access to the property by your own code, by any external code access you allow, by the property system, and potentially by XAML processors.
and
Define a DependencyProperty identifier as a public static readonly field on the owner type.
(this is the documentation for DependencyProperty, but it applies to BindableProperties quite well in this case. See https://msdn.microsoft.com/en-us/library/ms753358(v=vs.110).aspx)
You can fix your code by replacing
private static readonly BindableProperty controlProperty = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);
public ICommand Test1
{
get { return (ICommand)GetValue(controlProperty); }
set { SetValue(controlProperty, value); }
}
by
public static readonly BindableProperty Test1Property = BindableProperty.Create("Test1", typeof(ICommand), typeof(Control1), null);
public ICommand Test1
{
get { return (ICommand)GetValue(Test1Property); }
set { SetValue(Test1Property, value); }
}
That should prevent the property detection issue.
to get your full stuff working, the Button command binding should have it's source set to the control itself, and you should set your MainPageViewModel as BindingContext for MainPage.
Thank you very much both of you, i have talked offline with Krzysztof, and after all those changes, one thing is left to make it working.
Now when you click a button in custom control (Control1) binded command will not be called, the command saved in the binding is null, but even if you notify the view to get fresh command from bindablecontext it will not work because it will not found the proper property, because we dont have specified bindablecontext so the framework will search our property somewhere else (in parent object). Dont try to set bindablecontext of custom control to this, it will brake the binding ability to this control (you will not be able to bind properly to those dependency properties).
In this situation the easiest way to solve it is to set proper binding source in xaml.
Set the name of your content view and specify the binding source in binding all bindings (Here is only one).
<?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="App3.Controls.Control1"
x:Name="MySpecifiedName">
<StackLayout>
<Button Command="{Binding Test1, Source={x:Reference MySpecifiedName}}" Text="Test"/>
</StackLayout>
</ContentView>
I have wasted few hours on digging how to fix it so, i am writing here to save others developers which are facing those problems, time.

Categories

Resources