Hy
I would like to share my approach for a custom xamarin.forms control within a Xamarin PCL Project with MVVM-Light. What do you think about it?
Custom Control -> PersonPanel.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="xxx.PersonPanel">
<StackLayout Orientation="Vertical">
<Label x:Name="titleLabel" Text="{Binding TitleLabel}"/>
<Entry x:Name="filterText" Placeholder="{Binding FilterPlaceholderText}" Text="{Binding Filter.Lookup}" TextChanged="OnFilterTextChanged"/>
<Label x:Name="resultText" Text="{Binding ResultText}" IsVisible="{Binding ResultTextVisible}"/>
</StackLayout>
</ContentView>
Code-Behind -> PersonPanel.xaml.cs:
public partial class PersonPanel : ContentView
{
public PersonPanel()
{
InitializeComponent();
//Init ViewModel
BindingContext = ServiceLocator.Current.GetInstance<PersonPanelViewModel>();
}
private PersonPanelViewModel PersonPanelViewModel
{
get
{
return (PersonPanelViewModel)BindingContext;
}
}
public string TitleLabel
{
get
{
return PersonPanelViewModel.TitleLabel;
}
set
{
PersonPanelViewModel.TitleLabel = value;
}
}
public string FilterPlaceholderText
{
get
{
return PersonPanelViewModel.FilterPlaceholderText;
}
set
{
PersonPanelViewModel.FilterPlaceholderText = value;
}
}
private void OnFilterTextChanged(object sender, EventArgs e)
{
PersonPanelViewModel.SearchCommand.Execute(null);
}
}
ViewModel -> PersonPanelViewModel:
public class PersonPanelViewModel : ViewModelBase
{
private IPersonService _personService;
private decimal _personId = 0;
private string _titleLabel = string.Empty;
private string _filterPlaceholderText = string.Empty;
private string _resultText = string.Empty;
private bool _resultTextVisible = true;
public PersonPanelViewModel(IPersonService personService)
{
_personService = personService;
// Init Filter
Filter = new PersonFilter();
// Init Commands
SearchCommand = new RelayCommand(Search);
}
public ICommand SearchCommand { get; set; }
public PersonFilter Filter
{
get;
private set;
}
public string ResultText
{
get
{
return _resultText;
}
set
{
Set(() => ResultText, ref _resultText, value);
}
}
public bool ResultTextVisible
{
get
{
return _resultTextVisible;
}
set
{
Set(() => ResultTextVisible, ref _resultTextVisible, value);
}
}
public string FilterPlaceholderText
{
get
{
return _filterPlaceholderText;
}
set
{
Set(() => FilterPlaceholderText, ref _filterPlaceholderText, value);
}
}
public string TitleLabel
{
get
{
return _titleLabel;
}
set
{
Set(() => TitleLabel, ref _titleLabel, value);
}
}
public decimal PersonId
{
get
{
return _PersonId;
}
set
{
Set(() => PersonId, ref _PersonId, value);
}
}
private async void Search()
{
//Reset
ResultText = string.Empty;
ResultTextVisible = false;
PersonId = 0;
if (Filter.PersonLookup != null && Filter.PersonLookup.Length >= 3)
{
//Call to Person Service
List<PersonResult> Person = await _personService.FindpersonByFilter(Filter);
if (Person.Count == 1)
{
PersonId = Person[0].PersonId;
ResultText = Person[0].PersonName;
ResultTextVisible = true;
}
}
}
}
Using of Control in another View:
<?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:components="clr-namespace:xxx.ViewElements.Components"
x:Class="xxx.MainPage">
<StackLayout Orientation="Vertical">
<components:PersonPanel x:Name="personPanel" TitleLabel="Person" FilterPlaceholderText="Type your search criteria here..."/>
</StackLayout>
</ContentPage>
I'm using Autofac as the IOC Container.
What do you think about it? I am using MVVM the right way (it's very new to me)?
Is there a better way to deal with calling the Command from the Event (TextChanged) on the view?
What's about the properties in the Code-Behind (which do a routing to the ViewModel)?
Edit: I'll try to describe, what I want to achieve:
- Creating our own control (reusable in different views, cross-platform) -> PersonPanel.xaml
- Written in XAML in our PCL Project with pure Xamarin.Forms controls in it
- The control should have it's own commands (Search) and properties
- One of the commands is using a Service
- The control should get the Service (as an Interface) injected through IOC
- The Service itself is also implemented in the PCL Project and makes a REST Call to a Webservice
- The Result of the Call is than set to a property of the control -> ResultText Property
- The Result is visible to the User
-> With the above implementation, all of that works, but I'm not sure if this is the right way...
Thanks for your help!
Kind regards, Peter
The approach for mapping the event to the command is exactly as I would perform.
The rest is a little bit confusing. The general pattern is to create bindable properties in your control that are exposed to the view model when instantiated within the host view. A very basic sample structure is below:
You would then create the native renderers to handle the drawing on each platform.
By following this approach you keep the code separated and concise. It does seem a slightly long winded way of getting things done but it is highly scalable and configurable.