2012-11-17 35 views
3

我遇到了問題,從我的viewmodel引發的事件有時在視圖中將datacontext顯示爲null。我開始認爲這是一個弱綁定模式問題,並且我沒有使用它,或者誤解它(對於設計模式來說是新的,並且幾個月前啓動了WPF)。在事件期間間歇性地使用DataContext

相關MainWindow.xaml

<Grid Tree:TreeView.ItemSelected="DisplayRequested" 
     Tree:TreeView.PoolCategoriesChanged="PoolContentChanged"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="200"/> 
     <ColumnDefinition Width="Auto" /> 
     <ColumnDefinition /> 
    </Grid.ColumnDefinitions> 
    <GridSplitter Grid.Column="0" VerticalAlignment="Stretch"/> 
    <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" 
        Background="Gray" ShowsPreview="True" Width="5" 
        HorizontalAlignment="Center"/> 
    <GridSplitter Grid.Column="2" VerticalAlignment="Stretch"/> 
    <Tree:TreeView Name="poolTree" Grid.Column="0" Focusable="True" /> 
    <ScrollViewer Grid.Column="2" VerticalScrollBarVisibility="Auto"> 
     <Details:DetailsView Name="detailsDisplay" Focusable="True"/> 
    </ScrollViewer> 
</Grid> 

相關的代碼背後

public partial class MainWindow 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     var vm = (CLUViewModel)e.NewValue; 
     if (vm == null) return; 
     vm.OwnerCleanupStarted += OnOwnerCleanupStarting; 
     vm.OwnerCleanupEnded += OnOwnerCleanupEnding; 
    } 

    #region Event Handlers 
    private void OnOwnerCleanupEnding(object sender, EventArgs e) 
    { 
     ViewServices.CloseProgressDialog(); 
    } 

    private void OnOwnerCleanupStarting(object sender, EventArgs e) 
    { 
     var vm = DataContext as CLUViewModel; 
     if (vm == null) return; 
     var progressDialogViewModel = vm.ProgressDialogVM; 
     ViewServices.ShowProgressDialog(GetWindow(this), progressDialogViewModel); 
    } 
    #endregion 
} 

我有幾個RoutedEvents似乎沒有問題的功能。儘管如此,OnOwnerCleanupStarting事件似乎在var vm = DataContext as CLUViewModel;上返回null。這是因爲它太強烈,而且沒有使用WPF框架?

如果我把它放在調試和跟蹤通過它總是工作,並且在正常使用過程中多次工作沒有錯誤。這是一種競爭條件,我在一個視圖中使用偵聽器,該視圖在焦點到子組件時未初始化?

主叫從VM邏輯:

public class CLUViewModel : ViewModelBase 
{ 
    #region Properties 
    private RelayCommand _manageOwnersDialogCommand; 

    public ProgressDialogViewModel ProgressDialogVM; 
    #endregion 

    public CLUViewModel() 
    { 
     ProgressDialogVM = new ProgressDialogViewModel(string.Empty); 
    } 

    #region ManageOwnersDialogCommand 
    public ICommand ManageOwnersDialogCommand 
    { 
     get 
     { 
      return _manageOwnersDialogCommand ?? 
        (_manageOwnersDialogCommand = new RelayCommand(param => OnManageOwnersDialogShow())); 
     } 
    } 

    private void OnManageOwnersDialogShow() 
    { 
     var dialog = new ManageOwnersDialog(); 
     var vm = new ManageOwnersViewModel(); 
     dialog.DataContext = vm; 

     if (!dialog.ShowDialog().Value) return; 

     var ownersRequiringCleanup = GetOwnersRequiringCleanup(vm); 

     if(ownersRequiringCleanup.Count < 1) return; 

     ProgressDialogVM.ClearViewModel(); 
     ProgressDialogVM.TokenSource = new CancellationTokenSource(); 
     ProgressDialogVM.ProgressMax = ownersRequiringCleanup.Count*2; 

     RaiseOwnerCleanupStartedEvent(); 

     var taskOne = Task.Factory.StartNew(() => OwnerCleanupService.DoOwnerCleanup(ownersRequiringCleanup, ProgressDialogVM)); 

     taskOne.ContinueWith(t => RaiseOwnerCleanupEndedEvent(), TaskScheduler.FromCurrentSynchronizationContext()); 
    } 

    private List<Owner> GetOwnersRequiringCleanup(ManageOwnersViewModel vm) 
    { 
     var ownersRequiringCleanup = new List<Owner>(); 

     // using DB to determine cleanup 
     // Proprietary code removed for this discussion 

     return ownersRequiringCleanup; 
    } 
    #endregion 

    #region Events 
    public event EventHandler OwnerCleanupStarted; 
    public event EventHandler OwnerCleanupEnded; 

    public void RaiseOwnerCleanupStartedEvent() 
    { 
     if (OwnerCleanupStarted == null) return;    
     OwnerCleanupStarted(this, new EventArgs()); 
    } 

    public void RaiseOwnerCleanupEndedEvent() 
    { 
     if (OwnerCleanupEnded == null) return; 
     OwnerCleanupEnded(this, new EventArgs()); 
    } 
    #endregion 
} 

我有,他們的各種虛擬機做出的家長電話(樹形視圖內)等幾個控制這個同樣的問題,即父引發事件。

我一直在學習這一點,而我在VM方面使用的一些事件是我之前對事物工作原理的理解。我應該把這個事件稱爲一個事件,引發到我的觀點,然後開始一個RountedEvent泡泡到適當的水平?我是否陷入強結合與弱結合的陷阱?

編輯:解決父/子TreeView問題被看到。主 - 細節模式意味着我一直在訪問不可見或從未加載過的細節視圖。現在最初的問題仍然是null的發生。有沒有更好的方法從VM回調查看UI相關的問題,View DataContext不會有困難?

+1

CLUViewModel的實例是否引發與您的MainWindow的DataContext相同的實例?如果不是,我會爲其他Window/UserControl創建一個新的ViewModel類,而不是讓多個Views具有相同的ViewModel。如果是,那麼當ViewModel可以非常容易地打開ProgressDialog本身時,我不明白爲什麼你要在ViewModel中的事件視圖中註冊事件處理程序。 –

+0

我提供了一個關於OwnerCleanup事件的蒸餾模型,使它更容易閱讀,並儘可能小,同時仍然會產生錯誤。進度對話框是可以從應用程序中的幾個點使用的東西,我希望它可以由樹上的任何一個孩子統一調用。 –

回答

1

我建議不要使用基於事件的WPF編程模型。因爲這很容易進入這樣的事情。而基於事件的編程會強化緊密耦合。所以它的雙重沒有沒有

而不是在查看拋出事件,使用DelegateCommand執行的視圖模型級別定義的行動,而不是在視圖模型投擲項目中,只要使用正規的屬性和INotifyPropertyChanged代表事物的狀態。

這當然需要從winforms或其他非wpf思維方式進行重要的思維轉換,但是當你意識到這一點時,就會產生更少更簡潔的代碼。

+0

這很有道理。我很想知道很多,但很多網上的例子都顯示了事件,所以它使我對正確性的學習有點困惑。在這一點上,我已經解決了我的應用程序的一切,除了這個null問題。我對這個解決方案進行了一些嘗試,我認爲這會很好地解決我的空數據上下文問題。謝謝! –