如何在ItemsControl中仅显示集合的几个项目 - Silverlight

时间:2011-10-11 20:46:28

标签: c# silverlight .net-4.0 viewmodel itemscontrol

我想知道是否有人知道如何在ItemsControl中仅显示绑定集合中的几个项目。无论是通过过滤ICollectionView还是其他方式。我相信我可以自己想出一个冗长的解决方案,但我想知道那里有什么。

基本上,我有一个ItemsControl绑定到Model中包含的对象集合。我想要做的只是显示其中的一些项目,然后有一个“查看更多”的超链接/按钮。这将显示整个项目集合。我希望能够使用VSM来发出“崩溃”和“扩展”状态的信号,但我在解决如何初始化列表方面遇到了问题。因为绑定是在XAML中创建的,所以我试图避免在代码隐藏中使用Linq来手动修改ItemsSource集合,如果其他所有集合都失败,这可能是一个解决方案。

如果有必要,我可以显示一些代码,但我认为它不会比我的解释更有帮助。再一次,我只是希望有人在我做了太多实验之前做了类似的事情并最终破坏了我的视图模型大声笑。

提前致谢。

[更新] - 这是我经过多次头脑风暴后想到的解决方案(对于其他希望做同样事情的人)。感谢 AnthonyWJones 这个想法。

我所做的是组合一个通用的“模型”,它充当模型的源集合和“视图”集合之间的桥梁。预期目的(对我来说)是扩展由WCF RIA服务生成的任何模型类,它可能在使用相同的UI(控件和模板)时可能有与之关联的注释,因此预期的集合是EntityCollection,其中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;
        }
    }
}

现在我们可以在绑定中引用注释模型,其中'Timeline'类是数据/绑定上下文。

示例:

<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>

1 个答案:

答案 0 :(得分:2)

这是ViewModel的工作。在内部,您有完整的项目集合。但是,最初ViewModel应该公开IEnumerable,这只会使少数几个可用。

ViewModel还将公开名为“ListAll”的ICommand属性。执行此命令时,会将公开的IEnumerable替换为将列出所有项目的{{1}}。

现在它是一个简单的绑定ItemsControl的情况,就像你正在做的那样,并添加一个“More”按钮绑定“ListAll”命令。