2011-10-05 65 views
5

我看到這個例子 - 在本例中Binding.UpdateSourceTrigger Property設置UpdateSourceTrigger以明確在的ShowDialog(WPF MVVM)

的UpdateSourceTrigger設置爲Explicit,然後在視圖代碼調用他到TextBox名UpdateSource。

但是,如果我使用MVVM dp我不想讓我的控件的名稱和源屬性在虛擬機中,而不是在視圖中,那麼什麼是正確的方式來綁定控件到VM屬性,並將UpdateSourceTrigger設置爲顯式?

我想在我的情況下,它的ShowDialog窗口,因爲這樣做,我想,源將更新只有當用戶點擊「確定」

提前感謝!

回答

9

如果您正在使用MVVM,那麼您的確定按鈕單擊必須由一些Command來處理。該命令必須來自您的ViewModel。綁定屬性Expliticly必須來自ViewModel。所以什麼阻止你。

  1. 請勿使用Explicit綁定,而是使用OneWay綁定。
  2. 在您按鈕中,綁定命令並將命令參數綁定到綁定的依賴項屬性的OneWay
  3. 在您的Command的Execute處理程序(必須是ViewModel中的某個方法)中,使用參數來更改ViewModel的屬性。
  4. ViewModel提高該房產的NotifyPropertyChanged

例如,

假設我需要將一個TextBox的文本更新到我的模型上,單擊OK按鈕。

因此,我有一個EmployeeViewModel類具有EmployeeName屬性。該物業有一個吸氣和一個二傳手。 setter引發屬性更改通知。視圖模型還有ICommand的另一個屬性,名爲SaveNameCommand,它返回一個命令讓我執行。

EmployeeViewModel是我的視圖的數據上下文類型。 Myview有一個TextBox(命名爲x:Name =「EmployeeNameTxBx」)OneWay綁定到EmployeeName和一個按鈕作爲OK。我將Button.Command屬性綁定到EmployeeViewModel.SaveNameCommand屬性,並將Button.CommandParameter綁定到EmployeeNameTxBx.Text屬性。

 <StackPanel> 
      <TextBox x:Name="EmployeeNameTxBx" 
        Text="{Binding EmployeeName, Mode=OneWay}" /> 
      <Button Content="OK" 
        Command="{Binding SaveNameCommand}" 
        CommandParameter="{Bidning Text, ElementName=EmployeeNameTxBx}" /> 
     </StackPanel> 

裏面我EmployeeViewModelOnSaveNameCommandExecute(object param)方法來執行我的SaveNameCommand

在此執行這個代碼...

 var text = (string)param; 
    this.EmployeeName = text; 

這樣,只有OK按鈕點擊,更新文本框的文本回復到模型的EmployeeName財產。

編輯

看着下面您的意見,我看你是想實現一個UI驗證。現在這改變了一點點。

IDataErrorInfo和相關驗證只適用於如果您的輸入控件(如文本框)爲雙向約束。是的,它是如何打算的。所以現在你可能會問:「如果我們使用IDataErrorInfo,這是否意味着不允許無效數據傳遞給模型的整個概念在MVVM中是徒勞的?」其實不是!

請參閱MVVM不強制執行一條規則,即只有有效的數據應該返回。它接受無效數據,這就是IDataErrorInfo的工作原理,並提出錯誤通知。關鍵是ViewModel只是你的視圖的軟拷貝所以它可以是。它應該確保這種骯髒不是承諾到您的外部接口,如服務或數據庫。

這樣的無效數據流應該通過測試無效數據來限制ViewModel。如果我們啓用了TwoWay綁定,那麼數據就會出現。所以考慮到你正在實現IDataErrorInfo,那麼你需要有TwoWay綁定,這是MVVM完全允許的。

方法1:

如果我婉明確確認上按鈕UI某些項目點擊?

爲此使用延遲驗證技巧。在你的ViewModel中有一個叫做isValidating的標誌。默認設置爲false。

在你IDataErrorInfo.this財產通過檢查isValidating標誌跳過驗證...

