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

时间:2016-03-23 14:32:13

标签: c# listview custom-controls xamarin.forms

我正在PCL项目中使用Xamarin.Forms。

我有一个页面/屏幕,其中有一个ListView控件。我为ViewCell创建了一个自定义DataTemplate。

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

我想通过这个我无法实现的Entry控件实现两件事:

首先,当我添加一个新项目时,我希望这个新项目的条目成为焦点,准备开始输入。

其次,当用户结束在Entry中写入值时,我想改变后面的值。我想知道ListView的哪个条目已修改。我尝试使用Unfocused事件,但是在启动方法的params中只有一个返回Entry对象的sender param,没有关于绑定模型的引用。

    public void OnItemUnfocus(object sender, EventArgs e)
    {
        Entry entry = (Entry)sender;
        //Here I would like to know the model object that's binded 
        //with this Entry / CellView item
    }

我如何才能实现这两点?

2 个答案:

答案 0 :(得分:3)

我建议您使用行为:

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>

我的模特:

public class TaskModel : INotifyPropertyChanged
{
    private bool _isFocused;

    public bool IsFocused
    {
        get { return _isFocused; }
        set
        {
            _isFocused = value;
            RaisePropertyChanged();
        }
    }

在ViewModel中,添加新项目后,将其IsFocused属性设置为true

您可以将TextChanged用于Entry的行为也是如此。

答案 1 :(得分:-1)

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

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

private Dictionary<string, Cell> dicCells;

另外,我已经覆盖了SetupContent和UnhookContent方法。

当添加新单元格时,SetupContent将触发,并且其中一个参数为我提供了保存到字典中的新单元格。 (MarkReference是我的关键值)

    //When a new cell item has added, we save it into a dictionary
    protected override void SetupContent(Cell content, int index)
    {
        base.SetupContent(content, index);

        ViewCell vc = (ViewCell)content;            

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

    }

删除单元格时会触发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);
            }
        }

    }

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

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

如果我从ItemTapped方法调用SetSelected()方法,工作正常,但如果我在then collection中添加一个项后调用SetSelected()方法,则Entry不会获得焦点。在这种情况下,我做了一个技巧。

    private async void FocusDelay(CustomEntry entry)
    {            
        await Task.Delay(500);
        entry.Focus();
    }

关于第二个问题,正如@markusian建议的那样,我扩展了Entry(CustomEntry)控件,并且在Unfocused事件中我完成了这个:

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