Xamarin.forms 如何从Xamarin表单中的DataTemplate条目TextChanged事件调用命令?

Xamarin.forms 如何从Xamarin表单中的DataTemplate条目TextChanged事件调用命令?,xamarin.forms,mvvm,datatemplate,relativesource,eventtocommand,Xamarin.forms,Mvvm,Datatemplate,Relativesource,Eventtocommand,正在CollectionView中使用数据模板。。。 我可以通过如下按钮调用ViewModel的命令: <Button Text="Test" Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.TestCommand}"/> <Frame.Ge

正在CollectionView中使用数据模板。。。 我可以通过如下按钮调用ViewModel的命令:

<Button Text="Test"
    Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}},
                      Path=BindingContext.TestCommand}"/>
<Frame.GestureRecognizers>
    <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.TestCommand}"/>        
</Frame.GestureRecognizers>

或者通过这样的手势:

<Button Text="Test"
    Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}},
                      Path=BindingContext.TestCommand}"/>
<Frame.GestureRecognizers>
    <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.TestCommand}"/>        
</Frame.GestureRecognizers>

那么,为什么我不能像这样从条目的TextChanged事件调用该命令呢

<Entry x:Name="PortionEntry"
    Text ="{Binding QtyTest, Mode=TwoWay}">
    <Entry.Behaviors>
        <behavors:EventToCommandBehavior
            EventName="TextChanged"
            Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}},
                              Path=BindingContext.TestCommand}"/>
    </Entry.Behaviors>  

EventToCommandBehavior的代码在数据模板中未使用时有效

以下是一个说明该问题的项目:

我从这些优秀的人那里得到了按钮代码:


谢谢

我测试您的代码,如果我不使用相对源代码绑定,它可以正常工作

<ContentPage
x:Class="demo3.simplecontrol2.Page2"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customcontrol="clr-namespace:demo3.customcontrol"
xmlns:local="clr-namespace:demo3.simplecontrol2"
x:Name="root">
<ContentPage.Resources>
    <DataTemplate x:Key="datatemplate1">
        <StackLayout>
            <Entry Text="{Binding str, Mode=TwoWay}">
                <Entry.Behaviors>
                    <local:EventToCommandBehavior Command="{Binding Source={x:Reference root}, Path=BindingContext.command1}" EventName="TextChanged" />
                </Entry.Behaviors>
            </Entry>
        </StackLayout>

    </DataTemplate>
</ContentPage.Resources>
<ContentPage.Content>
    <StackLayout>
       

        <Entry Text="123">
            <Entry.Behaviors>
                <local:EventToCommandBehavior Command="{Binding command1}" EventName="TextChanged" />
            </Entry.Behaviors>
        </Entry>

        <CollectionView ItemTemplate="{StaticResource datatemplate1}" ItemsSource="{Binding entries}">
           
        </CollectionView>


      
    </StackLayout>
</ContentPage.Content>

公共部分类第2页:内容页
{
公共ObservableCollection条目{get;set;}
公共命令command1{get;set;}
公共页2()
{
初始化组件();
条目=新的ObservableCollection()
{
新建testentry(){str=“test 1”},
新建testentry(){str=“test 2”},
新建testentry(){str=“test 3”},
新建testentry(){str=“test 4”},
新建testentry(){str=“test 5”},
新建testentry(){str=“test 6”}
};
command1=新命令(testcommand);
this.BindingContext=this;
}
私有void testcommand()
{
WriteLine(“这是测试!”);
}
}

我测试您的代码,如果我不使用相对源代码绑定,它可以正常工作

<ContentPage
x:Class="demo3.simplecontrol2.Page2"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customcontrol="clr-namespace:demo3.customcontrol"
xmlns:local="clr-namespace:demo3.simplecontrol2"
x:Name="root">
<ContentPage.Resources>
    <DataTemplate x:Key="datatemplate1">
        <StackLayout>
            <Entry Text="{Binding str, Mode=TwoWay}">
                <Entry.Behaviors>
                    <local:EventToCommandBehavior Command="{Binding Source={x:Reference root}, Path=BindingContext.command1}" EventName="TextChanged" />
                </Entry.Behaviors>
            </Entry>
        </StackLayout>

    </DataTemplate>
</ContentPage.Resources>
<ContentPage.Content>
    <StackLayout>
       

        <Entry Text="123">
            <Entry.Behaviors>
                <local:EventToCommandBehavior Command="{Binding command1}" EventName="TextChanged" />
            </Entry.Behaviors>
        </Entry>

        <CollectionView ItemTemplate="{StaticResource datatemplate1}" ItemsSource="{Binding entries}">
           
        </CollectionView>


      
    </StackLayout>
</ContentPage.Content>

公共部分类第2页:内容页
{
公共ObservableCollection条目{get;set;}
公共命令command1{get;set;}
公共页2()
{
初始化组件();
条目=新的ObservableCollection()
{
新建testentry(){str=“test 1”},
新建testentry(){str=“test 2”},
新建testentry(){str=“test 3”},
新建testentry(){str=“test 4”},
新建testentry(){str=“test 5”},
新建testentry(){str=“test 6”}
};
command1=新命令(testcommand);
this.BindingContext=this;
}
私有void testcommand()
{
WriteLine(“这是测试!”);
}
}

我查看了您的示例代码,您似乎正在使用来实现您的
事件到命令行为
。这也是以大致相同的方式在中实现的注意这些实现继承自
Xamarin.Forms.Behavior

我还尝试在分配给
ItemsView
DataTemplate
中执行这些示例的相对源绑定,但是当我运行示例(与上面的示例相同)时,我会在以下位置收到
invalidoOperationException

