Xamarin Forms / ICommand Bindable属性在自定义视图上不起作用

时间:2019-02-02 12:56:36

标签: c# xamarin mvvm xamarin.forms .net-core

我创建了一个名为HeaderTemplate的自定义视图。该控件具有图像。我想要实现的是单击Image并使用MVVM执行一些操作。

请在下面找到该控件的xml和cs。

HeaderTemplate.xml

appl_id

HeaderTemplate.xml.cs

emp_id

我的页面(HolidaysView)具有此自定义控件以及“图像点击/点击”命令。

<?xml version="1.0" encoding="UTF-8" ?>
<ContentView
    x:Class="PS.Views.Templates.HeaderTemplate"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:behaviors="clr-namespace:PS.Behaviors">
    <ContentView.Content>
        <StackLayout
            Padding="10"
            BackgroundColor="Transparent"
            Orientation="Horizontal">
            <Image
                x:Name="ImageSource_2"
                HeightRequest="50"
                HorizontalOptions="EndAndExpand"
                Source="{Binding ImageSource2}">
                <Image.GestureRecognizers>
                    <TapGestureRecognizer NumberOfTapsRequired="1" Tapped="ImageSource2_Tapped" />
                </Image.GestureRecognizers>
            </Image>
        </StackLayout>
    </ContentView.Content>
</ContentView>

此页面的绑定视图模型包含命令

public partial class HeaderTemplate : ContentView
{
    public HeaderTemplate()
    {
        InitializeComponent();
        BindingContext = this;
    }

    public static readonly BindableProperty ImageSource2Property =
        BindableProperty.Create(nameof(ImageSource2), typeof(string), typeof(HeaderTemplate));

    public string ImageSource2
    {
        get => (string)GetValue(ImageSource2Property);
        set => SetValue(ImageSource2Property, value);
    }

    public static readonly BindableProperty ImageSource2TapCommandProperty =
        BindableProperty.Create(nameof(ImageSource2TapCommand),
                                typeof(ICommand),
                                typeof(HeaderTemplate),
                                null);

    public ICommand ImageSource2TapCommand
    {
        get => (ICommand)GetValue(ImageSource2TapCommandProperty);
        set => SetValue(ImageSource2TapCommandProperty, value);
    }

    private void ImageSource2_Tapped(object sender, EventArgs e)
    {
        if (ImageSource2TapCommand == null) return;
        if (ImageSource2TapCommand.CanExecute(null))
        {
            ImageSource2TapCommand.Execute(null);
        }
    }
}

不起作用。我不知道我哪里错了。 我想念什么吗? 我对此进行了大量研究,但仍然找不到任何解决方案。

谢谢!

3 个答案:

答案 0 :(得分:3)

要使其正常工作,您只需要做几处更改。

首先,在ContenView类文件中,删除BindingContext = this;行。

然后,您需要向两个BindableProperty中添加PropertyChanged处理程序

public static readonly BindableProperty ImageSource2Property =
    BindableProperty.Create(nameof(ImageSource2), 
                            typeof(string), 
                            typeof(HeaderTemplate), 
                            defaultValue: default(string), 
                            propertyChanged: OnImageSourcePropertyChanged);

public static readonly BindableProperty ImageSource2TapCommandProperty =
    BindableProperty.Create(
                            propertyName: nameof(ImageSource2TapCommand),
                            returnType: typeof(ICommand),
                            declaringType: typeof(HeaderTemplate),
                            defaultValue: default(ICommand), 
                            propertyChanged: OnTapCommandPropertyChanged);

如果您看不到它们之间的区别,那么我在谈论以下内容:OnImageSourcePropertyChangedOnTapCommandPropertyChanged。我只是添加了propertyName,所以Create方法中的其他更改不是必需的,因此更加清楚。

您当然需要实现这两种方法:

static void OnTapCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    if(bindable is HeaderTemplate headerTemplate && newValue is ICommand command)
    {
        headerTemplate.ImageSource2TapCommand = command;
    }
}

static void OnImageSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
    if (bindable is HeaderTemplate headerTemplate && newValue is string imageSource)
    {
        headerTemplate.ImageSource_2.Source = ImageSource.FromFile(imageSource);
    }
}

通过这些更改,您应该可以点击Image并根据需要导航。

原因是什么?

由于您是在首次创建自定义控件时将“主页”上的值绑定到自定义控件,因此这些值是null,这就是为什么您需要收听值更改的原因,并且可以通过添加创建方法上的onPropertyChanged实现。

this帖子中也有很好的解释。

您的完整班级看起来应该类似于:

public partial class HeaderTemplate : ContentView
{
    public HeaderTemplate()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty ImageSource2Property =
        BindableProperty.Create(nameof(ImageSource2), 
                                typeof(string), 
                                typeof(HeaderTemplate), 
                                defaultValue: default(string), 
                                propertyChanged: OnImageSourcePropertyChanged);

    public string ImageSource2
    {
        get => (string)GetValue(ImageSource2Property);
        set => SetValue(ImageSource2Property, value);
    }

    public static readonly BindableProperty ImageSource2TapCommandProperty =
        BindableProperty.Create(
                                propertyName: nameof(ImageSource2TapCommand),
                                returnType: typeof(ICommand),
                                declaringType: typeof(HeaderTemplate),
                                defaultValue: default(ICommand), 
                                propertyChanged: OnTapCommandPropertyChanged);


    public ICommand ImageSource2TapCommand
    {
        get => (ICommand)GetValue(ImageSource2TapCommandProperty);
        set => SetValue(ImageSource2TapCommandProperty, value);
    }

    private void ImageSource2_Tapped(object sender, EventArgs e)
    {
        if (ImageSource2TapCommand == null) return;
        if (ImageSource2TapCommand.CanExecute(null))
        {
            ImageSource2TapCommand.Execute(null);
        }
    }

    static void OnTapCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if(bindable is HeaderTemplate headerTemplate && newValue is ICommand command)
        {
            headerTemplate.ImageSource2TapCommand = command;
        }
    }

    static void OnImageSourcePropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        if (bindable is HeaderTemplate headerTemplate && newValue is string imageSource)
        {
            headerTemplate.ImageSource_2.Source = ImageSource.FromFile(imageSource);
        }
    }
}

希望这会有所帮助。-

注意:尝试遵循其他答案之一中发布的示例,以使用Command属性更改TapEvent。

答案 1 :(得分:2)

TapGestureRecognizer class具有Command property {接受{1}}

我只是在类似于您的自定义控件中尝试过,并且工作正常。

所以,用于结合ICommandICommand,使用TapGestureRecognizer属性,而不是Command的事件。

Tap

引用Adding a tap gesture recognizer: Using ICommand

答案 2 :(得分:0)

Source的{​​{1}}中的BindingContext设置为ICommand

ContentPage

示例:

MyViewCommand="{{Binding Path=BindingContext.NavigateToCommand, Source={x:Reference MyContentPage}}}"