在使用MVVM處理小型Windows應用商店應用時,我遇到了一個棘手的問題。我想使用ListBox顯示Big Brother第14季的房客名單。按照設計,每個ListBoxItem中應該有一個按鈕。單擊按鈕時,應該觸發一個命令,它將從列表框中刪除ListBoxItem。如何通過單擊其DataTemplate中的按鈕從分組列表框中刪除/刪除項目?
下面是我目前的解決方案。它可以工作,但不是很令人滿意,因爲無論何時從ListBox中刪除項目,模型都需要過濾整個集合並刷新整個ListBox,這會導致一些性能問題,尤其是當原始集合非常龐大時。
[MainPage.xaml中]
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="groupInfo" IsSourceGrouped="true" ItemsPath="Items" />
<local:BigBrotherModel x:Name="BBModel" />
<DataTemplate x:Key="lvwItemTemp">
<StackPanel>
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Padding="0" Foreground="Red" FontFamily="Segoe UI Symbol"
Command="{Binding DataContext.RemoveHouseGuestCommand, ElementName=lbxHouseGuests}"
CommandParameter="{Binding}"></Button>
<TextBlock Grid.Column="1" Text="{Binding FirstName}" HorizontalAlignment="Stretch" />
<TextBlock Grid.Column="2" Text="{Binding LastName}" HorizontalAlignment="Stretch" />
</Grid>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<ListBox x:Name="lbxHouseGuests" DataContext="{StaticResource BBModel}" ItemsSource="{Binding Source={StaticResource groupInfo}}" ItemTemplate="{StaticResource lvwItemTemp}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border BorderBrush="Red" BorderThickness="0" Background="DarkGray" HorizontalAlignment="Stretch">
<TextBlock Width="500" Text="{Binding Role}"/>
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid>
</Page>
[MainPage.cs]
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Windows.Foundation;
using Windows.Foundation.Collections;
using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Popups;
using System.Diagnostics;
namespace App1
{
public class HouseGuest
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Role { get; set; }
public bool Deleted { get; set; }
}
public class BigBrotherModel
{
public ObservableCollection<HouseGuest> houseGuests { get; set; }
public ListBox lbx { get; set; }
public CollectionViewSource cvs { get; set; }
public BigBrotherModel()
{
houseGuests = new ObservableCollection<HouseGuest>();
houseGuests.Add(new HouseGuest() { FirstName = "Ian", LastName = "Terry", Role="Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Shane", LastName = "Meaney", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Wil", LastName = "Heuser", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Danielle", LastName = "Murphree", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Jenn", LastName = "Arroyo", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Jodi", LastName = "Rollins", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Ashley", LastName = "Iocco", Role = "Player" });
houseGuests.Add(new HouseGuest() { FirstName = "Britney", LastName = "Haynes", Role = "Coach" });
houseGuests.Add(new HouseGuest() { FirstName = "Dan", LastName = "Gheesling", Role = "Coach"});
houseGuests.Add(new HouseGuest() { FirstName = "Janelle", LastName = "Pierzina", Role = "Coach" });
houseGuests.Add(new HouseGuest() { FirstName = "Mike", LastName = "Boogie", Role = "Coach"});
RemoveHouseGuestCommand = new DelegateCommand(RemoveHouseGuest);
}
public ICommand RemoveHouseGuestCommand { get; set; }
void RemoveHouseGuest(object param)
{
Debug.Assert(param is HouseGuest);
(param as HouseGuest).Deleted = true;
RefreshListBox();
}
object GetGroupedView()
{
var view = from hg in houseGuests
where hg.Deleted == false
group hg by hg.Role into g
orderby g.Key
select new { Role = g.Key, Items = g };
return view;
}
public void RefreshListBox()
{
cvs.Source = GetGroupedView();
}
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
BBModel.cvs = groupInfo;
BBModel.lbx = lbxHouseGuests;
BBModel.RefreshListBox();
}
}
}
我的問題是:有沒有辦法刪除從分組列表框一個ListBoxItem的,而不需要刷新整個ListBox使用MVVM?我真的被困在這裏。任何建議,將不勝感激。提前謝謝了。
[編輯] 感謝Nate的建議,我在MainPage.cs中重寫我的代碼,並且它工作的很棒。以下是源代碼。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Input;
using Windows.Foundation;
using Windows.Foundation.Collections;
using System.Collections.ObjectModel;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Popups;
using System.Diagnostics;
using System.Collections.Specialized;
namespace App1
{
public class HouseGuest
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Role { get; set; }
public bool Deleted { get; set; }
}
public class HouseGuestGroup : IGrouping<string, HouseGuest>
{
public ObservableCollection<HouseGuest> Items { get; set; }
public string Role { get; set; }
public HouseGuestGroup()
{
Items = new ObservableCollection<HouseGuest>();
}
public string Key
{
get { return Role; }
}
public IEnumerator<HouseGuest> GetEnumerator()
{
return Items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return Items.GetEnumerator();
}
}
public class BigBrotherModel : ObservableCollection<HouseGuestGroup>
{
public BigBrotherModel()
{
RemoveHouseGuestCommand = new DelegateCommand(RemoveHouseGuest);
}
public ICommand RemoveHouseGuestCommand { get; set; }
void RemoveHouseGuest(object param)
{
Debug.Assert(param is HouseGuest);
HouseGuest guest = param as HouseGuest;
foreach (var g in Items)
{
if (g.Role == guest.Role)
{
g.Items.Remove(guest);
break;
}
}
}
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
HouseGuestGroup guestGroup;
guestGroup = new HouseGuestGroup();
guestGroup.Role = "Coach";
guestGroup.Items.Add(new HouseGuest() { FirstName = "Britney", LastName = "Haynes", Role = "Coach" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Dan", LastName = "Gheesling", Role = "Coach" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Janelle", LastName = "Pierzina", Role = "Coach" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Mike", LastName = "Boogie", Role = "Coach" });
BBModel.Add(guestGroup);
guestGroup = new HouseGuestGroup();
guestGroup.Role = "Player";
guestGroup.Items.Add(new HouseGuest() { FirstName = "Ian", LastName = "Terry", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Shane", LastName = "Meaney", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Wil", LastName = "Heuser", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Danielle", LastName = "Murphree", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Jenn", LastName = "Arroyo", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Jodi", LastName = "Rollins", Role = "Player" });
guestGroup.Items.Add(new HouseGuest() { FirstName = "Ashley", LastName = "Iocco", Role = "Player" });
BBModel.Add(guestGroup);
groupInfo.Source = BBModel;
}
}
}
順便說一句,我認爲這是更好地把這個問題留給打開,希望一些更好的解決方案,可以出來最後。
Nate,感謝您的幫助。但似乎這個解決方案不起作用,當我點擊一個按鈕時,什麼也沒有發生。也許這樣做的原因是:ListBox的Datasource是由LINQ語句創建的視圖,而不是原始集合housegGuests。我只是不知道如何直接在命令中訪問ListBoxItem。如果我們可以獲得ListBoxItem的引用,那麼我們可以直接從ListBox中移除它。 – SimonFisher
啊,的確如此。發生什麼事是LINQ語句每次都創建一個新列表,因此當您影響可觀察集合時,它不會被反映,因爲它們是兩個不同的列表。你需要做的是創建一個自定義的Group對象,其中有ObservableCollection來存儲客人。然後,您必須創建這些組的ObservableCollection。然後,您可以對靜態集合(或任何子集合)進行任何更改,並將其反映在您的視圖中。 –
我試過了你的建議,確實有效。所以我會將你的建議標記爲對原始問題的回答。謝謝! – SimonFisher