Reputation: 487
I have a custom view with 2 button in it - One on the left and one on the right.
I want to have a bindable property to dictate which function will be called by each button, but the function I want to call is in the original content page, not the custom control.
I'm aware that you can't have a void as the bindable property so I'm not sure how to achieve this.
Here's my custom control xaml:
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EngineerApp.HeaderBar" HeightRequest="50">
<BoxView x:Name="banner" BackgroundColor="#edeff2"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=1 }">
</BoxView>
<Button
Text="Left"
x:Name="btnLeft"
Style="{StaticResource headerBarButton}"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Height,
Factor=1 }"
>
</Button>
<Button
Text="Right"
Style="{StaticResource headerBarButton}"
RelativeLayout.XConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5,
Constant=1
}"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Height,
Factor=1 }"
>
</Button>
</RelativeLayout>
And here's the backend code:
using System;
using System.Collections.Generic;
using System.Windows.Input;
using Xamarin.Forms;
namespace EngineerApp
{
public partial class HeaderBar : RelativeLayout
{
public HeaderBar()
{
InitializeComponent();
}
}
}
And finally I'm including the custom new as normal with
<local:HeaderBar></local:HeaderBar>
How could I bind each of the 2 buttons click events to a method in my Content Page?
Upvotes: 3
Views: 2240
Reputation: 13601
You can expose a couple of events in your custom control.
Steps:
Create 2 events in your custom HeaderBar
control.
public event EventHandler RightButtonClickEvent;
public event EventHandler LeftButtonClickEvent;
Assign Clicked
event-handlers in XAML, and invoke these events in them
void RightButton_Clicked(object sender, EventArgs e)
{
RightButtonClickEvent?.Invoke(sender, e);
}
void LeftButton_Clicked(object sender, EventArgs e)
{
LeftButtonClickEvent?.Invoke(sender, e);
}
Bind custom events with event handlers in your ContentPage
<local:HeaderBar
RightButtonClickEvent="OnRightButtonClick"
LeftButtonClickEvent="OnLeftButtonClick" />
Sample code - HeaderBar.xaml.cs
public partial class HeaderBar : RelativeLayout
{
public HeaderBar()
{
InitializeComponent();
}
public event EventHandler RightButtonClickEvent;
public event EventHandler LeftButtonClickEvent;
void RightButton_Clicked(object sender, EventArgs e)
{
RightButtonClickEvent?.Invoke(sender, e);
}
void LeftButton_Clicked(object sender, EventArgs e)
{
LeftButtonClickEvent?.Invoke(sender, e);
}
}
Updated sample code - HeaderBar.xaml
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="EngineerApp.HeaderBar" HeightRequest="50">
<BoxView x:Name="banner" BackgroundColor="#edeff2"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=1 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToParent,
Property=Height,
Factor=1 }">
</BoxView>
<Button
Text="Left"
x:Name="btnLeft"
Clicked="LeftButton_Clicked"
Style="{StaticResource headerBarButton}"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Height,
Factor=1 }"
>
</Button>
<Button
Text="Right"
x:Name="btnRight"
Clicked="RightButton_Clicked"
Style="{StaticResource headerBarButton}"
RelativeLayout.XConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5,
Constant=1
}"
RelativeLayout.WidthConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Width,
Factor=0.5 }"
RelativeLayout.HeightConstraint="{ ConstraintExpression
Type=RelativeToView,
ElementName=banner,
Property=Height,
Factor=1 }"
>
</Button>
</RelativeLayout>
Sample code - MyContentPage.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:EngineerApp"
x:Class="EngineerApp.MyContentPage">
<local:HeaderBar
RightButtonClickEvent="OnRightButtonClick"
LeftButtonClickEvent="OnLeftButtonClick" />
</ContentPage>
Sample code - MyContentPage.xaml.cs
public partial class MyContentPage : ContentPage
{
public MyContentPage()
{
InitializeComponent();
}
void OnRightButtonClick(object sender, EventArgs e)
{
//handle right-button click here
}
void OnLeftButtonClick(object sender, EventArgs e)
{
//handle left-button click here
}
}
Expose two commands as bindable properties (which in turn will be bound to inner button controls). You can then bind these new commands to properties in your ContentPage
's ViewModel
Sample code - Header.xaml.cs
public partial class HeaderBar : RelativeLayout
{
public HeaderBar()
{
InitializeComponent();
//create binding between parent control and child controls
btnLeft.SetBinding(Button.CommandProperty, new Binding(nameof(LeftButtonCommand), source: this));
btnRight.SetBinding(Button.CommandProperty, new Binding(nameof(RightButtonCommand), source: this));
}
public static readonly BindableProperty LeftButtonCommandProperty =
BindableProperty.Create(
"ILeftButtonCommand", typeof(ICommand), typeof(HeaderBar),
defaultValue: null);
public ICommand LeftButtonCommand
{
get { return (ICommand)GetValue(LeftButtonCommandProperty); }
set { SetValue(LeftButtonCommandProperty, value); }
}
public static readonly BindableProperty RightButtonCommandProperty =
BindableProperty.Create(
"RightButtonCommand", typeof(ICommand), typeof(HeaderBar),
defaultValue: null);
public ICommand RightButtonCommand
{
get { return (ICommand)GetValue(RightButtonCommandProperty); }
set { SetValue(RightButtonCommandProperty, value); }
}
}
Sample code - MyContentPage.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:EngineerApp"
x:Class="EngineerApp.MyContentPage">
<local:HeaderBar
LeftButtonCommand="{Binding LeftClickCommand}"
RightButtonCommand="{Binding RightClickCommand}" />
</ContentPage>
Sample code - MyContentPageViewModel.cs
public class MyContentPageViewModel : ViewModelBase //base implementation for INotifyPropertyChanged
{
private ICommand _leftClickCommand;
public ICommand LeftClickCommand
{
get
{
return _leftClickCommand ??
(_leftClickCommand = new Command((obj) =>
{
//handle left button click here.
}));
}
}
private ICommand _rightClickCommand;
public ICommand RightClickCommand
{
get
{
return _rightClickCommand ??
(_rightClickCommand = new Command((obj) =>
{
//handle right button click here.
}));
}
}
}
Upvotes: 5
Reputation: 1849
I had a similar issue. I created a CalculatorPage.xaml (my custom control) and a linked CalculatorViewModel.
For each button inside my custom control:
<Button Grid.Row="0" Grid.Column="1" Font="Large" Command="{Binding Calculator.KeyPressCommand}" CommandParameter="Eight" Text="8" />
In the above example, "KeyPressCommand" exists in the CalculatorViewModel. But "Calculator" does not exist.
I created a new MainView (my main View) and the corresponding MainViewModel.
As you did, I included the custom control in my XAML like this:
<common:CalculatorView></common:CalculatorView>
And in the MainViewModel, I have a property
public CalculatorViewModel Calculator { get; private set; } = new CalculatorViewModel();
There is maybe a better solution where you define the BindingContext of the custom control but my solution is working fine
Upvotes: 0