web-dev-qa-db-ja.com

ComboBoxでnull値を選択できないのはなぜですか?

WPFでは、ComboBoxから「ヌル」値を(マウスで)選択することは不可能のようです。 編集明確にするために、これは.NET 3.5 SP1です。

これが私が何を意味するかを示すためのコードです。まず、C#宣言:

public class Foo
{
    public Bar Bar { get; set; }
}

public class Bar 
{
    public string Name { get; set; }
}

次に、私のWindow1 XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" 
                  DisplayMemberPath="Name" 
                  Height="21" 
                  SelectedItem="{Binding Bar}"
                  />
    </StackPanel>
</Window>

そして最後に、私のWindow1クラス:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        bars.ItemsSource = new ObservableCollection<Bar> 
        {
            null, 
            new Bar { Name = "Hello" }, 
            new Bar { Name = "World" } 
        };
        this.DataContext = new Foo();
    }
}

私と一緒に?項目がBarインスタンスのリストにバインドされているComboBoxがあり、その1つがnullです。ウィンドウをFooのインスタンスにバインドし、ComboBoxがそのBarプロパティの値を表示しています。

このアプリを実行すると、Foo.Barはデフォルトでnullであるため、ComboBoxは空のディスプレイで始まります。それはいいです。マウスを使用してComboBoxをドロップダウンし、「Hello」アイテムを選択すると、それも機能します。しかし、リストの上部にある空のアイテムを再度選択しようとすると、ComboBoxが閉じて、以前の値の「Hello」に戻ります。

矢印キーでnull値を選択すると期待どおりに機能し、プログラムで設定することもできます。機能しないのは、マウスで選択することだけです。

簡単な回避策は、nullを表すBarのインスタンスを作成し、それをIValueConverterで実行することですが、WPFのComboBoxでマウスを使用してnullを選択しても機能しない理由を誰かが説明できますか?

44
Matt Hamilton

ヌル「アイテム」はキーボードでまったく選択されていません-むしろ、前のアイテムは選択されておらず、後続のアイテムは選択されていません(選択可能です)。これは、「選択」した後、キーボードを使用してnull項目を選択すると、その後、以前に選択した項目( "Hello")を再選択できなくなります-マウスを使用する場合を除きます。

つまり、ComboBoxでnullアイテムを選択または選択解除することはできません。そうしていると思われる場合は、以前のアイテムまたは新しいアイテムの選択を解除または選択しています。

これはおそらく、ComboBoxの項目に背景を追加することで最もよくわかります。 「Hello」を選択すると、ComboBoxの背景が色付きになりますが、キーボードで選択を解除すると、背景色が消えます。マウスでリストをドロップすると、nullアイテムは実際には背景色を持つため、これはnullアイテムではないことがわかります。

元の質問のXAMLから変更された次のXAMLは、項目の背後にLightBlue背景を配置して、この動作を確認できるようにします。

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <ComboBox x:Name="bars" Height="21" SelectedItem="{Binding Bar}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <Grid Background="LightBlue" Width="200" Height="20">
                        <TextBlock Text="{Binding Name}" />
                    </Grid>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </StackPanel>
</Window>

さらに検証が必要な場合は、ComboBoxのSelectionChangedイベントを処理して、「nullアイテムを選択」すると、実際にSelectionChangedEventArgsにAddedItemsの空の配列が表示され、「マウスで「Hello」を選択してnullアイテムの選択を解除する」ことがわかります。 RemovedItemsの空の配列を提供します。

15
Tim Erickson

さて、私は最近nullComboBoxの値で同じ問題に遭遇しました。 2つのコンバーターを使用して解決しました。

  1. ItemsSourceプロパティの場合:コレクション内のnull値を、コンバーターのパラメーター内で渡された任意の値に置き換えます。

    class EnumerableNullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var collection = (IEnumerable)value;
    
            return
                collection
                .Cast<object>()
                .Select(x => x ?? parameter)
                .ToArray();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    
  2. SelectedValueプロパティの場合:これは同じことを行いますが、単一の値と2つの方法で行います。

    class NullReplaceConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value ?? parameter;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value.Equals(parameter) ? null : value;
        }
    }
    

使用例:

<ComboBox 
    ItemsSource="{Binding MyValues, Converter={StaticResource EnumerableNullReplaceConverter}, ConverterParameter='(Empty)'}" 
    SelectedValue="{Binding SelectedMyValue, Converter={StaticResource NullReplaceConverter}, ConverterParameter='(Empty)'}"
    />

結果:

enter image description here

注:ObservableCollectionにバインドすると、変更通知が失われます。また、コレクションに複数のnull値を含める必要はありません。

11

この質問に対する新しい解決策を得ました。 「マハプスを使う」

  xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"


  <ComboBox x:Name="bars"  **controls:TextBoxHelper.ClearTextButton="True"**
              DisplayMemberPath="Name" 
              Height="21" 
              SelectedItem="{Binding Bar}"/>

enter image description here

enter image description here

閉じるボタンを使用してコンテンツをクリアできます。

ありがとう。

8
Anurag

私はこの答えがあなたが求めたものではないことを知っていますが(それがマウスで機能しない理由の説明)、私はその前提に欠陥があると思います:

プログラマーおよびユーザー(.NETではない)としての私の観点からは、null値を選択することは悪いことです。 「null」は、選択したものではなく、値がないことを前提としています。

何かを明示的に選択しない機能が必要な場合は、あなたが言及した回避策(値として「-」、「n.a。」、または「none」)、またはそれ以上をお勧めします

  • チェックボックスをオフにしてコンボボックスを無効にできるチェックボックスでコンボボックスをラップします。これは、ユーザーの観点からもプログラム的にも、最もクリーンなデザインだと思います。
