2013-03-07 111 views

回答

1

另一种方法是使用事件聚合器将消息发布到视图。

喜欢的东西:

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更改时删除。你

可能还需要调用它SeekLastAddedItemBehaviourSeekLastAddedItemBehavior - 我倾向于保持美国的拼写,因为它微软的拼写匹配。我认为SeekLastItem听起来像它会滚动到列表中的最后一个项目,而不是最后添加的项目

+0

只要看一下,它似乎并不像'ListBox'知道'ItemsSource'的变化(在Silverlight中),所以它可能无法写出一个通用的行为。有一个'ScrollIntoView'方法需要一个对象 - 设置SelectedItem并调用ScrollIntoView可能会做到这一点,但在不知道'ItemsSource'变化的时候,钩住列表更改事件会很困难! – Charleh 2013-03-08 14:15:47

+0

那么有人设法通过创建一个绑定来观察属性的变化 - 我坚持在那里的行为的实现。它应该工作,但我没有测试它。 – Charleh 2013-03-08 14:28:27

1

您可以使用GetView()引用视图模型中的视图。这也加上了观点和观点模型。

var myView = GetView() as MyView; 
myView.MyListBox.DoStuff 

另一种选择是创建一个行为。 This是如何使用行为从视图模型中扩展TreeView的示例。这同样适用于ListBox

+1

让ViewModel操纵视图并不总是一个好主意 - 您将如何区分“ViewModel”的单元测试? *最好的答案是创建一个Derek指出的行为。 - 行为在View中保留,这将显示问题保留在正确的层次上。 – EtherDragon 2013-03-07 22:45:47

+0

我同意,这就是为什么我通常使用行为或实现IResult,但仍然从vm引用视图是一个选项。这是一种方便,快速和肮脏的方式来做一个概念证明,以后可以转移到更多的解耦选项。 – 2013-03-08 17:51:59

+0

我在之前的评论中提到了IResult,在这个特殊情况下它不起作用,所以我没有将它包含在答案中。由于传递的ActionExecutionContext,IResult在某些情况下非常方便。 – 2013-03-08 17:55:55

1

实际上,有一个更简单的方法来实现这一点,没有任何上述。然后

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>