Свойство Xamarin Forms / ICommand Bindable не работает в пользовательском представлении - PullRequest
0 голосов
/ 02 февраля 2019

Я создал собственное представление под названием HeaderTemplate.Этот элемент управления имеет изображение.То, чего я пытаюсь добиться, это нажать на изображение и выполнить какое-то действие с помощью MVVM.

Ниже приведены xml и cs этого элемента управления.

HeaderTemplate.xml

<?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>

HeaderTemplate.xml.cs

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

Моя страница (HolidaysView) имеет этот пользовательский элемент управления вместе с командой "Нажать / Нажать на изображение".

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="PS.Views.HolidaysView"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:behaviors="clr-namespace:PS.Behaviors"
    xmlns:templates="clr-namespace:PS.Views.Templates"
    xmlns:viewModelBase="clr-namespace:PS.ViewModels.Base"
    viewModelBase:ViewModelLocator.AutoWireViewModel="true">
    <ContentPage.Content>
        <StackLayout>
            <templates:HeaderTemplate
                HeightRequest="60"
                ImageSource2="upload.png"
                ImageSource2TapCommand="{Binding NavigateToCommand}" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

и модель связанного вида этой страницы содержит команду

public class HolidaysViewModel : ViewModelBase
{
    public HolidaysViewModel()
    {
    }

    public ICommand NavigateToCommand => new Command(async () => await NavigateTo());

    private async Task NavigateTo()
    {

        await NavigationService.NavigateToAsync<HolidayRequestViewModel>();
    }
}

Не работает.Я не знаю, где я не прав.Я что-то пропустил?Я исследовал много вопросов по этому вопросу, но до сих пор не могу найти никакого решения до сих пор.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 03 февраля 2019

Чтобы это работало, вам нужно сделать всего пару изменений.

Сначала в файле класса ContenView удалите строку BindingContext = this;.

Затем вам нужно будет добавитьPropertyChanged обработчики для вашего BindableProperty

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

Если вы не можете заметить разницу, я говорю об этом: OnImageSourcePropertyChanged и OnTapCommandPropertyChanged.Другие изменения в методе Create не нужны. Я просто добавил propertyName, чтобы он был более понятным.

Конечно, вам нужно реализовать эти два метода:

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 в методы Create.

Здесь также есть очень хорошее объяснение. этот пост.

Ваш полный класс должен выглядеть примерно так:

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

Надеюсь, это поможет .-

Примечание: попробуйте выполнитьПример, размещенный в одном из ответов, для изменения вашего TapEvent с помощью свойства Command.

0 голосов
/ 02 февраля 2019

TapGestureRecognizer класс имеет свойство Command принимает ICommand

Я только что попробовал его в пользовательском элементе управления, похожем на ваш, и работает нормально.

Итак, для привязки ICommand к TapGestureRecognizer используйте свойство Command, а не событие Tap.

<Image
    x:Name="ImageSource_2"
    HeightRequest="50"
    HorizontalOptions="EndAndExpand"
    Source="{Binding ImageSource2}">
    <Image.GestureRecognizers>
        <TapGestureRecognizer 
            NumberOfTapsRequired="1" 
            Command="{Binding ImageSource2TapCommand}" />
    </Image.GestureRecognizers>
</Image>

Ссылка Добавлениераспознаватель жестов касания: Использование ICommand

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...