我正在尝试创建一个小型应用程序,在其中我可以为即将到来的欧洲足球锦标赛做好准备。
为此,我需要添加课程匹配。
我希望有3个组合框,其中第一个是由它的匹配类型(Poule A,Poule B等)设置的。在设置了组合框之后,我希望接下来的两个组合框只显示那些组合中的团队。
我相信这可以使用转换器来完成,但我似乎无法让它工作......还是有更好的方法?
当前代码:
<ComboBox ItemsSource="{Binding MatchTypes}"
DisplayMemberPath="TypeName"
Grid.Row="1" />
<ComboBox ItemsSource="{Binding Teams}"
DisplayMemberPath="TeamName"
Grid.Column="1"
Grid.Row="1" />
<ComboBox ItemsSource="{Binding Teams}"
DisplayMemberPath="TeamName"
Grid.Column="2"
Grid.Row="1" />
有没有一种简单的方法(linq?)只查询第一个组合框中选择的poule中的最后两个组合框?
如果可能的话,我更喜欢将其保留在视图模型之外并使用转换器或类似的东西。
答案 0 :(得分:2)
就个人而言,我会在viewmodel代码中保留它。我做了类似的事情,所以在这里映射到你正在做的事情:
我的viewmodel中有一个预先填充的项目列表。这将是MatchTypes。
我会设置另一个名为CurrentMatchType的属性,在设置时使用INotifyPropertyChanged。
当设置了CurrentMatchType的值时,它将调用数据源并在viewmodel上填充其他两个列表。我们还有2个名为PouleA和PouleB的变量,代表最终的团队选择。我们将调用刚刚从服务器TeamsA和TeamsB中获取的列表。两个列表的数据相同,但是我将数据源结果设置为内部值,然后将TeamsA设置为除PouleB中选择的团队之外的所有团队列表,以及除了所有团队列表之外的团队B列表PouleA中的那些。这样,一支队伍就无法自己匹配。
我忘记了最后一件事:在PouleA和PouleB的设置器上,您将运行与上面相同的代码来过滤可用的团队,以便排除相反的团队。由于INPC与所有内容相连,因此您的组合框将自动更改。
当我从数据源中获取数据时,我会公开一个属性,让BusyIndicator接管屏幕,这样在抓取数据之前无法触及任何内容。
我在营地里认为尝试使用转换器来做这样的事情会增加不必要的挫败感。如果你不想将它添加到你的viewmodel中,因为你在不同的地方重复使用它,那么就没有什么能阻止你制作一个新的viewmodel来将旧的viewmodel暴露为属性。
<强>伪代码强>
using System;
/* In your model... */
public sealed class MatchType
{
public string Name { get; internal set; }
public string Description { get; internal set; }
public int ID { get; internal set; }
}
public sealed class Team
{
public string Name { get; set; }
public MatchType MatchType { get; set; }
public int? MatchTypeID { get; set; }
public int ID { get; set; }
}
/* In your viewmodel... */
public sealed class TeamSelection
{
// These two should be INotifyPropertyChanged, shortened for this example.
public MatchType[] MatchTypes { get; private set; }
public Team[] TeamsA { get; private set; }
public Team[] TeamsB { get; private set; }
private Team[] teams = null;
MatchType matchType = null;
public MatchType SelectedMatchType {
get { return matchType; }
set
{
if (value != null)
matchType = value;
else if (MatchTypes != null && MatchTypes.Length > 0)
matchType = MatchTypes[0];
else
return;
PropertyHasChanged(() => SelectedMatchType);
PopulateTeams();
}
}
Team teamA;
Team teamB;
public Team SelectedTeamA
{
get { return teamA; }
set
{
if (teamA.ID == teamB.ID)
// Alternatively, set a flag and stop execution.
throw new InvalidOperationException("The same team cannot be selected.");
teamA = value;
PopulateTeams();
PropertyHasChanged(() => SelectedTeamA);
}
}
public Team SelectedTeamB
{
get { return teamB; }
set
{
if (teamA.ID == teamB.ID)
// Alternatively, set a flag and stop execution.
throw new InvalidOperationException("The same team cannot be selected.");
teamB = value;
PopulateTeams();
PropertyHasChanged(() => SelectedTeamB);
}
}
/// <summary>
/// This can be done on your model, or what I do is pass it to
/// an intermediary class, then that sets the busy status to
/// a BusyIndicator set as the visual root of the application.
/// </summary>
public bool IsBusy { get; private set; }
public string IsBusyDoingWhat { get; private set; }
public TeamSelection()
{
// Call out to DB for the match types, setting busy status
var wcf = new WcfService();
wcf.GetMatchTypes(response =>
{
wcf.GetMatchTypesForTeam(MatchType, response2 =>
{
teams = response.Value.ToArray();
MatchTypes = response2.Value.ToArray();
MatchType = MatchTypes[0];
PopulateTeams();
});
});
}
void PopulateTeams()
{
if (MatchType == null)
return;
var op = teams.Where(t => t.MatchTypeID == MatchType.ID);
if (SelectedTeamA != null)
TeamsB = op.Where(t => t.ID != SelectedTeamA.ID).OrderBy(t => t.Name);
else
TeamsB = op.OrderBy(t => t.Name);
if (SelectedTeamB != null)
TeamsA = op.Where(t => t.ID != SelectedTeamB.ID).OrderBy(t => t.Name);
else
TeamsA = op.OrderBy(t => t.Name);
}
}