2015-10-15 25 views
5

Ho un DataGrid con DataGridTemplateColumn e ComboBox in esso.WPF DataGrid singolo clic per creare un nuovo articolo

<DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Width="*" Header="Test Column"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <ComboBox Width="150" 
            HorizontalAlignment="Left" 
            ItemsSource="{Binding TestChildCollection}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 



public ObservableCollection<TestClass> TestItemCollection { get; set; } = new ObservableCollection<TestClass> 
    { 
     new TestClass(), 
     new TestClass(), 
     new TestClass(), 
    }; 

    public class TestClass 
    { 
     public ObservableCollection<string> TestChildCollection { get; set; } = new ObservableCollection<string> 
     { 
      "First test item", "Second test item" , "Third test item" , "Fourth test item" 
     }; 
    } 

Quando clicco sul ComboBox nella riga vuota che a quanto pare non crea una nuova istanza della mia collezione e dà solo un elenco vuoto.

enter image description here

devo doppio click sul file vuote.

enter image description here

E solo allora vorrei avere dati nel ComboBox.

enter image description here

Come posso ottenere i dati nel Combobox con un solo clic su riga vuota ??

risposta

1

Se avete bisogno di ottenere i dati nel ComboBox con un singolo click sulla riga vuota, vi suggerisco di utilizzare Caliburn.Micro a "collegare" un comando per la DropDownOpened caso di vostra ComboBox.

Ecco un esempio: prima di tutto il codice XAML

<Window x:Class="WpfApplication1.MainView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:cal="http://www.caliburnproject.org" 
     Title="MainView" Height="600" Width="600" 
     Name="_window"> 

    <DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Width="*" Header="Test Column"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <ComboBox Width="150" 
            HorizontalAlignment="Left" 
            ItemsSource="{Binding TestChildCollection}" 
            cal:Message.Attach="[Event DropDownOpened] = [Action Choose($dataContext)]"/> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 

Poi il ViewModel:

public class MainViewModel : Caliburn.Micro.PropertyChangedBase 
{ 
    public MainViewModel() 
    { 
     TestItemCollection = new ObservableCollection<TestClass> 
     { 
      new TestClass(), 
      new TestClass(), 
      new TestClass(), 
     }; 
    } 

    public void Choose(object data) 
    { 
     if (!(data is TestClass)) 
     { 
      TestItemCollection.Add(new TestClass()); 
     } 
    } 

    public ObservableCollection<TestClass> TestItemCollection { get; set; } 
} 

perche non che nel mio campione codice TestClass è la stessa che hai scritto. Naturalmente è necessario configurare l'applicazione per lavorare con Caliburn.Micro (se non si sa come farlo, è possibile leggere lo documentation).

Se non si desidera (o forse non si può) utilizzare Caliburn.Micro, è possibile ottenere lo stesso risultato utilizzando la libreria System.Windows.Interactivity (vedere la mia modifica di seguito).

Prova il codice: basta un clic e una nuova riga viene creata automaticamente. Spero possa aiutarti.

EDIT: soluzione alternativa con System.Windows.Interactivity

Se non è possibile utilizzare Caliburn.Micro, è sufficiente modificare il codice XAML MainView in questo modo:

<Window x:Class="WpfApplication1.MainView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
     xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
     Title="MainView" Height="600" Width="600" 
     Name="_window"> 

    <DataGrid GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Width="*" Header="Test Column"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <ComboBox Width="150" 
            HorizontalAlignment="Left" 
            ItemsSource="{Binding TestChildCollection}"> 
          <i:Interaction.Triggers> 
           <i:EventTrigger EventName="DropDownOpened"> 
            <ei:CallMethodAction MethodName="ChooseWithInteraction" 
                 TargetObject="{Binding ElementName=_window, Path=DataContext}" /> 
           </i:EventTrigger> 
          </i:Interaction.Triggers> 
         </ComboBox> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 

</Window> 

Come si può vedere , Ho appena aggiunto i riferimenti a Microsoft.Expression.Interactions e System.Windows.Interactivity librerie. Quindi ho aggiunto uno EventTrigger e uno CallMethodAction allo ComboBox.

Ora, nel MainViewModel si può sostituire il metodo Choose con l'ChooseWithInteraction uno (naturalmente si può anche semplicemente aggiungerla al codice):