5
Galghamon

私は1日を費やして、comboboxでnull値を選択するというこの問題の解決策を見つけました。最後に、ええと、最終的にこのURLに書かれた記事で解決策を見つけました。

http://remyblok.tweakblogs.net/blog/7237/wpf-combo-box-with-empty-item-using-net-4-dynamic-objects.html

public class ComboBoxEmptyItemConverter : IValueConverter 
{ 
/// <summary> 
/// this object is the empty item in the combobox. A dynamic object that 
/// returns null for all property request. 
/// </summary> 
private class EmptyItem : DynamicObject 
{ 
    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
        // just set the result to null and return true 
        result = null; 
        return true; 
    } 
} 

public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    // assume that the value at least inherits from IEnumerable 
    // otherwise we cannot use it. 
    IEnumerable container = value as IEnumerable; 

    if (container != null) 
    { 
        // everything inherits from object, so we can safely create a generic IEnumerable 
        IEnumerable<object> genericContainer = container.OfType<object>(); 
        // create an array with a single EmptyItem object that serves to show en empty line 
        IEnumerable<object> emptyItem = new object[] { new EmptyItem() }; 
        // use Linq to concatenate the two enumerable 
        return emptyItem.Concat(genericContainer); 
    } 

    return value; 
} 

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
{ 
    throw new NotImplementedException(); 
} 

}

 <ComboBox ItemsSource="{Binding  TestObjectCollection, Converter={StaticResource ComboBoxEmptyItemConverter}}" 
      SelectedValue="{Binding SelectedID}" 
      SelectedValuePath="ID" 
      DisplayMemberPath="Name" />
3
John

これはあなたの答えに完全に対処しないかもしれませんが、うまくいけばそれは正しい方向へのヒットです:

  1. SP1をインストールしましたか?

Scott Guのブログから:

  • NET 3.5 SP1には、いくつかのデータバインディングと編集の改善が含まれています
    WPF。これらには以下が含まれます:
  • バインドされた値の簡単なフォーマットを可能にする{{Binding}}式内のStringFormatサポート
  • ItemsControlから派生したコントロール内の新しい交互の行のサポート。これにより、行に交互のプロパティを設定しやすくなります(例:交互の背景色)
  • 編集可能なコントロールでのnull値の処理と変換のサポートの向上バインドされたアイテム全体に検証ルールを適用するアイテムレベルの検証
  • 複数選択および一括編集シナリオを処理するためのMultiSelectorサポート
  • IEditableCollectionViewのサポートにより、データコントロールをデータソースにインターフェースし、アイテムをトランザクション的に編集/追加/削除できるようにします。
  • IEnumerableデータソースにバインドするときのパフォーマンスの向上

私があなたの時間を無駄にして、これが近くさえなかったなら申し訳ありません。しかし、問題は以下から継承されると思います:

強く型付けされたデータセットの制約

NullValueDataSetについてはこちらで説明

しかし今、.Net 3.5のSP1はこの問題に対処しているはずです。

1
Ric Tokyo

ComboBoxは、項目がどれほど単純であっても、項目を表示するためにDataTemplateを必要とします。 DataTemplateは次のように機能します。インスタンスから値を取得します。[パス]。

bar1.Car.Color

したがって、値を取得できません

null.Car.Color

Null参照例外がスローされます。そのため、nullインスタンスは表示されません。ただし、この場合は例外がないため、Color(参照型の場合)はnullにすることができます。

0
redjackwong

同じような問題があり、次のようにコレクションプロパティに値プロパティを追加するなど、いくつかの回避策を実行しました。

 public class Bar

   {
      public string Name { get; set; }
      public Bar Value
      {
         get { return String.IsNullOrEmpty(Name) ?  null :  this; } // you can define here your criteria for being null
      }
   }

次に、nullの代わりに項目を追加するときに、同じオブジェクトを使用します。

  comboBox1.ItemsSource=  new ObservableCollection<Bar> 
        {
            new Bar(),
            new Bar { Name = "Hello" }, 
            new Bar { Name = "World" } 
        };

そして、selecteditemの代わりに、私はそれをselectedvalueにバインドします:

<ComboBox Height="23" Margin="25,40,133,0" DisplayMemberPath="Name"
              SelectedValuePath="Value" 
              SelectedValue="{Binding Bar}"
              Name="comboBox1" VerticalAlignment="Top" />

私はそれが完全な解決策ではなく、私が使用する1つの回避策だけを知っています

0
Dincer Uyav
0
rudigrobler

推測に過ぎませんが、それは妥当なように思えます。

コンボボックスが "ListCollectionView"(インスタンスとしてlcv)をアイテムコレクションとして使用していると想定します。あなたがプログラマーなら、あなたはどうするつもりですか?

キーボードとマウスの両方に対応します。

キーボード入力を受け取ったら、

lcv.MoveCurrentToNext();

または

lcv.MoveCurrentToPrevious();

だから、キーボードがうまく機能することを確認してください。

次に、マウス入力の応答に取り組んでいます。そして、問題が発生します。

  1. アイテムの「MouseClick」イベントを聞きたいのですが。しかし、おそらく、私のアイテムは生成されず、単なるプレースホルダーです。したがって、ユーザーがこのプレースホルダーをクリックしても、何も表示されません。

  2. イベントに成功したら、次は何をしますか。呼び出します

    lcv.MoveCurrentTo(selectedItem);

nullになる "selectedItem"は、ここでは許容できるパラメータではありません。

とにかく、それはただの推測です。デバッグはできますが、デバッグする時間がありません。修正すべき欠陥がたくさんあります。幸運を。 :)

0
redjackwong