2013-03-07 113 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>