Xamarin自定义视图按钮绑定

3

我有一个自定义视图,在其中有2个按钮 - 一个在左边,一个在右边。

我想要一个可绑定的属性来指示每个按钮将调用哪个函数,但是我想要调用的函数在原始内容页面中,而不是自定义控件中。

我知道你不能将void作为可绑定属性,所以我不确定如何实现这一点。

这是我的自定义控件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>

以下是后端代码:

using System;
using System.Collections.Generic;
using System.Windows.Input;
using Xamarin.Forms;

namespace EngineerApp
{
    public partial class HeaderBar : RelativeLayout
    {

        public HeaderBar()
        {

            InitializeComponent();
        }

    }
}

最后,我像往常一样包含自定义的新内容。
<local:HeaderBar></local:HeaderBar>

我该如何将2个按钮的单击事件绑定到我的内容页面中的一个方法?
2个回答

5

解决方案-1

您可以在自定义控件中公开一些事件。

步骤:

  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" />
    

示例代码 - 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>

示例代码 - 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>

示例代码- 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
    }
}

解决方案-2(如果您正在使用MVVM,则建议使用)

将两个命令公开为可绑定属性(这些属性将绑定到内部按钮控件)。 然后,您可以将这些新命令绑定到ContentPageViewModel中的属性。

示例代码 - 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); }
    }
} 

示例代码 - 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>

示例代码 - 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绝对是最好的 - 我的MyContentPage的后端代码需要放入哪些代码才能执行这些命令? - Martin Crawley
不需要在MyContentPage的后端代码中添加任何代码。您只需要在其ViewModel中定义命令即可。每当您单击按钮时,这些命令将被执行。 - Sharada Gururaj
你能举个例子说明该方法在视图模型中应该如何出现吗? - Martin Crawley
添加了一个例子。 - Sharada Gururaj

0

我曾经遇到过类似的问题。 我创建了一个名为 CalculatorPage.xaml 的自定义控件和一个关联的 CalculatorViewModel。

对于自定义控件中的每个按钮:

    <Button Grid.Row="0" Grid.Column="1" Font="Large" Command="{Binding Calculator.KeyPressCommand}" CommandParameter="Eight" Text="8" />

在上面的例子中,“KeyPressCommand”存在于CalculatorViewModel中。但是“Calculator”不存在。
我创建了一个新的MainView(我的主视图)和相应的MainViewModel。
就像你所做的那样,我在我的XAML中包含了自定义控件,如下所示:
<common:CalculatorView></common:CalculatorView>

在 MainViewModel 中,我有一个属性。
public CalculatorViewModel Calculator { get; private set; } = new CalculatorViewModel();

也许有更好的解决方案,可以定义自定义控件的BindingContext,但我的解决方案也能正常工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接