2011-08-01 18 views
14

Chcę umożliwić użytkownikowi edytować niektóre dane w DataGrid WPF (z .NET Framework 4.0). Kolumna "Instrumenty" powinna umożliwiać użytkownikowi wybór dostępnego dokumentu z listy statycznej lub napisanie dowolnego tekstu. Moja DataGrid jest powiązana z danymi przy użyciu MVVM. Próbowałem wielu rozwiązań, które znalazłem w Internecie, ale żaden z nich nie działa poprawnie. Oto mój kod:Jak zaimplementować edytowalne DataGridComboBoxColumn w WPF DataGrid

<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> 

rozwijana-lista jest pokazany poprawnie. Pole można edytować dowolnym tekstem, ale ustawia ono wartość null w Selected Instrument po zamknięciu menu rozwijanego dla dowolnego tekstu. Działa tylko dla wybranego elementu. Próbowałem zmienić na SelectedValueBinding, ale to nie pomaga.

Jak właściwie wdrożyć te wymagania? Czy ktoś może zamieścić tutaj roboczą próbkę?

dodatkowe: zamówienia jest ObservableCollection Zamówienie ma obiekt, jak tytuł String, DateTime zamówiony, strun SelectedInstrument, Instruments jest string []

Solutions: obserwuję sugerować jako obejście od bathineni prac :

<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> 
+0

myślę w roztworze należy zastąpić ' Neil

+0

tak, to jest literówka –

Odpowiedz

13

tak się dzieje, ponieważ dowolny tekst, który ma wejść jest ciągiem typu i wybraną pozycją, która jest powiązana z kombiBox, jest złożonego typu ...

zamiast używać DataGridComboBoxColumn użyj DataGridTemplateColumn i możesz powiązać właściwość comboBox z pewną właściwością, która będzie utrzymywać wartość wolnego tekstu po zamknięciu listy rozwijanej.

można uzyskać lepszy pomysł, patrząc na następujący przykład.

<DataGrid> 
    <DataGrid.Columns> 
     <DataGridTemplateColumn> 
      <DataGridTemplateColumn.CellTemplate> 
       <DataTemplate> 
        <ComboBox IsEditable="True" 
           Text="{Binding NewItem}" 
           ItemsSource="{Binding Sourcelist.Files}"/> 
       </DataTemplate> 
      </DataGridTemplateColumn.CellTemplate> 
     </DataGridTemplateColumn> 
    </DataGrid.Columns> 
</DataGrid> 
+0

Dziękuję, działa dobrze, jeśli dodaję również

+4

To jest okropnym doświadczeniem dla użytkownika. Musisz raz wejść do tej kolumny, a następnie po raz drugi do combobox. Wszystkie inne kolumny w datagrid wymagają tylko jednej karty, ta wymaga dwukrotnego naciśnięcia tabulatora. – Nick

5

Spróbuj użyć tylko wybranej wartości, ale wraz z nią użyj DisplayMe mberPath i TextSearch.TextPath.

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

Do edycji comboboxes musimy synchronizować co wartość combo wybiera, co wartość na wyświetlaczu wartość przedmiotów i co musimy wyszukiwania w oparciu o dane wprowadzone przez użytkownika.

ale jeśli używasz kolekcji ciąg związać swoją combobox to można spróbować następujących ...

  1. Dodaj nową właściwość w ViewModel o nazwie InstrumentsView. Zwraca nowy ListCollectionView.

    public static string ListCollectionView InstrumentsView 
    { 
         get 
         { 
           return new ListCollectionView(Instruments); 
         } 
    } 
    
  2. Zmień DataGridComboBoxColumn XAML jak poniżej ...

    <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> 
    

Powiedz mi, czy to działa ....

+0

Czy chodziło Ci zmienić DataGridComboBoxColumn.EditingElementStyle lub użyć DataGridTemplateColumn zamiast DataGridComboBoxColumn? A mój obiekt nie ma żadnych właściwości podrzędnych, takich jak MyValueProperty, ponieważ jest to ciąg znaków. –

+0

Proszę zobaczyć moją zredagowaną odpowiedź powyżej z nowym możliwym rozwiązaniem do zbierania ciągów. –

+0

If SelectedInstrument {get {return InstrumentView.CurrentItem; }} jak mogę zaimplementować zestaw, aby zainicjować widok początkowy? CurrentItem jest tylko do odczytu. –

2

Można utworzyć własny typ kolumny ComboBox przez instacji DataGridBoundColumn.W porównaniu do rozwiązania podrzędnego dla bathineni: DataGridTemplateColumn poniższe rozwiązanie ma lepszą wygodę użytkowania (bez podwójnego zakładania) i masz więcej opcji dostosowywania kolumny do swoich potrzeb.

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; 
    } 
} 

Można następnie użyć komponentu na przykład w ten sposób.

<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> 

mam z tego rozwiązania, wykonując this answer z podobnym pytaniem.