我想知道是否有人知道如何在 ItemsControl 中仅显示绑定集合中的几个项目。无论是通过过滤ICollectionView还是其他方式。我相信我可以自己想出一个冗长的解决方案,但我想看看已经有什么了。
基本上,我有一个绑定到模型中包含的对象集合的 ItemsControl。我想做的是只显示其中的几个项目,然后有一个超链接/按钮来"查看更多"。这将显示整个项目集合。我希望能够使用 VSM 来发出"折叠"和"扩展"状态的信号,但我在如何初始化列表时遇到了问题。由于绑定是在 XAML 中创建的,因此我试图避免在代码隐藏中使用 Linq 来手动修改 ItemsSource 集合,如果所有其他方法都失败,这可能是一个解决方案。
如有必要,我可以显示一些代码,但我认为它不会比我的解释更有帮助。再说一次,我只是希望有人在我尝试太多并最终破坏我的视图模型之前做过类似的事情,哈哈。
提前谢谢。
[更新] - 这是我经过多次头脑风暴后提出的解决方案(对于希望做同样事情的任何其他人(。感谢AnthonyWJones的想法。
我所做的是将一个通用的"模型"放在一起,它充当模型的源集合和"视图"集合之间的桥梁。预期目的(对我来说(是扩展 WCF RIA 服务生成的任何模型类,这些模型类在使用相同的 UI(控件和模板(时可能具有与之关联的注释,因此预期的集合是实体集合,其中 T 是"实体"的实例
以下所有类都在 Silverlight 客户端项目中声明
首先是一点管道:
// this is so we can reference our model without generic arguments
public interface ICommentModel : INotifyPropertyChanged
{
Int32 TotalComments { get; }
Int32 VisibleComments { get; }
Boolean IsExpanded { get; set; }
Boolean IsExpandable { get; }
ICommand ExpandCommand { get; }
IEnumerable Collection { get; }
}
// the command we'll use to expand our collection
public class ExpandCommand : ICommand
{
ICommentModel model;
public ExpandCommand(ICommentModel model) {
this.model = model;
this.model.PropertyChanged += ModelPropertyChanged;
}
public bool CanExecute(object parameter) {
return this.model.IsExpandable;
}
public void Execute(object parameter) {
this.model.IsExpanded = !this.model.IsExpanded;
}
private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "IsExpandable")
RaiseCanExecuteChanged();
}
private void RaiseCanExecuteChanged() {
var execute = CanExecuteChanged;
if (execute != null) execute(this, EventArgs.Empty);
}
public event EventHandler CanExecuteChanged;
}
// and finally.. the big guns
public class CommentModel<TEntity> : ICommentModel
where TEntity : Entity
{
Boolean isExpanded;
ICommand expandCommand;
IEnumerable<TEntity> source;
IEnumerable<TEntity> originalSource;
public Int32 TotalComments { get { return originalSource.Count(); } }
public Int32 VisibleComments { get { return source.Count(); } }
public Boolean IsExpanded {
get { return isExpanded; }
set { isExpanded = value; OnIsExpandedChanged(); }
}
public Boolean IsExpandable {
get { return (!IsExpanded && originalSource.Count() > 2); }
}
public ICommand ExpandCommand {
get { return expandCommand; }
}
public IEnumerable Collection { get { return source; } }
public CommentModel(EntityCollection<TEntity> source) {
expandCommand = new ExpandCommand(this);
source.EntityAdded += OriginalSourceChanged;
source.EntityRemoved += OriginalSourceChanged;
originalSource = source;
UpdateBoundCollection();
}
private void OnIsExpandedChanged() {
OnPropertyChanged("IsExpanded");
UpdateBoundCollection();
}
private void OriginalSourceChanged(object sender, EntityCollectionChangedEventArgs<TEntity> e) {
OnPropertyChanged("TotalComments");
UpdateBoundCollection();
}
private void UpdateBoundCollection() {
if (IsExpanded)
source = originalSource.OrderBy(s => PropertySorter(s));
else
source = originalSource.OrderByDescending(s => PropertySorter(s)).Take(2).OrderBy(s => PropertySorter(s));
OnPropertyChanged("IsExpandable");
OnPropertyChanged("VisibleComments");
OnPropertyChanged("Collection");
}
// I wasn't sure how to get instances Func<T,TRet> into this class
// without some dirty hacking, so I used some reflection to run "OrderBy" queries
// All entities in my DataModel have 'bigint' Id columns
private long PropertySorter(TEntity s) {
var props = from x in s.GetType().GetProperties()
where x.Name == "Id"
select x;
if (props.Count() > 0)
return (long)props.First().GetValue(s, null);
return 0;
}
protected virtual void OnPropertyChanged(string propName) {
var x = PropertyChanged;
if (x != null) x(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
现在我们需要使用它。WCF RIA 服务生成标记为部分的类(我不知道是否存在它没有的情况,但从我所看到的情况来看(。因此,我们将扩展它生成的实体类以包含我们的新模型。
// this must be inside the same namespace the classes are generated in
// generally this is <ProjectName>.Web
public partial class Timeline
{
ICommentModel model;
public ICommentModel CommentModel {
get {
if (model == null)
model = new CommentModel<TimelineComment>(Comments);
return model;
}
}
}
现在我们可以在绑定中引用注释模型,其中"时间线"类是数据/绑定上下文。
例:
<UserControl x:Class="Testing.Comments"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="291" d:DesignWidth="382">
<Border CornerRadius="2" BorderBrush="{StaticResource LineBrush}" BorderThickness="1">
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Visibility="{Binding Path=CommentModel.IsExpandable, Converter={StaticResource BooleanToVisibility}}">
<HyperlinkButton
FontSize="10"
Command="{Binding Path=CommentModel.ExpandCommand}"
Background="{StaticResource BackBrush}">
<TextBlock>
<Run Text="View all"/>
<Run Text="{Binding Path=CommentModel.TotalComments}"/>
<Run Text="comments"/>
</TextBlock>
</HyperlinkButton>
<Rectangle Height="1" Margin="0,1,0,0" Fill="{StaticResource LineBrush}" VerticalAlignment="Bottom"/>
</StackPanel>
<ItemsControl
Grid.Row="1"
ItemsSource="{Binding Path=CommentModel.Collection}"
ItemTemplate="{StaticResource CommentTemplate}" />
</Grid>
</Border>
</UserControl>
您的 ViewModel 的工作。 在内部,您拥有完整的项目集合。 但是,最初 ViewModel 应公开一个仅提供少数IEnumerable
。
ViewModel 还将公开一个名为"ListAll"的ICommand
属性。 执行此命令时,会将公开的IEnumerable
替换为列出所有项目的。
简单的案例,即像您已经做的那样绑定 ItemsControl,并添加一个绑定"ListAll"命令的"更多"按钮。