My objective is to create a user control containing a basic Entry control that will update a specified property in my view model.
I have been unable to bind the user control to the property name in my view model unless the view model property name is exactly the same as the property in the user control. This defeats the purpose of a reusable two way reusable control.
I suspect I'm just missing a key concept.
My expectation is that I should be able to add one or more of my UserEditors to a page and bind it to various string properties in the view model, and the property the UserEditor gets/sets should be the property I specify in the {Binding } for the UserEditor.
I have created a simple PCL project to test with. I would be very grateful if someone can get me on the correct track with this. I have exhausted all other resources to come to grips with this. I can provide a zip file of the VS2015 solution, however its pretty big (65MB) with all the XAM stuff.
Here are all the pieces:
UserEditor.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="UserControl.UserEditor">
<ContentView.Content>
<StackLayout Orientation="Vertical" Margin="0,0,0,10" >
<Entry x:Name="TextEntry"
Text="{Binding Text}"
HorizontalOptions="Fill"/>
</StackLayout>
</ContentView.Content>
</ContentView>
UserEditor.xaml.cs
using System;
using System.Linq;
using Xamarin.Forms;
namespace UserControl
{
public partial class UserEditor : ContentView
{
//Bindable property for Text
public static readonly BindableProperty TextProperty =
BindableProperty.Create("Text",
typeof(string),
typeof(UserEditor),
string.Empty,
BindingMode.TwoWay,
null,
OnTextPropertyChanged,
null,
null,
null);
public static void OnTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = bindable as UserEditor;
if (control == null) return;
control.TextEntry.Text = newValue.ToString();
}
public UserEditor()
{
InitializeComponent();
}
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
}
}
MainPageViewModel.cs
using System;
using System.ComponentModel;
using System.Linq;
namespace UserControl
{
class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainPageViewModel()
{
}
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
string textProperyInVM = "Initial value";
public string TextPropertyInVm
{
get
{
return textProperyInVM;
}
set
{
textProperyInVM = value;
OnPropertyChanged("TextPropertyInVm");
}
}
}
}
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:local="clr-namespace:UserControl"
x:Class="UserControl.MainPage">
<!--//Page binding context is set to the view model in the page contructor.-->
<StackLayout Orientation="Vertical">
<Label Text="Binding viewmodel properties to user control" />
<!--//Label is bound to ViewModel field named TextPropertyInVm-->
<Label Text="{Binding TextPropertyInVm}" ></Label>
<!--//UserEditor custom control is bound to ViewModel field named TextPropertyInVm
//User editor will not see the view model property.
//User editor ALWAYS wants to work with a property named "Text"
//User editor neither reads nor updates the view model property specified in the Binding -->
<!--
Text changes in the user control cause this error:
[0:] Binding: 'Text' property not found on 'UserControl.MainPageViewModel', target property: 'Xamarin.Forms.Entry.Text'
-->
<local:UserEditor Text="{Binding TextPropertyInVM, Mode=TwoWay}"></local:UserEditor>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace UserControl
{
public partial class MainPage : ContentPage
{
private MainPageViewModel vm;
public MainPage()
{
InitializeComponent();
//instantiate and bind this page to a new view model.
vm = new MainPageViewModel();
this.BindingContext = vm;
}
}
}