2012-09-28 12 views
5

我正在使用標準wpf/mvvm應用程序,其中將組合框綁定到ViewModel上的集合。取消選擇MVVM中的ComboBoxItems

我需要能夠從下拉菜單中取消選擇一個項目。意思是說,用戶應該能夠選擇一些東西,然後決定他們想取消選擇它(不選擇)。問題是在我的綁定集合中沒有空元素

我最初的想法只是在集合中插入一個新項目,這會導致在集合頂部有一個空項目。

雖然這是一種黑客行爲,但它會影響在視圖模型上使用該集合的所有代碼。

例如,如果有人在寫

_myCollection.Frist(o => o.Name == "foo") 

這將拋出一個空引用異常。

可能的解決辦法是:

_myCollection.Where(o => o != null).First(o => o.Name == "foo"); 

這會工作,但沒有辦法,以確保任何未來該集合的使用不會造成任何中斷。

什麼是一個好的模式/解決方案能夠添加一個空的項目,以便用戶可以取消選擇。 (我也知道的CollectionView的結構,但是,似乎是一個矯枉過正這麼簡單的東西)

更新

去與@hbarck建議和實施CompositeCollection(概念的快速證明)

public CompositeCollection MyObjects { 
     get { 
      var col = new CompositeCollection(); 

      var cc1 = new CollectionContainer(); 
      cc1.Collection = _actualCollection; 

      var cc2 = new CollectionContainer(); 
      cc2.Collection = new List<MyObject>() { null }; // PROBLEM 

      col.Add(cc2); 
      col.Add(cc1); 
      return col; 
     } 
    } 

這段代碼適用於現有的綁定(包括SelectedItem),這很好。與此

的一個問題是,如果該項目完全是空的的SelectedItem二傳手從未在選擇它調用。

如果我修改一行到這一點:

  cc2.Collection = new List<MyObject>() { new MyObject() }; // PROBLEM 

設置器調用,但現在我的選擇的產品只是一個基本的初始化類,而不是空..我可以在二傳手添加一些代碼檢查/重置,但這並不好。

+2

你可以讓通過單擊文本菜單一個菜單項,用戶去選擇一個項目的優勢。 –

+0

右鍵點擊下拉菜單?這並不夠直觀.. –

+0

我可以忍受這一點。^^ Combobox甚至不會打開,如果你右鍵點擊它,所以它不會是用戶不友好的。 –

回答

3

我認爲最簡單的方法是使用CompositeCollection。只需將您的集合附加到僅包含空項目(空或佔位符對象,無論您需要的套件)的另一個集合中,並將CompositeCollection作爲ComboBox的ItemsSource。這可能是它的目的。

更新:

這原來是更復雜,比我首先想到的,但實際上,我想出了這個解決方案:

<Window x:Class="ComboBoxFallbackValue" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:t="clr-namespace:TestWpfDataBinding" 
    xmlns:s="clr-namespace:System;assembly=mscorlib" 
    xmlns:w="clr-namespace:System.Windows;assembly=WindowsBase" 
Title="ComboBoxFallbackValue" Height="300" Width="300"> 
<Window.Resources> 
    <t:TestCollection x:Key="test"/> 
    <CompositeCollection x:Key="MyItemsSource"> 
     <x:Static Member="t:TestCollection.NullItem"/> 
     <CollectionContainer Collection="{Binding Source={StaticResource test}}"/> 
    </CompositeCollection> 
    <t:TestModel x:Key="model"/> 
    <t:NullItemConverter x:Key="nullItemConverter"/> 
</Window.Resources> 
<StackPanel> 
    <ComboBox x:Name="cbox" ItemsSource="{Binding Source={StaticResource MyItemsSource}}" IsEditable="true" IsReadOnly="True" Text="Select an Option" SelectedItem="{Binding Source={StaticResource model}, Path=TestItem, Converter={StaticResource nullItemConverter}, ConverterParameter={x:Static t:TestCollection.NullItem}}"/> 
    <TextBlock Text="{Binding Source={StaticResource model}, Path=TestItem, TargetNullValue='Testitem is null'}"/> 
</StackPanel> 

基本上,格局您聲明您用作項目的類的單例NullInstance,並使用Converter在將VM屬性設置爲null時將此實例轉換爲null。該轉換器可普遍寫的,像這樣的(這是VB,我希望你不介意):

