26

ユーザーが WPF DataGrid (.net Framework 4.0 から) で一部のデータを編集できるようにしたいと考えています。「楽器」列では、ユーザーが静的リストから使用可能な楽器を選択したり、自由なテキストを書いたりできるようにする必要があります。私の DataGrid は、MVVM を使用してデータにバインドされています。インターネットで見つけた多くの解決策を試しましたが、どれも正しく機能しません。これが私のコードです:

<DataGrid Margin="0,6" ItemsSource="{Binding Path=Orders}" AutoGenerateColumns="False"  CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="True">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Instrument" MinWidth="140"                                      
 ItemsSource="{x:Static ViewModel.Instruments}" SelectedItemBinding="{Binding Path=SelectedInstrument}">
 <DataGridComboBoxColumn.EditingElementStyle>
   <Style TargetType="ComboBox">
     <Setter Property="IsEditable" Value="True"/>
   </Style>                  
 </DataGridComboBoxColumn.EditingElementStyle>                
</DataGridComboBoxColumn>   
</DataGrid.Columns>
</DataGrid>

ドロップダウン リストが正しく表示されます。このフィールドは任意のテキストで編集できますが、フリー テキストのドロップダウンが閉じられると、SelectedInstrument に null が設定されます。選択したアイテムのみに機能します。SelectedValueBinding に変更しようとしましたが、役に立ちません。

この要件を適切に実装するにはどうすればよいですか? 誰かがここに実際のサンプルを投稿できますか?

追加: Orders is ObservableCollection Order has Property has String Title, DateTime Ordered, string SelectedInstrument, Instruments is a string[]

解決策: 次は、bathineni作品からの回避策として提案します。

<DataGrid Margin="0,6" ItemsSource="{Binding Path=Orders}" AutoGenerateColumns="False" CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="True">
 <DataGrid.Columns>
  <DataGridTemplateColumn Header="Instrument" MinWidth="140">
   <DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
     <TextBlock Text="{Binding Path=SelectedInstrument, Mode=OneWay}"/>
    </DataTemplate>
   </DataGridTemplateColumn.CellTemplate>
   <DataGridTemplateColumn.CellEditingTemplate>
    <DataTemplate>
     <ComboBox IsEditable="True" Text="{Binding Path=SelectedInstrument}" 
      ItemsSource="{x:Static ViewModel.Instruments}"/>                   
    </DataTemplate>
   </DataGridTemplateColumn.CellEditingTemplate>
  </DataGridTemplateColumn>   
 </DataGrid.Columns>
</DataGrid>
4

4 に答える 4

19

これは、入力された自由テキストが文字列型であり、comboBox にバインドした選択項目が複雑な型であるために発生しています....

DataGridComboBoxColumnuseを使用する代わりに、comboBox のプロパティを、ドロップダウン リストを閉じた後にフリー テキスト値を保持するプロパティにDataGridTemplateColumnバインドできます。Text

次のサンプルを見ると、より良いアイデアを得ることができます。

<DataGrid>
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox IsEditable="True" 
                              Text="{Binding NewItem}" 
                              ItemsSource="{Binding Sourcelist.Files}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
于 2011-08-01T14:48:16.980 に答える
8

SelectedValue のみを使用するようにしてください。ただし、DisplayMemberPath と TextSearch.TextPath を一緒に使用してください。

   <ComboBox IsEditable="True" DisplayMemberPath="MyDisplayProperty" SelectedValuePath="MyValueProperty" SelectedValue="{Binding MyViewModelValueProperty}" TextSearch.TextPath="MyDisplayProperty" />

編集可能なコンボボックスの場合、コンボが選択する値、アイテムが表示する値、およびユーザー入力に基づいて検索する必要がある値を同期する必要があります。

