[英]How can I update my XAML while a database operation is ongoing and then update it again after it's finished?
[英]How can I update my indexPath after deleting a Row in a TableView?
我是Xamarin
新手,正在尝试制作一些基本的todolist iOS应用。
在使用TableViews
挣扎了一段时间之后,我现在有了一个TableSource和包含UISwitch
自定义单元格。
我的目标是,当用户激活UISwitch
,将为TableView
删除单元格。 为此,我在自定义单元格中创建了一些自定义事件。
public partial class TodoTableViewCell : UITableViewCell
{
public delegate void DeleteTodoEventHandler(UITableView tableView, NSIndexPath indexPath);
public event DeleteTodoEventHandler DeleteTodo;
partial void DeleteTodoEvent(UISwitch sender)
{
if (DeleteTodo != null)
DeleteTodo(null, new NSIndexPath());
}
public TodoTableViewCell(IntPtr p):base(p)
{
}
public TodoTableViewCell(string reuseIdentifier) : base(UITableViewCellStyle.Default, reuseIdentifier)
{
}
public void UpdateCell(TodoItem Item)
{
TodoLabel.Text = Item.Name;
DateLabel.Text = Formating.formatDate(Item.Date);
HourLabel.Text = Formating.formatTime(Item.Date);
}
}
当用户激活UISwitch
时,将调用DeleteTodoEvent 。 这是我的表源:
public class TableSource : UITableViewSource
{
List<TodoItem> TableItems;
string CellIdentifier = "TodoCell";
...
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)
{
TodoTableViewCell cell = tableView.DequeueReusableCell(CellIdentifier) as TodoTableViewCell;
TodoItem item = TableItems[indexPath.Row];
//---- if there are no cells to reuse, create a new one
if (cell == null)
{
cell = new TodoTableViewCell(CellIdentifier);
}
cell.DeleteTodo += (table, index) => DeleteTodoHandler(tableView, indexPath);
cell.UpdateCell(item);
return cell;
}
...
private void DeleteTodoHandler(UITableView tableView, NSIndexPath indexPath)
{
TableItems.RemoveAt(indexPath.Row);
// delete the row from the table
tableView.DeleteRows(new NSIndexPath[] { indexPath }, UITableViewRowAnimation.Fade);
}
}
基本上,只要用户单击UISwitch
, 就会调用DeleteTodoHandler 。 首先它会正确删除该单元格。 但是,indexPath永远不会更新。
我的意思是:想象一下,我的TodoList中有3个单元格。
Cell1-> indexPath.Row = 0 Cell2-> indexPath.Row = 1 Cell3-> indexPath.Row = 2
如果删除第二个单元格Cell2,则Cell3的indexPath应该= = 1而不是2。事实并非如此。 这意味着如果我之后尝试删除Cell3,
TableItems.RemoveAt(indexPath.Row)
会尝试从列表中删除项目3,这导致我们出现异常,因为列表中只有2个项目。
我正在遵循我在Xamarin Doc中找到的用于删除TableView中的行的示例,但就我而言,它显然不起作用。 indexPath.Row是只读的,从TableView中删除项目后,我该怎么做才能正确更新indexPath?
您应该使用同时包含UITableView和自定义UITableSource对象的ViewController来处理删除事件。
然后,您可以从UITableView中获得方法单击的正确行,如下所示:
Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell);
并同时从数据列表和用户界面中删除该行的数据。
这是我为您编写的示例代码,请看一下:
这是ViewController:
public override void ViewDidLoad ()
{
//Create a custom table source
MyTableSource mySource = new MyTableSource ();
//Create a UITableView
UITableView tableView = new UITableView ();
CGRect tbFrame = this.View.Bounds;
tbFrame.Y += 20;
tbFrame.Height -= 20;
tableView.Frame = tbFrame;
tableView.Source = mySource;
tableView.RowHeight = 50;
this.Add (tableView);
//Handle delete event
mySource.DeleteCell += (cell) => {
//Get the correct row
Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell);
//Remove data from source list
mySource.RemoveAt(indexPath.Row);
//Remove selected row from UI
tableView.BeginUpdates();
tableView.DeleteRows(new Foundation.NSIndexPath[]{indexPath},UITableViewRowAnimation.Fade);
tableView.EndUpdates();
};
}
此MyTableSource.cs:
public delegate void DeleteCellHandler(UITableViewCell cell);
public event DeleteCellHandler DeleteCell;
private string CellID = "MyTableCell";
private List<string> tableItems;
public MyTableSource ()
{
tableItems = new List<string> ();
for (int i = 0; i < 10; i++) {
tableItems.Add ("Test Cell " + i.ToString ());
}
}
public void RemoveAt(int row)
{
tableItems.RemoveAt (row);
}
#region implement from UITableViewSource
public override nint RowsInSection (UITableView tableview, nint section)
{
return tableItems.Count;
}
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell;
if (null == cell) {
//Init custom cell
cell = new MyTableCell (UITableViewCellStyle.Default, CellID);
//Bind delete event
cell.DeleteCell += delegate {
if(null != DeleteCell)
DeleteCell(cell);
};
}
cell.Title = tableItems [indexPath.Row];
return cell;
}
#endregion
这是MyTableCell.cs:
public class MyTableCell : UITableViewCell
{
public delegate void DeleteCellHandler();
public event DeleteCellHandler DeleteCell;
UILabel lbTitle = new UILabel ();
UIButton btnDelete = new UIButton (UIButtonType.System);
public string Title{
get{
return lbTitle.Text;
}
set{
lbTitle.Text = value;
}
}
public MyTableCell (UITableViewCellStyle style, string reuseID) : base(style,reuseID)
{
lbTitle.Text = "";
lbTitle.Frame = new CoreGraphics.CGRect (0, 0, 100, 50);
this.AddSubview (lbTitle);
btnDelete.SetTitle ("Delete", UIControlState.Normal);
btnDelete.Frame = new CoreGraphics.CGRect (120, 0, 50, 50);
this.AddSubview (btnDelete);
btnDelete.TouchUpInside += delegate {
if(null != DeleteCell)
DeleteCell();
};
}
}
希望它能对您有所帮助。
如果您仍然需要建议,请在此处给我留言。
- - - - - - - - - - - - - - - 新 - - - - - - - - - - -----------------
我只是建议您将所有逻辑代码移到ViewController中,因为根据MVC模型,它应该在Controller层中。
而且,如果您想通过调用一些API或从本地数据库加载数据来管理数据,这将更加方便。
当然,您可以将delete事件放在TableSource中,只需要从控制器中删除该事件并将其放入TableSource中即可,例如:
ViewController.cs:
public override void ViewDidLoad ()
{
//Create a custom table source
MyTableSource mySource = new MyTableSource ();
//Create a UITableView
UITableView tableView = new UITableView ();
CGRect tbFrame = this.View.Bounds;
tbFrame.Y += 20;
tbFrame.Height -= 20;
tableView.Frame = tbFrame;
tableView.Source = mySource;
tableView.RowHeight = 50;
this.Add (tableView);
// //Handle delete event
// mySource.DeleteCell += (cell) => {
// //Get the correct row
// Foundation.NSIndexPath indexPath = tableView.IndexPathForCell(cell);
// //Remove data from source list
// mySource.RemoveAt(indexPath.Row);
// //Remove selected row from UI
// tableView.BeginUpdates();
// tableView.DeleteRows(new Foundation.NSIndexPath[]{indexPath},UITableViewRowAnimation.Fade);
// tableView.EndUpdates();
// };
}
在TableSource.cs中,像这样更改GetCell方法:
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell;
if (null == cell) {
//Init custom cell
cell = new MyTableCell (UITableViewCellStyle.Default, CellID);
//Bind delete event
cell.DeleteCell += delegate {
// if(null != DeleteCell)
// DeleteCell(cell);
//Get the correct row
Foundation.NSIndexPath dIndexPath = tableView.IndexPathForCell(cell);
int deleteRowIndex = dIndexPath.Row;
Console.WriteLine("deleteRowIndex = "+deleteRowIndex);
//Remove data from source list
RemoveAt(deleteRowIndex);
//Remove selected row from UI
tableView.BeginUpdates();
tableView.DeleteRows(new Foundation.NSIndexPath[]{dIndexPath},UITableViewRowAnimation.Fade);
tableView.EndUpdates();
};
}
cell.Title = tableItems [indexPath.Row];
return cell;
}
您犯的唯一错误是,您不应在自定义单元格的删除事件句柄中添加任何参数。
因为当您从表中删除一个单元格时,其他单元格将永远不会更新其自己的IndexPath。
例如,如果您有3行,而您删除了第二行,则它可以工作。 但是,当您尝试删除第三行时(现在是第二行,因为您只删除了第二行),您将得到一个例外,因为您没有3行或索引越界,因为此单元格的IndexPath是仍然是2(正确的是1)。
因此,您需要使用UITableView的方法“ IndexPathForCell”在删除事件处理程序中计算索引,就像我上面提到的那样。 它将始终获得正确的索引。
------------------关于您的新问题------------------
您犯的错误是关于delete事件init的,您必须在构造单元格时将其初始化,就像我所做的那样:
public override UITableViewCell GetCell (UITableView tableView, Foundation.NSIndexPath indexPath)
{
MyTableCell cell = tableView.DequeueReusableCell (CellID) as MyTableCell;
if (null == cell) {
cell = new MyTableCell (UITableViewCellStyle.Default, CellID);
//Bind delete event
cell.DeleteCell += delegate {
//Delete stuff
};
}
cell.Title = tableItems [indexPath.Row];
return cell;
}
如果只是像您一样使用它:
if (null == cell) {
cell = new MyTableCell (UITableViewCellStyle.Default, CellID);
}
//Bind delete event
cell.DeleteCell += delegate {
//Delete stuff
};
cell.Title = tableItems [indexPath.Row];
这将导致重复绑定问题,因为表单元已被重用,因此当您尝试刷新/插入/删除甚至滚动UITableView时,将触发“ GetCell”方法,因此当您尝试调用单元的delete事件时,如果将调用多个委托,第一个委托正在工作,并且如果您至少有2个委托,它将导致遇到的问题。
效果类似于此屏幕截图: 重现步骤:1为tableview添加一个新项目; 2删除刚添加的项目; 3再次添加新项目; 4删除刚添加的项目;
因为那里有2位代表,所以引发了异常。
最后,我将完整的示例代码上传到了我的GitHub,如果需要,请下载它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.