[英]How to handle the SelectionChanged event of ComboBox with MVVM in wpf?
[英]Handling a ComboBox Selection using WPF,MVVM, and the SelectionChanged Event
我有一個組合框。 當用戶嘗試更改 ComboBox 上的 selectedItem 時,我希望彈出一個 MessageBox 並確認他們是否要更改他們的選擇。 如果不是,則選擇不應更改。 這是我最初擁有的:
選擇站點視圖 (XAML):
<ComboBox Name="SiteSelectionComboBox"
ItemsSource="{Binding SiteList}"
SelectedItem="{Binding SelectedSite}"/>
選擇站點視圖模型:
private List<string> siteList;
public List<string> SiteList
{
get { return siteList; }
set { siteList = value;
OnPropertyChanged();
}
}
private string selectedSite;
public string SelectedSite
{
get { return selectedSite; }
set {
if (CanChangeSite())
{
selectedSite = value;
OnPropertyChanged();
}
//if false, I DONT want the combobox to change it's selection.
//What I end up getting is a mismatch. The SelectedSite never gets updated to the new 'value', but the ComboBox still changes
}
}
private bool CanChangeSite()
{
MessageBoxResult result = MessageBox.Show("Are you sure?", "WARNING", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
return true;
else return false;
}
我無法讓它工作。 我猜因為綁定是雙向的,即使我沒有更新我的 SelectedSite 值,ComboBox 仍然會從 UI 更改 SelectedItem 值,導致我的綁定可能不匹配的情況。
然后我繼續嘗試處理 SelectionChanged 事件,但沒有成功。 這是我嘗試實施它的方式:
選擇站點視圖 (XAML):
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
<ComboBox Name="SiteSelectionComboBox"
ItemsSource="{Binding SiteList}"
SelectedItem="{Binding SelectedSite}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
選擇站點視圖模型:
public ICommand SelectionChangedCommand { get; set; }
public SelectSiteViewModel(MainViewModel main)
{
SelectionChangedCommand = new RelayCommand(o => SelectionChangedAction(o));
}
public void SelectionChangedAction(object param)
{
MessageBox.Show("selection changed command activated, param = " + param.ToString());
if (param == null)
MessageBox.Show("NULL");
//MessageBox.Show("Added item: " + param.AddedItems[0]);
//DOESNT WORK
}
我嘗試將我的 ComboBox 選擇事件參數作為參數傳遞給 ViewModel。 當我打印它的 ToString 值時,我得到:“System.Windows.Controls.SelectionChangedEventArgs”,在調試器上,我可以看到它具有我感興趣的“AddedItems”和“RemovedItems”屬性。但是,我不能不要訪問這些項目。 是因為對象沒有正確傳遞嗎? 我將如何訪問這些項目以便我可以拒絕組合框中的更改? 使用此事件處理程序是執行此操作的最佳方法嗎?
我看到了一個使用 MVVMLite 的解決方案,如果可能的話,我寧願不使用任何額外的依賴項。 提前致謝!
您不想在 MVVM 應用程序的視圖模型中處理 UI 事件。
這是一個經典的屬性驗證場景。
您必須通過實施INotifyDataErrorInfo
來驗證該屬性。 然后您的視圖模型將忽略無效的屬性值,例如當INotifyDataErrorInfo.HasErrors
返回 true 或當INotifyDataErrorInfo.GetErrors
為特定屬性返回錯誤時,視圖模型知道它處於無效狀態。
發出無效屬性值的信號將自動(通過綁定引擎)觸發視圖向用戶顯示錯誤指示器。
在 GUI 中強制值不是推薦的解決方案,因為這對用戶來說就像黑魔法一樣。 從他的角度來看,他的選擇會神奇地變成其他東西,可能無法識別。
在這種情況下,最好指示無效選擇並讓用戶明確更改它。
甚至更好:通過禁用(灰色)或過濾無效選項來禁止無效輸入。 這樣用戶只能選擇有效的項目。 這是最簡潔的解決方案,可以避免顯示輸入錯誤,從而提供流暢流暢(不間斷)的用戶體驗。
我建議從 UI 中過濾無效的輸入選項,因為這是最優雅的解決方案。 它以多種方式改善了用戶體驗。 它不會中斷流程,也不會用無效或禁用的元素使 UI 混亂。
如果您想強制綁定目標拒絕無效值,您可以從控件(需要擴展控件或將相關邏輯作為附加行為實現)或從視圖模型執行此操作。
重要提示:由於您要求的是 MVVM 解決方案,因此以下所有示例(第一個和最后一個除外)都希望視圖模型實現INotifyDataErrorInfo
(或一般的綁定驗證)。
這些示例也適用於視圖級綁定驗證( Binding.ValidationRules
,您應該在 MVVM 場景中盡量避免這種情況)。
此解決方案通常不需要INotifyDataErrorInfo
或綁定驗證。 它不允許任何錯誤。 因此,不需要錯誤反饋。
因為在 WPF 中,綁定引擎會自動綁定到任何集合的ICollectionView
,我們可以簡單地使用默認的ICollectionView
來過濾無效的項目,以防止它們出現在 UI 中。 以這種方式過濾不會修改集合本身:
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
public ObservableCollection<object> SourceCollection { get; set; }
public MainViewModel()
{
this.SourceCollection = new ObservableCollection<object>();
ICollectionView collectionView = CollectionViewSource.GetDefaultView(this.SourceCollection);
collectionView.Filter = IsValueValid;
}
// Return true if the item should appear in the collection view
private bool IsValueValid(object itemToValidate) => true;
}
您將在依賴屬性更改回調中實現邏輯。
對於ComboBox
(或任何Selector
),我們可以通過覆蓋處理程序來處理Selector.SelectionChanged
事件:
自定義組合框.cs
public class CustomComboBox : ComboBox
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
BindingExpression selectedItemPropertyBinding = GetBindingExpression(Selector.SelectedItemProperty);
var isSelectedItemInvalid = selectedItemPropertyBinding.HasValidationError;
if (isSelectedItemInvalid)
{
this.SelectedItem = e.RemovedItems[0];
}
}
}
該示例將在代碼隱藏中處理ComboBox
的Selector.SelectionChanged
事件:
主窗口.xaml.cs
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
BindingExpression selectedItemPropertyBinding = comboBox.GetBindingExpression(Selector.SelectedItemProperty);
if (selectedItemPropertyBinding.HasValidationError)
{
comboBox.SelectedItem = e.RemovedItems[0];
}
}
此解決方案通常不需要INotifyDataErrorInfo
或綁定驗證。 它不允許任何錯誤。 因此,不需要錯誤反饋。
驗證綁定到Selector.SelectedItem
屬性的源屬性后,如果驗證失敗,視圖模型類將覆蓋無效值。
除了設計決策(尤其是用戶體驗設計)之外,僅從所需的代碼中,您就可以看出這不是最優雅的方式。:
主視圖模型
private object selectedDataItem;
public object SelectedDataItem
{
get => this.selectedDataItem;
set
{
if (value == this.SelectedDataItem)
{
return;
}
var oldItem = this.selectedDataItem;
this.selectedDataItem = value;
OnPropertyChanged();
if (IsValueValid(value))
{
// Defer overriding the property value because we must leave the scope
// to allow the binding to complete the current transaction.
// Then we can start a new to actually override the current value.
// This example uses a background thread to allow the current context to leave the scope.
// Note: you can use Dispatcher.InvokeAsync (do not await it) to achieve the same. But you probably want to avoid tight coupling to the Singleton.
Task.Run(() => this.SelectedDataItem = oldItem);
}
}
}
或者,通過將Binding.IsAsync
設置為true
將Binding
配置為異步執行。
這樣就不需要后台線程或Dispatcher.InvokeAsync
來“欺騙” Binding
流程:
<ComboBox SelectedItem="{Binding SelectedDataItem, IsAsync=True}" />
private object selectedDataItem;
public object SelectedDataItem
{
get => this.selectedDataItem;
set
{
if (value == this.SelectedDataItem)
{
return;
}
var oldItem = this.selectedDataItem;
this.selectedDataItem = value;
OnPropertyChanged();
if (IsValueValid(value))
{
// Because the Binding.IsAsync is set to true,
// the Binding can continue to leave the scope while the property executes.
// This allows the second PropertyChanged to trigger the Binding to update as expected.
this.SelectedDataItem = oldItem;
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.