Public Class NullItemConverter 
Implements IValueConverter 

Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert 
    If value Is Nothing Then 
     Return parameter 
    Else 
     Return value 
    End If 
End Function 

Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack 
    If value Is parameter Then 
     Return Nothing 
    Else 
     Return value 
    End If 
End Function 

末級

既然你可以重用轉換器,你可以在XAML處理這件事了;代碼中唯一需要完成的是提供單例NullItem。

+0

我喜歡這個。我唯一擔心的是,我使用SelectedItem綁定..它需要是原始集合中的相同項目(未克隆),以使選擇正常工作。 –

+0

如果您將「未選定」項目設爲null,我認爲這不會成爲問題。就我所知,這些項目不會被克隆,因此SelectedItem將會是一個集合成員或爲null。 – hbarck

+1

實現了CompositeCollection,但遇到了有趣的WPF行爲..(更新了我的問題) –

0

一種選擇是創建一個適配器集合,專門爲需要初始「空」元素的消費者公開的適配器集合。您需要創建一個實現IList的包裝類(如果您需要與ObservableCollection相同的性能)和INotifyCollectionChanged。您需要在包裝的集合上收聽INotifyCollectionChanged,然後重新播放索引向上移動一個的事件。所有相關的列表方法也需要將索引移動一位。如果你想避免的IList方法

public sealed class FirstEmptyAdapter<T> : IList<T>, IList, INotifyCollectionChanged 
{ 
    public FirstEmptyCollection(ObservableCollection<T> wrapped) 
    { 
    } 

    //Lots of adapter code goes here... 
} 

最低配置是實現INotifyCollectionChanged和IEnumerable<T>

1

您也可以擴展ComboBox以啓用取消選擇。添加一個或多個掛鉤(例如,按下退出鍵),允許用戶將SelectedItem設置爲空。

using System.Windows.Input; 

public class NullableComboBox : ComboBox 
{ 
    public NullableComboBox() 
     : base() 
    { 
     this.KeyUp += new KeyEventHandler(NullableComboBox_KeyUp); 

     var menuItem = new MenuItem(); 
     menuItem.Header = "Remove selection"; 
     menuItem.Command = new DelegateCommand(() => { this.SelectedItem = null; }); 
     this.ContextMenu = new ContextMenu(); 
     this.ContextMenu.Items.Add(menuItem); 
    } 

    void NullableComboBox_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) 
    { 
     if (e.Key == Key.Escape || e.Key == Key.Delete) 
     { 
      this.SelectedItem = null; 
     } 
    } 
} 

編輯只注意到弗洛裏安GI的評論,上下文菜單可能是另一個很好的取消掛鉤的補充。

+0

我從哪裏得到DelegateCommand?你如何在AXML而不是ComboBox中使用它? – theAlse

+0

@theAlse例如:https://code.google.com/p/moltenmonkey/source/browse/Source/mmonkey/Commands/DelegateCommand.cs?spec = svneb38ee7524ec4ec9cf2520ddee47ad303396e389&r = eb38ee7524ec4ec9cf2520ddee47ad303396e389 – McGarnagle

+0

使用'Command',我在哪裏得到的? – theAlse

2

就我個人而言,我傾向於在我綁定的集合中添加任何對象的「空白」版本。因此,例如,如果您綁定到字符串列表,那麼在viewmodel中,在集合的開頭插入一個空字符串。如果你的模型有數據收集,那麼在你的視圖模型中用另一個集合包裝它。

MODEL:

public class Foo 
{ 
    public List<string> MyList { get; set;} 
} 

視圖模型:

public class FooVM 
{ 
    private readonly Foo _fooModel ; 

    private readonly ObservableCollection<string> _col; 
    public ObservableCollection<string> Col // Binds to the combobox as ItemsSource 
    { 
     get { return _col; } 
    } 

    public string SelectedString { get; set; } // Binds to the view 

    public FooVM(Foo model) 
    { 
     _fooModel = model; 
     _col= new ObservableCollection<string>(_fooModel.MyList); 
     _col.Insert(0, string.Empty); 
    } 
} 
0

一個簡單的方法是重新模板組合框,這樣,當有一個項目選擇出現在右側的小X盒子。點擊即可清除選定的項目。

這有沒有讓你的ViewModels任何更復雜

+0

也許使用Adorner? – McGarnagle