在我看来,我有一个ListBox
,绑定到一个动态增长的集合。我希望滚动位置可以追踪最后添加的项目(附加到列表底部)。我如何通过Caliburn.Micro实现这一目标?如何使用caliburn.micro设置视图模型的滚动位置?
回答
另一种方法是使用事件聚合器将消息发布到视图。
喜欢的东西:
Aggregator.Publish(ItemAddedMessage<SomeItemType>(itemThatWasJustAdded));
,并在视图:
public class SomeView : IHandle<ItemAddedMessage<SomeItemType>>
{
public void Handle(ItemAddedMessage<SomeItemType> message)
{
// Implement view specific behaviour here
}
}
这取决于你的需求是什么,但至少再视图负责显示的担忧,你仍然可以测试VM
你也可以只实现代码仅在视图 - 因为它似乎是一个视图关注(例如,使用列表框提供的事件)
一种行为也会很有用,但也许会对您的类型有一些帮助 - 例如一个通用行为SeekAddedItemBehaviour
,它钩住列表框事件以查找最后一个项目。不知道,如果列表框暴露所需的事件,但值得一看
编辑:
确定这可能工作句号 - 你应该能够只重视这种行为的列表框和应采取的护理休息:
public class ListBoxSeekLastItemBehaviour : System.Windows.Interactivity.Behavior<ListBox>
{
private static readonly DependencyProperty ItemsSourceWatcherProperty = DependencyProperty.Register("ItemsSourceWatcher", typeof(object), typeof(ListBoxSeekLastItemBehaviour), new PropertyMetadata(null, OnItemsSourceWatcherPropertyChanged));
private ListBox _listBox = null;
private static void OnItemsSourceWatcherPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ListBoxSeekLastItemBehaviour source = d as ListBoxSeekLastItemBehaviour;
if (source != null)
source.OnItemsSourceWatcherPropertyChanged();
}
private void OnItemsSourceWatcherPropertyChanged()
{
// The itemssource has changed, check if it raises collection changed notifications
if (_listBox.ItemsSource is INotifyCollectionChanged)
{
// if it does, hook the CollectionChanged event so we can respond to items being added
(_listBox.ItemsSource as INotifyCollectionChanged).CollectionChanged += new NotifyCollectionChangedEventHandler(ListBoxSeekLastItemBehaviour_CollectionChanged);
}
}
void ListBoxSeekLastItemBehaviour_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems.Count > 0)
{
// If an item was added seek it
ScrollIntoView(e.NewItems[0]);
}
}
protected override void OnAttached()
{
base.OnAttached();
// We've been attached - get the associated listbox
var box = this.AssociatedObject as ListBox;
if (box != null)
{
// Hold a ref
_listBox = box;
// Set a binding to watch for property changes
System.Windows.Data.Binding binding = new System.Windows.Data.Binding("ItemsSource") { Source = _listBox; }
// EDIT: Potential bugfix - you probably want to check the itemssource here just
// in case the behaviour is applied after the original ItemsSource binding has been evaluated - otherwise you might miss the change
OnItemsSourceWatcherPropertyChanged();
}
}
private void ScrollIntoView(object target)
{
// Set selected item and try and scroll it into view
_listBox.SelectedItem = target;
_listBox.ScrollIntoView(target);
}
}
你可能想整理一下了一下,也确保了CollectionChanged
事件处理程序的ItemsSource
更改时删除。你
可能还需要调用它SeekLastAddedItemBehaviour
或SeekLastAddedItemBehavior
- 我倾向于保持美国的拼写,因为它微软的拼写匹配。我认为SeekLastItem
听起来像它会滚动到列表中的最后一个项目,而不是最后添加的项目
您可以使用GetView()引用视图模型中的视图。这也加上了观点和观点模型。
var myView = GetView() as MyView;
myView.MyListBox.DoStuff
另一种选择是创建一个行为。 This是如何使用行为从视图模型中扩展TreeView
的示例。这同样适用于ListBox
。
让ViewModel操纵视图并不总是一个好主意 - 您将如何区分“ViewModel”的单元测试? *最好的答案是创建一个Derek指出的行为。 - 行为在View中保留,这将显示问题保留在正确的层次上。 – EtherDragon 2013-03-07 22:45:47
我同意,这就是为什么我通常使用行为或实现IResult,但仍然从vm引用视图是一个选项。这是一种方便,快速和肮脏的方式来做一个概念证明,以后可以转移到更多的解耦选项。 – 2013-03-08 17:51:59
我在之前的评论中提到了IResult,在这个特殊情况下它不起作用,所以我没有将它包含在答案中。由于传递的ActionExecutionContext,IResult在某些情况下非常方便。 – 2013-03-08 17:55:55
实际上,有一个更简单的方法来实现这一点,没有任何上述。然后
namespace Extensions.Examples {
public class ScrollingListBox : ListBox
{
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
int newItemCount = e.NewItems.Count;
if (newItemCount > 0)
this.ScrollIntoView(e.NewItems[newItemCount - 1]);
base.OnItemsChanged(e);
}
}
}
}
在XAML中,声明你的扩展类的位置,因为这样:
只是下面的扩展您的列表框
xmlns:Extensions="clr-namespace:Extensions.Examples"
当您创建列表框,而不是使用
<Listbox></Listbox>
只需使用您的扩展类
<Extensions:ScrollingListBox></Extensions:ScrollingListBox>
- 1. expandablelist视图滚动位置
- 2. Uitextview滚动视图位置
- 3. 如何设置UIScrollView的滚动位置?
- 4. 如何设置滚动位置长PreferenceScreen
- 5. 滚动窗格设置滚动位置
- 6. 设置滚动位置
- 7. 如何设置div内滚动div的滚动位置?
- 8. 如何在滚动视图中保存滚动位置?
- 9. 如何将滚动条/视图置于顶部位置?
- 10. 如何获取上一个滚动视图位置,滚动视图
- 11. Backbone.js的设置使用模型视图EL id属性动态
- 12. 的Android如何设置滚动型
- 13. 如何使用滚动条重置滚动条内的滚动条位置?
- 14. 在滚动时跟踪滚动视图中图像的位置
- 15. 如何设置从jQuery的视图模型值MVC Rozor视图
- 16. 如何设置此数据以使用视图和模型
- 17. 如何设置图表的滚动条比例视图位置到最近使用c增加值#
- 18. 如何设置一个视图不滚动与Java滚动视图
- 19. Recycler视图滚动到特定位置
- 20. 如何使用ActionScript3.0的设置在图像的动态位置?
- 21. Caliburn.Micro,如何访问从视图内使用的实际视图模型
- 22. 如何使用位置:绝对滚动
- 23. 如何在使用addView时设置视图位置
- 24. 设置滚动条的位置
- 25. 设置一个滚动条的位置
- 26. 设置列表框的滚动位置
- 27. 设置ListBox的滚动条位置
- 28. 根据滚动设置div的位置
- 29. 我如何设置水平滚动视图的图像对齐
- 30. 当在子视图位置选择滚动视图子视图滚动
只要看一下,它似乎并不像'ListBox'知道'ItemsSource'的变化(在Silverlight中),所以它可能无法写出一个通用的行为。有一个'ScrollIntoView'方法需要一个对象 - 设置SelectedItem并调用ScrollIntoView可能会做到这一点,但在不知道'ItemsSource'变化的时候,钩住列表更改事件会很困难! – Charleh 2013-03-08 14:15:47
那么有人设法通过创建一个绑定来观察属性的变化 - 我坚持在那里的行为的实现。它应该工作,但我没有测试它。 – Charleh 2013-03-08 14:28:27