C# 如何知道在ListView中修改了哪个Xamarin表单条目?

C# 如何知道在ListView中修改了哪个Xamarin表单条目?,c#,listview,custom-controls,xamarin.forms,C#,Listview,Custom Controls,Xamarin.forms,我在一个PCL项目中与Xamarin.Forms合作 我有一个页面/屏幕,其中有一个ListView控件。我已经为ViewCell创建了自定义数据模板 此ViewCell具有不同的控件:一些标签、一个按钮和一个条目 <ListView x:Name="lvProducts" > <ListView.ItemTemplate> <DataTemplate> <ViewCell> <StackLayout

我在一个PCL项目中与Xamarin.Forms合作

我有一个页面/屏幕,其中有一个ListView控件。我已经为ViewCell创建了自定义数据模板

此ViewCell具有不同的控件:一些标签、一个按钮和一个条目

<ListView x:Name="lvProducts" >
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell>
        <StackLayout BackgroundColor="#FFFFFF" Orientation="Vertical">
          <Grid Padding="5">
            ...
            <Button Grid.Row="0" Grid.Column="1" Text="X" 
                    CommandParameter="{Binding MarkReference}"
                    Clicked="DeleteItemClicked" />
            ...
            <StackLayout Grid.Row="2" Grid.ColumnSpan="2" Orientation="Horizontal" >
              <Label Text="Ref.: " FontSize="24" FontAttributes="Bold" TextColor="#000000" />
              <Label Text="{Binding Reference}" FontSize="24" TextColor="#000000" />
            </StackLayout>
            ...
            <Entry Grid.Row="3" Grid.Column="1" Text="{Binding NumElements}"
                   Keyboard="Numeric" Placeholder="" FontSize="24"
                   HorizontalTextAlignment="Center"  Focused="OnItemFocus"    
                   Unfocused="OnItemUnfocus" />
         </Grid>
       </StackLayout>   
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>

如何实现这两点?

我建议您使用以下行为:

public class FocusBehavior : Behavior<Entry>
{
    private Entry _entry;

    public static readonly BindableProperty IsFocusedProperty =
        BindableProperty.Create("IsFocused",
                                typeof(bool),
                                typeof(FocusBehavior),
                                default(bool),
                                propertyChanged: OnIsFocusedChanged);

    public int IsFocused
    {
        get { return (int)GetValue(IsFocusedProperty); }
        set { SetValue(IsFocusedProperty, value); }
    }

    protected override void OnAttachedTo(Entry bindable)
    {
        base.OnAttachedTo(bindable);

        _entry = bindable;
    }

    private static void OnIsFocusedChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var behavior = bindable as FocusBehavior;
        var isFocused = (bool)newValue;

        if (isFocused)
        {
            behavior._entry.Focus();
        }
    }
}

<ListView x:Name="TasksListView"
          ItemsSource={Binding Tasks}
          RowHeight="200">
  <ListView.ItemTemplate>
    <DataTemplate>
      <ViewCell x:Name="ViewCell">
        <Grid x:Name="RootGrid"
              Padding="10,10,10,0"
              BindingContext="{Binding}">
          <Entry>
              <Entry.Behaviors>
                <helpers:FocusBehavior IsFocused="{Binding BindingContext.IsFocused, Source={x:Reference RootGrid}}"/>
              </Entry.Behaviors>
          </Entry>
        </Grid>
      </ViewCell>
    </DataTemplate>
  </ListView.ItemTemplate>
</ListView>
在ViewModel中,添加新项后,将其
IsFocused
属性设置为
true


对于行为,你可以使用
TextChanged
进行输入。

对于我的第一个问题,我找到了一种解决方法。我不知道这是否是最好的解决办法

我已将ListView扩展到CustomListView,并添加了一个单元格字典:

private Dictionary<string, Cell> dicCells;
当单元格被移除时,UnhookContent将激发。我删除字典中存在的项

    //When a new cell item has removed, we remove from the dictionary
    protected override void UnhookContent(Cell content)
    {
        base.UnhookContent(content);

        ViewCell vc = (ViewCell)content;

        if (vc != null)
        {
            BasketProduct bp = (BasketProduct)vc.BindingContext;
            if (bp != null)
            {
                this.dicCells.Remove(bp.MarkReference);
            }
        }

    }
然后,我创建了一个函数,用于检索包含对象(在我的例子中是BasketProduct)的条目(在我的例子中是CustomEntry)

当我想将焦点放在某个条目上时,我调用此方法:

    //Put the focus on the CustomEntry control that represents de BasketProduct that they have passed
    public void SetSelected(BasketProduct bp, bool withDelay)
    {
        CustomEntry entry = null;

        entry = GetEntry(bp);

        if (entry != null)
        {
            if (withDelay)
            {
                FocusDelay(entry);
            } else
            {
                entry.Focus();
            }                                
        }            
    }
如果我从ItemTapped方法调用SetSelected()方法,效果很好,但是如果我在then集合中添加一个项后调用SetSelected()方法,则该项不会获得焦点。在这种情况下,我做了一个诡计

    private async void FocusDelay(CustomEntry entry)
    {            
        await Task.Delay(500);
        entry.Focus();
    }
关于第二个问题,正如@markusian所建议的,我已经扩展了Entry(CustomEntry)控件,在未聚焦的事件中,我已经这样做了:

    private void CustomEntry_Unfocused(object sender, FocusEventArgs e)
    {
        try
        {
            //If the user leaves the field empty, we set the last value
            BasketProduct bp = (BasketProduct)BindingContext;
            if (this.Text.Trim().Equals(string.Empty))
            {
                this.Text = bp.NumElements.ToString();
            }
        }
        catch (FormatException ex) { }
    }

关于第二点,如果需要访问数据对象,可以覆盖单元格中的
OnBindingContextChanged
,然后使用
var object=(YourClass)BindingContext
获取对象。通过这种方式,您可以在需要时访问数据源中的对象。谢谢@markusian,我了解您的想法,我已经使用过它。我扩展了入口控制,并将其用于非焦点事件。很好。谢谢@EgorGromadskiy!你帮助我获得了灵感。我从来没有和行为学打过交道。我必须研究它。
    //Retrieves a CustomEntry control that are into the collection and represents the BasketProduct that we have passed
    public CustomEntry GetEntry(BasketProduct bp)
    {
        CustomEntry ce = null;

        if (bp != null && this.dicCells.ContainsKey(bp.MarkReference))
        {
            ViewCell vc = (ViewCell)this.dicCells[bp.MarkReference];

            if (vc != null)
            {
                ce = (CustomEntry)((Grid)((StackLayout)vc.View).Children[0]).Children[4];
            }

        }

        return ce;
    }
    //Put the focus on the CustomEntry control that represents de BasketProduct that they have passed
    public void SetSelected(BasketProduct bp, bool withDelay)
    {
        CustomEntry entry = null;

        entry = GetEntry(bp);

        if (entry != null)
        {
            if (withDelay)
            {
                FocusDelay(entry);
            } else
            {
                entry.Focus();
            }                                
        }            
    }
    private async void FocusDelay(CustomEntry entry)
    {            
        await Task.Delay(500);
        entry.Focus();
    }
    private void CustomEntry_Unfocused(object sender, FocusEventArgs e)
    {
        try
        {
            //If the user leaves the field empty, we set the last value
            BasketProduct bp = (BasketProduct)BindingContext;
            if (this.Text.Trim().Equals(string.Empty))
            {
                this.Text = bp.NumElements.ToString();
            }
        }
        catch (FormatException ex) { }
    }