2011-10-11 2 views
2

ItemsControl 내의 바운드 컬렉션에서 몇 가지 항목 만 표시하는 방법을 알고 있는지 궁금합니다. ICollectionView 또는 다른 방법을 필터링하여 표시할지 여부. 나는 독자적으로 장황한 해결책을 제시 할 수있을 것이라고 확신하지만 이미 무엇이 있는지보고 싶습니다.ItemsControl 내에서 컬렉션의 몇 가지 항목 만 표시하는 방법 - Silverlight

기본적으로 모델에 포함 된 개체 컬렉션에 바운드 된 ItemsControl이 있습니다. 내가하고 싶은 것은 그 중 몇 가지 항목 만 표시 한 다음 "더보기"를위한 하이퍼 링크/버튼이 있습니다. 어느 항목의 전체 컬렉션을 표시합니다. VSM을 사용하여 '축소 된'상태와 '확장 된 상태'를 알릴 수 있기를 희망했지만 목록을 초기화하는 방법에 대해 머리를 감싸는 데 문제가 있습니다. 바인딩은 XAML에서 만들어 지므로 코드 숨김에서 Linq를 사용하여 ItemsSource 컬렉션을 수동으로 수정하는 것을 피하려고합니다. 이는 다른 모든 것이 실패 할 경우 솔루션이 될 수 있습니다.

필요한 경우 일부 코드를 표시 할 수 있지만 내 설명보다 더 도움이되지 않을 것이라고 생각합니다. 다시 말하지만, 나는 너무 많은 실험을하고 내 뷰 모델을 깨는 것을 끝내기 전에 누군가가 비슷한 것을했기를 바랄뿐입니다.

미리 감사드립니다.

[업데이트] - 이것은 (다른 사람이 똑같은 일을하기를 원한다면) 많은 브레인 스토밍 후에 나왔던 해결책입니다. 아이디어 덕분에 AnthonyWJones에게 감사드립니다.

내가 한 것은 모델의 원본 컬렉션과 '보기'컬렉션 사이의 다리 역할을하는 일반 '모델'을 결합한 것입니다. 의도 된 목적은 동일한 UI (컨트롤 및 템플릿)를 사용하는 동안 주석이있을 수있는 WCF RIA Service에 의해 생성 된 모든 모델 클래스를 확장하여 예상 컬렉션이 EntityCollection이고 T가 '

// 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 Services는 부분 클래스로 표시된 클래스를 생성합니다 (상황이없는 경우에는 알 수 없지만 내가 본 것을 통해 알 수 있습니다). 새로운 모델을 포함하도록 생성 한 엔티티 클래스를 확장합니다.

// 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> 
+0

기본적으로 결과 매김을 원하십니까? – CodingGorilla

+0

정말 용어의 정의에 없습니다. 컬렉션에 10 개의 항목이 있다고 가정하고 처음로드했을 때 처음 2 개만 표시되고 사용자가 "모두 표시"또는 "더보기"를 클릭하면 다음 2를 표시하는 대신 10 개를 모두 표시합니다. – SilverX

+0

I 당신이 재사용 할 수있는 것이 아무것도 없다고 생각하지 마십시오. 이것은 당신이 자신을 구현하는 데 필요한 것 같은데. 하지만 어쩌면 누군가가 나를 틀리게 증명할 것입니다. =) – CodingGorilla

답변

2

이것은 당신의 ViewModel을위한 일이다. 내부적으로 항목의 전체 모음을 가지고 있습니다. 그러나 처음에는 ViewModel이 IEnumerable을 노출시켜야 몇 가지를 사용할 수 있습니다.

ViewModel은 "ListAll"이라는 ICommand 속성도 노출합니다. 이 명령을 실행하면 노출 된 IEnumerable이 모든 항목을 나열하는 것으로 바뀝니다.

이제는 이미 수행중인 것처럼 ItemsControl을 바인딩하고 "추가"단추를 추가하는 간단한 경우가 "ListAll"명령을 바인딩합니다.

+0

내 솔루션보다 훨씬 간단합니다 (지금 게시 할 필요가 없습니다). – ChrisF

+0

예. 감사의 말을 들었습니다. RIA Services에서 모델을 생성했지만 부분 클래스 선언이 이러한 유형의 작업에 매우 유용 할 때 어떻게 할 것인지 질문했습니다. – SilverX

관련 문제