我们正在开发一个关于Xamarin.Forms的跨平台应用程序
在其中一个页面上,我们需要显示一组具有相同项目列表的3个选择器。这个想法是,当你在其中一个选择器上选择一个项目时,它会从其他两个项目的源项目中删除
为此,我们开发了以下代码:
我们从一个名为BaseList
的项目列表开始,我们从Web服务获取这些项目。我们还创建了3个单独的列表(ListA
,ListB
和ListC
)以及3个项目来存储每个选择器的选定项目(SelectedA
,SelectedB
和{ {1}})。
SelectedC
此代码在我们的ViewModel上,因此我们使用SetProperty将引用的属性设置为该值,并从private List<Item> BaseList;
private List<Item> _ListA;
private Item _SelectedA;
private List<Item> _ListB;
private Item _SelectedB;
private List<Item> _ListC;
private Item _SelectedC;
…
//Api Calls
private void LoadData()
{
…
BaseList = new List<Item> (ListFromWebServices);
_ListA = new List<Item>(BaseList);
OnPropertyChanged(nameof(ListA));
_ListB = new List<Item>(BaseList);
OnPropertyChanged(nameof(ListB));
_ListC = new List<Item>(BaseList);
OnPropertyChanged(nameof(ListC));
}
…
//Public Fields
public List<Item> ListA
{
get
{
return _ListA;
}
}
public Item SelectedA
{
get
{
return _SelectedA;
}
set
{
SetProperty(ref _SelectedA, value, nameof(SelectedA));
}
}
public List<Item> ListB
{
get
{
return _ListB;
}
}
public Item SelectedB
{
get
{
return _SelectedB;
}
set
{
SetProperty(ref _SelectedB, value, nameof(SelectedB));
}
}
public List<Item> ListC
{
get
{
return _ListC;
}
}
public Item SelectedC
{
get
{
return _SelectedC;
}
set
{
SetProperty(ref _SelectedC, value, nameof(SelectedC));
}
}
PropertyChangedEventArgs
INotifyPropertyChanged
为了更新ItemSource,每当更改所选项目时,我们都会从public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value,
[CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
,OnSelectedItemChanged
和SelectedA
的设置者中调用SelectedB
。此方法接收一个索引,指示哪个Picker触发了它:
SelectedC
我们在这里做的基本上是将每个选择器的当前所选项目保存在单独的变量上,将private void OnSelectedItemChanged(int index)
{
Item CurrentA = SelectedA;
Item CurrentB = SelectedB;
Item CurrentC = SelectedC;
int i;
switch (index)
{
case 0:
_ListB = new List<Item> (BaseList);
_ListB.Remove(CurrentA);
_ListB.Remove(CurrentC);
OnPropertyChanged(nameof(ListB));
_ListC = new List<Item>(BaseList);
_ListC.Remove(CurrentA);
_ListC.Remove(CurrentB);
OnPropertyChanged(nameof(ListC));
i = ListB.IndexOf(CurrentB);
if (i > -1)
{
_SelectedB = ListB[i];
}
OnPropertyChanged(nameof(SelectedB));
i = ListC.IndexOf(CurrentC);
if (i > -1)
{
_SelectedC = ListC[i];
}
OnPropertyChanged(nameof(SelectedC));
break;
case 1:
_ListA = new List<Item>(BaseList);
_ListA.Remove(CurrentB);
_ListA.Remove(CurrentC);
OnPropertyChanged(nameof(ListA));
_ListC = new List<Item>(BaseList);
_ListC.Remove(CurrentA);
_ListC.Remove(CurrentB);
OnPropertyChanged(nameof(ListC));
i = ListA.IndexOf(CurrentA);
if (i > -1)
{
_SelectedA = ListA[i];
}
OnPropertyChanged(nameof(SelectedA));
i = ListC.IndexOf(CurrentC);
if (i > -1)
{
_SelectedC = ListC[i];
}
OnPropertyChanged(nameof(SelectedC));
break;
case 2:
_ListA = new List<Item>(BaseList);
_ListA.Remove(CurrentB);
_ListA.Remove(CurrentC);
OnPropertyChanged(nameof(ListA));
_ListB = new List<Item>(BaseList);
_ListB.Remove(CurrentA);
_ListB.Remove(CurrentC);
OnPropertyChanged(nameof(ListB));
i = ListA.IndexOf(CurrentA);
if (i > -1)
{
_SelectedA = ListA[i];
}
OnPropertyChanged(nameof(SelectedA));
i = ListB.IndexOf(CurrentB);
if (i > -1)
{
_SelectedB = ListB[i];
}
OnPropertyChanged(nameof(SelectedB));
break;
}
}
复制到没有调用该事件的两个选择器中,然后在每个新列表中删除所有其他选择器使用的选项,将每个新列表中的选定项目再次设置为最初选择的项目,最后调用BaseList
以通知视图更改。
这里的问题是,当我们更改Picker上的OnPropertyChanged()
时,会将ItemSource
设置为SelectedItem
。调用null
后调用setter上的OnPropertyChanged()
导致一个Picker的无限循环更新另一个,并添加一个过滤器,在设置之前检查该值是否为null使得Picker显示no选择的项目,而值已经设置。
答案 0 :(得分:0)
万一有人遇到同样的问题,我们找到了解决方法。如果您制作CurrentA
,CurrentB
和CurrentC
全局变量,并在每种情况下添加if ((CurrentA != SelectedA) && (!(SelectedA is null))) { ... (do all the stuff) } break;
并在最后设置
_SelectedA = CurrentA;
OnPropertyChanged(nameof(SelectedA));
_SelectedB = CurrentB;
OnPropertyChanged(nameof(SelectedB));
_SelectedC = CurrentC;
OnPropertyChanged(nameof(SelectedC));
它有效。我们不知道为什么:)