public void ChooseWithInteraction(object sender, EventArgs args) 
{ 
    object data = ((ComboBox)sender).DataContext; 
    if (!(data is TestClass)) 
    { 
     TestItemCollection.Add(new TestClass()); 
    } 
} 

In questo modo è possibile ottenere lo stesso comportamento del mio prima soluzione, ma senza usare Caliburn.

+0

Ho la tua idea. Ciò creerà una nuova istanza di TestClass in DataContext ma non aprirà Combobox con i dati. Com'è stato prima di iniziare a selezionare la riga e solo allora Combobox otterrà i dati. – Spirosi

+0

Non è giusto @Spirosi. Ho testato il mio codice con .NET 4 e Caliburn.Micro 2.0.2; se clicco sull'ultimo Combobox - quello inserito nella "riga di inserimento" - si aprirà _ ** con ** _ dati (cioè "Primo elemento test", "Secondo elemento test", "Terzo elemento test" e " Quarto articolo di prova "). Hai un comportamento diverso? –

+0

L'ho provato solo con Interattività. Proverò con Cliburn. – Spirosi

1

Sono giunto a una soluzione che è possibile ottenere la funzionalità desiderata seguendo due passaggi.

Fase uno: Si dovrebbe specificare un CellEditingTemplate per la DataGridTemplateColumn e impostare IsHitTestVisible-false per il DataGridTemplateColumn.CellTemplate. Ciò costringerà l'interfaccia utente a entrare in modalità di modifica quando viene fatto clic su DataGridCell (ad esempio, il tuo ComboBox) e, di conseguenza, verrà creata una nuova istanza della raccolta. Per fare ciò, si deve prima definire una proprietà nel vostro TestClass per mantenere il valore selezionato di ogni ComboBox:

public class TestClass 
{ 
    public ObservableCollection<string> TestChildCollection { get; set; } 

    // keeps the selected value of each ComboBox 
    public string SelectedTestItem { get; set; } 

    public TestClass() 
    { 
     TestChildCollection = new ObservableCollection<string> {"First test item", "Second test item" , "Third test item" , "Fourth test item" }; 
    } 

} 

Poi il xaml per il vostro DataGrid dovrebbe cambiare in questo modo:

<DataGrid Grid.Row="0" 
       SelectionUnit="Cell" 
       DataGridCell.Selected="DataGridCell_GotFocus" 
       GridLinesVisibility="All" AutoGenerateColumns="False" ItemsSource="{Binding TestItemCollection}"> 
     <DataGrid.Columns> 
      <DataGridTemplateColumn Width="*" Header="Test Column"> 
       <DataGridTemplateColumn.CellTemplate> 
        <DataTemplate> 
         <ComboBox Width="150" IsHitTestVisible="False" 
           HorizontalAlignment="Left" 
           ItemsSource="{Binding TestChildCollection}" 
           SelectedItem="{Binding SelectedTestItem}" /> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellTemplate> 
       <DataGridTemplateColumn.CellEditingTemplate> 
        <DataTemplate> 
         <ComboBox Width="150" 
           HorizontalAlignment="Left" 
           ItemsSource="{Binding TestChildCollection}" 
            SelectedItem="{Binding SelectedTestItem}"/> 
        </DataTemplate> 
       </DataGridTemplateColumn.CellEditingTemplate> 
      </DataGridTemplateColumn> 
     </DataGrid.Columns> 
    </DataGrid> 

Fase due : Come si può vedere sopra xaml, SelectionUnit è impostato su Cell ed è stato definito anche un gestore di eventi per DataGridCell.Selected. Il codice di gestore di eventi è la seguente:

private void DataGridCell_GotFocus(object sender, RoutedEventArgs e) 
    { 
     if (e.OriginalSource.GetType() == typeof(DataGridCell)) 
     { 
      DataGrid dataGrid = (DataGrid)sender; 
      dataGrid.BeginEdit(e); 
     } 
    } 

Questo fa in modo che ogni volta che si fa clic su un DataGridCell entrerà editing-mode. pertanto è necessario un clic in meno ogni volta che si tenta di aprire ComboBox all'interno del nuovo DataGridRow.

+1

Uno in meno ma non è ancora un singolo :) Ci vorranno due clic per aprire il menu a discesa. Ma comunque grazie, la tua soluzione mi sta bene. – Spirosi

+0

Hai ragione, il primo clic serve per selezionare una nuova cella e il secondo clic aprirà il menu a discesa. –