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

Posted

技术标签:

【中文标题】如何知道 ListView 中修改了哪个 Xamarin 表单条目?【英文标题】:How to know which Xamarin Forms Entry has been modified in a ListView? 【发布时间】:2016-07-10 21:54:00 【问题描述】:

我在 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 事件,但是在启动的方法的参数中只有一个返回 Entry 对象的发送方参数,没有关于已绑定模型的引用。

    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
    

我怎样才能达到这两点?

【问题讨论】:

关于第二点,如果需要访问数据对象,可以覆盖单元格中的OnBindingContextChanged,然后获取var object = (YourClass)BindingContext的对象。这样您就可以在需要时访问数据源中的对象。 谢谢@markusian,我明白了你的想法,我已经使用了。我扩展了 Entry 控件并使用了 Unfocused 事件。工作正常。 【参考方案1】:

我想建议你使用行为:

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 的行为相同。

【讨论】:

谢谢@EgorGromadskiy!你帮助我获得了灵感。我从来没有处理过行为。我必须研究它。【参考方案2】:

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

我已将 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;
    

当我想把焦点放在某个Entry上时,我调用这个方法:

    //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) 控件,并且在 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)  
    

【讨论】:

以上是关于如何知道 ListView 中修改了哪个 Xamarin 表单条目?的主要内容,如果未能解决你的问题,请参考以下文章

winform中修改ListView选中项颜色

WPF里,如何修改ListView的指定行的字体颜色和背景颜色?

ListView中的ContextMenu,长按出来后如何知道来自哪一行?

如何查找哪个脚本修改了选定属性的 css

vb哪个列表控件好(listview , datagrid等)

Xama不会跟踪Xamarin Android新文件