2011-08-26 59 views
2

我有一個MVVM應用程序和應用程序中的某個地方,我們公司使用不能使用{綁定}的第三方。這是一個繪製形狀的組件,等等。我想要的是,當ViewModel從持久存儲裝載所有形狀時通知View來繪製它們。在一個完美的世界中,我只需把第三方綁定到ViewModel Shapes集合上,但我不能。Silverlight與MVVM:如何從視圖訪問ViewModel的事件?

從那裏,我的想法是,我可以從查看ViewModel(通過DataContext)並鉤住PropertyChanged事件。問題是DataContext尚未在構造函數中初始化,所以它是NULL,並且我無法掛接事件。下面是代碼的樣本:

 public CanvasView() 
     { 
      InitializeComponent(); 
      ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged); //Exception Throw here because DataContext is null 
     } 

     void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e) 
     { 
      if (e.PropertyName == "Shapes") 
      { 
       DrawShapes(); 
      } 
     } 

如何,我可以從我的ViewModel信息,我在這種情況下查看?

回答

4

到目前爲止所有的答案都在視圖上隱藏了代碼隱藏的情況下打破了MVVM模式。就我個人而言,我會將第三方控件包裝在UserControl中,然後用屬性更改事件連接一些依賴項屬性。

C#

public partial class MyWrappedControl : UserControl 
{ 
    public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register("Shapes", typeof(ObservableCollection<IShape>), typeof(MyWrappedControl), 
     new PropertyMetadata(null, MyWrappedControl.OnShapesPropertyChanged); 

    public ObservableCollection<IShape> Shapes 
    { 
    get { return (ObservableCollection<IShape>)GetValue(ShapesProperty); } 
    set { SetValue(ShapesProperty, value); } 
    } 

    private static void OnShapesPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
    ((MyWrappedControl)o).OnShapesPropertyChanged(e); 
    } 

    private void OnShapesPropertyChanged(DependencyPropertyChangedEventArgs e) 
    { 
    // Do stuff, e.g. shapeDrawer.DrawShapes(); 
    } 
} 

XAML

<UserControl 
    Name="MyWrappedControl" 
    x:Class="MyWrappedControl" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> 
    <!-- your control --> 
    <shapeDrawerControl x:Name="shapeDrawer" /> 
</UserControl> 
+0

我會測試星期一。 Thx –

+0

因此,而不是在ViewController中具有Shapes集合我將擁有UserControl內的集合嗎? –

+0

沒有。抱歉,我的答案昨晚匆匆寫完了。 ...您需要在* viewmodel *中擁有* shapes *集合,以綁定到用戶控件上的依賴項屬性。 usercontrol是一個包裝器,它使MVVM中的非綁定友好控件可用。 – Dennis

0

使用上查看DataContextChanged僅事件(窗口或用戶控件)

public CanvasView() 
    { 
     InitializeComponent(); 
     Action wireDataContext += new Action (() => { 
      if (DataContext!=null) 
         ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged); 
      }); 
     this.DataContextChanged += (_,__) => wireDataContext(); 
     wireDataContext(); 
    } 

    void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName == "Shapes") 
     { 
      DrawShapes(); 
     } 
    } 

更新:這裏是你還可以附上您的處理程序的記錄方式Silverlight 3中得到DataContextChanged僅4 http://www.lhotka.net/weblog/AddingDataContextChangedInSilverlight.aspx

+0

DataContextChanged僅將可從Silverlight的五隻有... –

+0

對不起。我不知道你不在SL5上。 – jrwren

2

Loaded事件。

public CanvasView() 
{ 
    InitializeComponent(); 
    this.Loaded += this.ViewLoaded; 
} 

void ViewLoaded(object sender, PropertyChangedEventArgs e) 
{ 
    ((CanvasViewModel)this.DataContext).PropertyChanged += new PropertyChangedEventHandler(CanvasView_PropertyChanged);  
} 

void CanvasView_PropertyChanged(object sender, PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "Shapes") 
    { 
     DrawShapes(); 
    } 
} 
+0

-1。這打破了MVVM模式和一些* hackish *。 – Dennis

+0

是的,但它遵循OP的代碼。它的工作原理。有時你必須對你的模式進行例外處理,尤其是在處理第三方控件/庫時。 –

0

我要評論丹尼斯羅氏答案。 真的,在這種情況下,我們可以使用wrap方法,因爲當Shapes集合發生更改時,我們需要重繪視圖。但是視圖模型邏輯可能太複雜,例如,而不是在PropertyChanged上重繪,我們應該重繪一些自定義事件(f.i.ModelReloadEvent)。在這種情況下,包裝並沒有幫助,但是對此事件的訂閱確實如在Muad'Dib解決方案 - 視圖模型中使用基於事件的視圖通信,但此事件應該是視圖特定的。

使用代碼隱藏與查看特定邏輯不會中斷MVVM。是的,這個代碼可以用行爲/動作來修飾,但是使用後面的代碼 - 只是簡單的解決方案。

另外,看看這個view on MVVM。根據結構,ViewModel知道抽象的IView。如果你使用Caliburn/Caliburn.Micro MVVM框架,你還記得ViewAware類和IViewAware,它允許在視圖模型中獲得視圖。

所以,更靈活的解決方案,我的猜測是下一個:

查看:

public class CanvasView() : ICanvasView 
{ 
     public CanvasView() 
     { 
      InitializeComponent(); 
     } 

     public void DrawShapes() 
     { 
      // implementation 
     } 
} 

ICanvasView:

public interface ICanvasView 
{ 
    void DrawShapes(); 
} 

CanvasViewModel:

public class CanvasViewModel : ViewAware 
{  
    private ObservableCollection<IShape> _shapes; 
    public ObservableCollection<IShape> Shapes 
    { 
     get 
     { 
      return _shapes; 
     } 
     set 
     { 
      _shapes = value; 
      NotifyOfPropertyChange(() => Shapes); 
      RedrawView(); 
     } 
    } 

    private void RedrawView() 
    { 
     ICanvasView abstractView = (ICanvasView)GetView(); 
     abstractView.DrawShapes(); 
    } 
} 
+0

我沒有使用Caliburn/Caliburn.Micro MVVM框架,我需要看看我是否可以在沒有它的情況下真正使用GetView()(我懷疑)。 Thx的信息,我會去工作中檢查一下你的信息是否可以做你的信息。 –

相關問題