ただし、文字列コレクションを使用してコンボボックスをバインドしている場合は、次のことを試すことができます...

  1. ViewModel に InstrumentsView という新しいプロパティを追加します。これにより、新しい ListCollectionView が返されます。

    public static string ListCollectionView InstrumentsView
    {
            get
            {
                    return new ListCollectionView(Instruments);
            }
    }
    
  2. 以下のように DataGridComboBoxColumn XAML を変更します...

    <DataGridComboBoxColumn Header="Instrument" MinWidth="140"
                            ItemsSource="{x:Static ViewModel.InstrumentsView}">
            <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="ComboBox">
                            <Setter Property="IsEditable" Value="True"/>
                            <Setter Property="IsSynchronizedWithCurrentItem" Value=True" />
                            <Setter Property="SelectedItem" Value="{Binding SelectedInstrument, Mode=OneWayToSource}" /> <!-- Assuming that SelectedInstrument is string  -->
                    </Style>
            </DataGridComboBoxColumn.EditingElementStyle>
    </DataGridComboBoxColumn>
    

これが機能するかどうか教えてください....

于 2011-08-01T14:25:46.807 に答える
5

をサブクラス化することで、独自の ComboBox 列タイプを作成できますDataGridBoundColumn。サブクラス化の bathineni のソリューションと比較してDataGridTemplateColumn、以下のソリューションにはユーザー エクスペリエンスが向上するという利点があり (ダブルタブを使用しない)、特定のニーズに合わせて列を調整するためのオプションが増えます。

public class DataGridComboBoxColumn : DataGridBoundColumn {
    public Binding ItemsSourceBinding { get; set; }

    protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem) {
        var textBox = new TextBlock();
        BindingOperations.SetBinding(textBox, TextBlock.TextProperty, Binding);
        return textBox;
    }

    protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem) {
        var comboBox = new ComboBox { IsEditable = true };
        BindingOperations.SetBinding(comboBox, ComboBox.TextProperty, Binding);
        BindingOperations.SetBinding(comboBox, ComboBox.ItemsSourceProperty, ItemsSourceBinding);
        return comboBox;
    }

    protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs) {
        var comboBox = editingElement as ComboBox;
        if (comboBox == null) return null;

        comboBox.Focus(); // This solves the double-tabbing problem that Nick mentioned.
        return comboBox.Text;
    }
}

次に、たとえば次のようにコンポーネントを使用できます。

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding MyItems}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
        <local:DataGridComboBoxColumn Header="Thingy" Binding="{Binding Thingy}"
            ItemsSourceBinding="{Binding
                RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}},
                Path=Thingies}"/>
    </DataGrid.Columns>
</DataGrid>

同様の質問に対するこの回答に従うことで、この解決策を得ました。

于 2017-02-14T08:59:58.957 に答える
1

多分それはまだ誰かに役立つでしょう。このソリューションでは、新しく入力した値を選択リストに追加でき、編集中の副作用はありません。

XAML:

<DataGridTemplateColumn Header="MyHeader" Width="Auto">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ComboBox IsEditable="True"
                Text="{Binding MyTextProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                DisplayMemberPath="MyTextProperty"
                SelectedValuePath="MyTextProperty" 
                ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.SelectionList}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

ビューモデル:

public class MyViewModel 
{
    public class MyItem : INotifyPropertyChanged {
        private string myTextProperty;
        public string MyTextProperty {
            get { return myTextProperty; }
            set { myTextProperty = value;
                OnPropertyChanged("MyTextProperty"); }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
        }
    }
    public ObservableCollection<MyItem> MyItems { get; set; }
    public object SelectionList { get; set; }
}

分離コード:

MyWindow.DataContext = MyViewModelInstance;
MyDataGrid.ItemsSource = MyItems;

// Before DataGrid loading and each time after new MyProperty value adding, you must execute:
MyViewModelInstance.SelectionList = MyViewModelInstance.MyItems.OrderBy(p => p.MyTextProperty).GroupBy(p => p.MyTextProperty).ToList();
于 2021-02-04T11:08:58.457 に答える