Мне было интересно, знает ли кто-нибудь, как отобразить только несколько элементов из связанной коллекции в ItemsControl. Будь то путем фильтрации ICollectionView или других средств. Я уверен, что смогу придумать долгосрочное решение самостоятельно, но я хотел бы посмотреть, что там уже есть.
По сути, у меня есть ItemsControl, связанный с коллекцией объектов, содержащихся в модели. То, что я хотел бы сделать, это отобразить только некоторые из этих элементов, а затем добавить гиперссылку / кнопку для «просмотра более». Который будет отображать всю коллекцию предметов. Я надеялся, что смогу использовать VSM для оповещения о «свернутых» и «развернутых» состояниях, но у меня возникают проблемы, связанные с инициализацией списка. Поскольку привязка создается в XAML, я стараюсь не использовать Linq в коде для изменения вручную коллекции ItemsSource, что может быть решением в случае сбоя всего остального.
Я мог бы показать некоторый код, если это необходимо, но я не думаю, что он будет более полезным, чем мое объяснение. Опять же, я просто надеюсь, что кто-то проделал нечто подобное, прежде чем я слишком много экспериментирую и в итоге сломаю свою модель зрения.
Заранее спасибо.
[ОБНОВЛЕНИЕ] - Это решение, которое я придумал после долгих мозговых штурмов (для тех, кто хочет сделать то же самое). Спасибо AnthonyWJones за идею.
Что я сделал, так это собрал общую «модель», которая действует как мост между исходной коллекцией модели и коллекцией «представления». Предполагаемая цель (для меня) состояла в том, чтобы расширить любой класс модели, сгенерированный службой RIA WCF, который может иметь комментарии, связанные с ним, используя тот же пользовательский интерфейс (элементы управления и шаблоны), поэтому ожидаемая коллекция - это EntityCollection, где T - это экземпляр Entity '
Все следующие классы объявлены в клиентском проекте 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 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 является контекстом данных / привязки.
* 1 028 * Пример:
<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>