string IDataErrorInfo.this[string columnName] 
    { 
     get 
     { 
     if (!isValidating) return string.Empty; 

     string result = string.Empty; 
     bool value = false; 

     if (columnName == "EmployeeName") 
     { 
      if (string.IsNullOrEmpty(AccountType)) 
      { 
       result = "EmployeeName cannot be empty!"; 
       value = true; 
      } 
     } 
     return result; 
     } 
    } 

然後在執行處理您的OK命令,檢查員工的名稱,然後提高屬性更改通知事件相同的屬性...

private void OnSaveNameCommandExecute(object param) 
    { 
     isValidating = true; 
     this.NotifyPropertyChanged("EmployeeName"); 
     isValidating = false; 
    } 

這會在您單擊確定時觸發驗證。請記住EmployeeName必須包含用於驗證才能工作的無效數據。

方法2:

如果我想在MVVM明確地更新沒有雙向模式的綁定?

然後你將不得不使用Attached Behavior。該行爲將附加到「確定」按鈕,並將接受需要刷新其綁定的所有項目的列表。

 <Button Content="OK"> 
      <local:SpecialBindingBehavior.DependentControls> 
       <MultiBinding Converter="{StaticResource ListMaker}"> 
        <Binding ElementName="EmployeeNameTxBx" /> 
        <Binding ElementName="EmployeeSalaryTxBx" /> 
        .... 
       <MultiBinding> 
      </local:SpecialBindingBehavior.DependentControls> 
     </Button> 

ListMakerIMultiValueConverter簡單地轉換值成列表...

 Convert(object[] values, ...) 
     { 
      return values.ToList(); 
     } 

在你SpecialBindingBehaviorDependentControls屬性更改處理器...

 private static void OnDependentControlsChanged(
      DependencyObject depObj, 
      DependencyPropertyChangedEventArgs e) 
     { 
      var button = sender as Button; 
      if (button != null && e.NewValue is IList) 
      { 
       button.Click 
        += new RoutedEventHandler(
         (object s, RoutedEventArgs args) => 
         { 
           foreach(var element in (IList)e.NewValue) 
           { 
           var bndExp 
            = ((TextBox)element).GetBindingExpression(
             ((TextBox)element).Textproperty); 

           bndExp.UpdateSource(); 
           } 
         }); 
      } 
     } 

但我還是建議你使用基於我以前純MVVM **方法1

+0

首先感謝您的詳細解答!並回到我的問題,我使用純mvvm,我有我的虛擬機命令,我使用NotifyPropertyChanged。但如果我使用OneWay綁定,並在窗口10文本框中它的意思是我需要發送命令參數10個元素名稱?然後手動更新它們?有沒有辦法使用ToWay綁定? – Maya

+0

也現在我使用IDataError來驗證文本框中的文本,如果我將使用OneWay綁定,我仍然可以驗證文本? – Maya

+0

@Maya請看我上面編輯過的naswer。 –

1

這是一個老問題,但我仍然想爲其他用戶提供另一種方法,他們偶然發現這個問題... 在我的viewmodels中,我不直接在get/set Property方法中公開模型屬性。我爲所有屬性使用內部變量。然後我將所有屬性雙向綁定。所以我可以將所有的驗證都做成「平常」,因爲只有內部變量發生了變化。在視圖模型構造函數中,我將模型對象作爲參數,並將內部變量設置爲我的模型的值。現在當我點擊「保存」按鈕( - >保存命令在我的視圖模型中觸發)並且沒有錯誤時,我將模型的所有屬性設置爲相應內部變量的值。如果我點擊「Canel/Undo」-Button( - > Cancel-Command在我的視圖模型中觸發),我將內部變量設置爲未觸摸模型的值(使用視圖模型屬性的setter,以便NotifyPropertyChanged爲調用並且視圖顯示更改=舊值)。

另一種方法是在模型中實現Memento-Support,所以在開始編輯之前,您需要調用模型中的函數來保存當前值,如果取消編輯,則調用函數來恢復這些值。 ..這樣,你將有撤消/取消支持無處不在一個視圖模型... 我已經在不同的項目中實施了兩種方法,都工作正常,這取決於項目的要求...