Xamarin Custom View Button Binding

点点圈 提交于 2019-12-23 20:44:46

问题


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?


回答1:


Solution-1

You can expose a couple of events in your custom control.

Steps:

  1. Create 2 events in your custom HeaderBar control.

    public event EventHandler RightButtonClickEvent;
    public event EventHandler LeftButtonClickEvent;
    
  2. 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);
    }
    
  3. 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
    }
}

Solution-2 (recommended if you are using MVVM)

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



回答2:


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



来源:https://stackoverflow.com/questions/43881019/xamarin-custom-view-button-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!