2015-10-15 12 views
5

Mam z DataGridTemplateColumn i ComboBox w nim.WPF DataGrid Pojedyncze Kliknij, aby utworzyć nowy element

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

Kiedy klikam na ComboBox w pustym wierszu to widocznie nie tworzy nową instancję mojej kolekcji i tylko daje pustą listę.

enter image description here

muszę dwukrotnie na pustej przestrzeni wiersza.

enter image description here

I dopiero wtedy byłoby uzyskać dane w ComboBox.

enter image description here

Jak mogę uzyskać dane w Combobox za pomocą jednego kliknięcia na pustym wierszu ??

Odpowiedz

1

Jeśli trzeba uzyskać dane w ComboBox z pojedynczego kliknięcie na pustym wierszu, proponuję użyć Caliburn.Micro do „dołączyć” polecenie przypadku DropDownOpened swojej ComboBox.

Oto próbka: Przede wszystkim 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> 

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

Proszę zwrócić uwagę, że w moim próbki kodu TestClass jest taka sama, że ​​napisałeś. Oczywiście musisz skonfigurować swoją aplikację, aby pracować z Caliburn.Micro (jeśli nie wiesz jak to zrobić, możesz przeczytać documentation).

Jeśli nie chcesz (a może nie możesz) używać Caliburn.Micro, możesz uzyskać ten sam wynik, korzystając z biblioteki System.Windows.Interactivity (zobacz moją edycję poniżej).

Wypróbuj kod: wystarczy jedno kliknięcie, a nowy wiersz zostanie automatycznie utworzony. Mam nadzieję, że ci to pomoże.

EDIT: alternatywne rozwiązanie z System.Windows.Interactivity

Jeśli nie można używać Caliburn.Micro, wystarczy zmodyfikować XAML MAINVIEW w ten sposób:

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

Jak widać , Właśnie dodałem odniesienia do Microsoft.Expression.Interactions i bibliotek System.Windows.Interactivity. Następnie dodałem EventTrigger i CallMethodAction do ComboBox.

Teraz w MainViewModel można zastąpić metodę Choose z ChooseWithInteraction jednej (oczywiście można też po prostu dodać go do kodu):

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

W ten sposób można uzyskać takie samo zachowanie z mojej pierwsze rozwiązanie, ale bez użycia Caliburn.

+0

Mam twój pomysł. To stworzy nową instancję TestClass w DataContext, ale nie otworzy Combobox z danymi. Tak jak wcześniej, najpierw wybierz wiersz i dopiero wtedy Combobox dostanie dane. – Spirosi

+0

To nie jest w porządku @Spirosi. Przetestowałem swój kod za pomocą .NET 4 i Caliburn.Micro 2.0.2; jeśli kliknę na ostatni Combobox - umieszczony w "wierszu wstawiania" - otworzy się _ ** z danymi ** _ (tzn. "Pierwszy przedmiot testowy", "Drugi przedmiot testowy", "Trzeci element testowy" i " Czwarta pozycja testowa "). Czy otrzymujesz inne zachowanie? –

+0

Testowałem to tylko z interakcyjnością. Spróbuję z Cliburnem. – Spirosi

1

Doszedłem do rozwiązania, które można osiągnąć pożądaną funkcjonalność, wykonując dwa kroki.

Krok pierwszy: Należy określić CellEditingTemplate dla DataGridTemplateColumn i ustawić IsHitTestVisible do false dla DataGridTemplateColumn.CellTemplate. Zmusi to interfejs użytkownika do przejścia do trybu edycji po kliknięciu przycisku DataGridCell (np. Twój ComboBox), w wyniku czego zostanie utworzona nowa instancja kolekcji. Aby to zrobić, należy najpierw zdefiniować właściwość w TestClass zachować wybraną wartość każdego 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" }; 
    } 

} 

wtedy xaml dla DataGrid należy zmienić tak:

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

dwuetapową : Jak widać w powyższym xaml, SelectionUnit jest ustawiony na Cell, a także zdefiniowano procedurę obsługi zdarzeń dla DataGridCell.Selected. Kod obsługi zdarzeń jest jak poniżej:

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

Daje to pewność, że za każdym razem po kliknięciu na DataGridCell Wejdzie edycji-mode. dlatego potrzebujesz jednego kliknięcia mniej za każdym razem, gdy próbujesz otworzyć ComboBox wewnątrz nowo utworzonego DataGridRow.

+1

Jedna mniej, ale wciąż nie jest singielka :) Otwarcie menu zajmie dwa kliknięcia. Ale dzięki, twoje rozwiązanie pasuje do mnie. – Spirosi

+0

Masz rację, pierwsze kliknięcie służy do wybierania nowej komórki, a drugie kliknięcie powoduje otwarcie listy rozwijanej. –