我正在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
}
我如何才能实现这两点?
答案 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) { }
}