Xamarin.Forms.Binding.ApplyRelativeSourceBinding (Xamarin.Forms.BindableObject targetObject, Xamarin.Forms.BindableProperty targetProperty)[0x0006c]位于 C:\Advanced Dev\Xamarin.Forms\Xamarin.Forms.Core\Binding.cs:158

转到,您可以看到抛出是由于在
binding.ApplyRelativeSourceBinding()
中应用绑定时,绑定
targetObject
未继承自
Xamarin.Forms.Element
。由于
EventToCommandBehavior
继承自
Xamarin.Forms.Behavior
这就是结果

他们没有特别提到绑定目标需求,他们显然关心绑定源。但它们确实提到这些绑定搜索可视树或与元素相关:

FindAncestor
指示绑定元素的可视树中的祖先

Self
表示正在设置绑定的元素

由于
行为
不是
元素
,也不是可视化树的一部分(它存储在
VisualElement.Behaviors
属性中),因此绑定在运行时无法直接访问这两个元素以执行其“搜索”,因此绑定永远无法满足要求

我通过扩展条目并在需要的地方添加命令来解决这个问题。它不是最可重用的解决方案,因为我必须在其他元素(如Switch)上使用它,但它可以工作

public class Entry : Xamarin.Forms.Entry
{
    public Entry()
    {
        this.TextChanged += this.OnTextChanged;
    }

    public static readonly BindableProperty TextChangedCommandProperty =
        BindableProperty.Create( nameof( Entry.TextChangedCommand ), typeof( ICommand ), typeof( Entry ) );

    public static readonly BindableProperty TextChangedCommandParameterProperty =
        BindableProperty.Create( nameof( Entry.TextChangedCommandParameter ), typeof( object ), typeof( Entry ) );

    public ICommand TextChangedCommand
    {
        get => (ICommand)this.GetValue( Entry.TextChangedCommandProperty );
        set => this.SetValue( Entry.TextChangedCommandProperty, (object)value );
    }

    public object TextChangedCommandParameter
    {
        get => this.GetValue( Entry.TextChangedCommandParameterProperty );
        set => this.SetValue( Entry.TextChangedCommandParameterProperty, value );
    }

    private void OnTextChanged( object sender, TextChangedEventArgs e )
    {
        if ( this.TextChangedCommand == null ||
             !this.TextChangedCommand.CanExecute( this.TextChangedCommandParameter ) )
            return;

        this.TextChangedCommand.Execute( this.TextChangedCommandParameter );
    }
}
以及嵌入在
数据模板中的xaml:

    <my:Entry Grid.Column="1"
           Text="{Binding Value}"
           HorizontalTextAlignment="Start"
           HorizontalOptions="FillAndExpand"
           VerticalOptions="Center"
           VerticalTextAlignment="Center"
           Keyboard='Text'
           ClearButtonVisibility="WhileEditing"
           TextChangedCommand="{Binding BindingContext.TextChangedCommand, Mode=OneTime, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsView}}}"
           TextChangedCommandParameter="{Binding Mode=OneTime}" >
    </my:Entry>
        <my:EventToCommandObserver Grid.Column="1"
                                   Command="{Binding BindingContext.TextChangedCommand, Mode=OneTime, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsView}}}"
                                   CommandParameter="{Binding Mode=OneTime}">
            <Entry Text="{Binding Value}"
                   HorizontalTextAlignment="Start"
                   HorizontalOptions="FillAndExpand"
                   VerticalOptions="Center"
                   VerticalTextAlignment="Center"
                   Keyboard='Text'
                   ClearButtonVisibility="WhileEditing"
                   my:EventToCommandObserver .EventName="TextChanged" />
        </my:EventToCommandObserver >
这个替代的xaml嵌入在一个
数据模板中

    <my:Entry Grid.Column="1"
           Text="{Binding Value}"
           HorizontalTextAlignment="Start"
           HorizontalOptions="FillAndExpand"
           VerticalOptions="Center"
           VerticalTextAlignment="Center"
           Keyboard='Text'
           ClearButtonVisibility="WhileEditing"
           TextChangedCommand="{Binding BindingContext.TextChangedCommand, Mode=OneTime, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsView}}}"
           TextChangedCommandParameter="{Binding Mode=OneTime}" >
    </my:Entry>
        <my:EventToCommandObserver Grid.Column="1"
                                   Command="{Binding BindingContext.TextChangedCommand, Mode=OneTime, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsView}}}"
                                   CommandParameter="{Binding Mode=OneTime}">
            <Entry Text="{Binding Value}"
                   HorizontalTextAlignment="Start"
                   HorizontalOptions="FillAndExpand"
                   VerticalOptions="Center"
                   VerticalTextAlignment="Center"
                   Keyboard='Text'
                   ClearButtonVisibility="WhileEditing"
                   my:EventToCommandObserver .EventName="TextChanged" />
        </my:EventToCommandObserver >

我查看了您的示例代码,您似乎正在使用来实现您的
事件到命令行为
。这也是以大致相同的方式在中实现的注意这些实现继承自
Xamarin.Forms.Behavior

我还尝试在分配给
ItemsView
DataTemplate
中执行这些示例的相对源绑定,但是当我运行示例(与上面的示例相同)时,我会在以下位置收到
invalidoOperationException

Xamarin.Forms.Binding.ApplyRelativeSourceBinding (Xamarin.Forms.BindableObject targetObject, Xamarin.Forms.BindableProperty targetProperty)[0x0006c]位于 C:\Advanced Dev\Xamarin.Forms\Xamarin.Forms.Core\Binding.cs:158

转到,您可以看到抛出是由于在
binding.ApplyRelativeSourceBinding()
中应用绑定时,绑定
targetObject
未继承自
Xamarin.Forms.Element
。自
EventToCommandBehavior
inher