如何知道在 ListView 中修改了哪个 Xamarin Forms 条目?
How to know which Xamarin Forms Entry has been modified in a ListView?
我正在 Xamarin.Forms 的一个 PCL 项目中工作。
我有一个 page/screen,其中有一个 ListView 控件。我为 ViewCell 创建了自定义 DataTemplate。
这个 ViewCell 有不同的控件:一些 Labels,一个 Button 和一个 Entry。
<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 的哪个 Entry 被修改了。我尝试使用 Unfocused 事件,但是在启动的方法的参数中只有一个发送器参数,returns 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
}
我怎样才能达到这两点?
我想建议你使用行为:
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 的行为与此相同。
对于我的第一个问题,我找到了解决它的方法。不知道是不是最好的方案。
我已将 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);
}
}
}
然后,我创建了一个函数来检索包含对象(在我的例子中是 BasketProduct)的条目(在我的例子中是 CustomEntry)。
//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() 方法,工作正常,但如果我在然后集合中添加项目后调用 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) { }
}
我正在 Xamarin.Forms 的一个 PCL 项目中工作。
我有一个 page/screen,其中有一个 ListView 控件。我为 ViewCell 创建了自定义 DataTemplate。
这个 ViewCell 有不同的控件:一些 Labels,一个 Button 和一个 Entry。
<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 的哪个 Entry 被修改了。我尝试使用 Unfocused 事件,但是在启动的方法的参数中只有一个发送器参数,returns 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
}
我怎样才能达到这两点?
我想建议你使用行为:
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 的行为与此相同。
对于我的第一个问题,我找到了解决它的方法。不知道是不是最好的方案。
我已将 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);
}
}
}
然后,我创建了一个函数来检索包含对象(在我的例子中是 BasketProduct)的条目(在我的例子中是 CustomEntry)。
//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() 方法,工作正常,但如果我在然后集合中添加项目后调用 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) { }
}