web-dev-qa-db-fra.com

WPF Datagrid définit la ligne sélectionnée

Comment utiliser le Datagrid.SelectedItem pour sélectionner une ligne par programme?

Dois-je d'abord créer un objet IEnumerable of DataGridRow et transmettre la ligne correspondante à cette propriété SelectedItem ou comment procéder?

MODIFIER:

Je dois faire correspondre le contenu de la cellule de la première colonne avec un TextBox.Text avant de sélectionner la ligne. 

15
Tony The Lion

veuillez vérifier si le code ci-dessous fonctionne pour vous. il parcourt les cellules de la première colonne de datagris et vérifie si le contenu de la cellule est égal à la valeur textbox.text et sélectionne la ligne.

for (int i = 0; i < dataGrid.Items.Count; i++)
{
    DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(i);
    TextBlock cellContent = dataGrid.Columns[0].GetCellContent(row) as TextBlock;
    if (cellContent != null && cellContent.Text.Equals(textBox1.Text))
    {
        object item = dataGrid.Items[i];
        dataGrid.SelectedItem = item;
        dataGrid.ScrollIntoView(item);
        row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        break;
    }
}

espérons que cela aide, salutations

35
serge_gubenko

Vous n'avez pas besoin de parcourir les lignes DataGrid, vous pouvez atteindre votre objectif avec une solution plus simple . Afin de correspondre à votre ligne, vous pouvez parcourir la collection liée à votre propriété DataGrid.ItemsSource puis vous attribue cet élément à votre propriété DataGrid.SelectedItem par programme, ou vous pouvez également l'ajouter à votre collection DataGrid.SelectedItems si vous souhaitez autoriser l'utilisateur à sélectionner plusieurs lignes. Voir le code ci-dessous:

<Window x:Class="ProgGridSelection.MainWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525" Loaded="OnWindowLoaded">
<StackPanel>
    <DataGrid Name="empDataGrid" ItemsSource="{Binding}" Height="200"/>
    <TextBox Name="empNameTextBox"/>
    <Button Content="Click" Click="OnSelectionButtonClick" />
</StackPanel>

public partial class MainWindow : Window
{
    public class Employee
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }

    private ObservableCollection<Employee> _empCollection;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void OnWindowLoaded(object sender, RoutedEventArgs e)
    {
        // Generate test data
        _empCollection =
            new ObservableCollection<Employee>
                {
                    new Employee {Code = "E001", Name = "Mohammed A. Fadil"},
                    new Employee {Code = "E013", Name = "Ahmed Yousif"},
                    new Employee {Code = "E431", Name = "Jasmin Kamal"},
                };

        /* Set the Window.DataContext, alternatively you can set your
         * DataGrid DataContext property to the employees collection.
         * on the other hand, you you have to bind your DataGrid
         * DataContext property to the DataContext (see the XAML code)
         */
        DataContext = _empCollection;
    }

    private void OnSelectionButtonClick(object sender, RoutedEventArgs e)
    {
        /* select the employee that his name matches the
         * name on the TextBox
         */
        var emp = (from i in _empCollection
                   where i.Name == empNameTextBox.Text.Trim()
                   select i).FirstOrDefault();

        /* Now, to set the selected item on the DataGrid you just need
         * assign the matched employee to your DataGrid SeletedItem
         * property, alternatively you can add it to your DataGrid
         * SelectedItems collection if you want to allow the user
         * to select more than one row, e.g.:
         *    empDataGrid.SelectedItems.Add(emp);
         */
        if (emp != null)
            empDataGrid.SelectedItem = emp;
    }
}
21
Mohammed A. Fadil

C’est un peu plus délicat de faire ce que vous essayez de faire que ce que je préférerais, mais c’est parce que vous ne liez pas directement une DataGrid à une DataTable

Lorsque vous liez DataGrid.ItemsSource à une DataTable, vous le liez vraiment à la valeur par défaut DataView, pas à la table elle-même. C'est pourquoi, par exemple, vous n'avez rien à faire pour créer un rang DataGrid lorsque vous cliquez sur un en-tête de colonne. Cette fonctionnalité est intégrée à DataView et DataGrid sait comment y accéder (via l'interface IBindingList).

DataView implémente IEnumerable<DataRowView> (plus ou moins), et DataGrid remplit ses éléments en itérant dessus. Cela signifie que lorsque vous avez lié DataGrid.ItemsSource à une DataTable, sa propriété SelectedItem sera une DataRowView et non une DataRow.

Si vous savez tout cela, il est assez simple de construire une classe wrapper qui vous permette d'exposer des propriétés auxquelles vous pouvez vous lier. Il y a trois propriétés principales:

  • Table, le DataTable,
  • Row, propriété bidirectionnelle pouvant être liée de type DataRowView, et 
  • SearchText, une propriété de chaîne qui, lorsqu'elle est définie, trouve la première DataRowView correspondante dans la vue par défaut de la table, définit la propriété Row et lève PropertyChanged.

Cela ressemble à ceci:

public class DataTableWrapper : INotifyPropertyChanged
{
    private DataRowView _Row;

    private string _SearchText;

    public DataTableWrapper()
    {
        // using a parameterless constructor lets you create it directly in XAML
        DataTable t = new DataTable();
        t.Columns.Add("id", typeof (int));
        t.Columns.Add("text", typeof (string));

        // let's acquire some sample data
        t.Rows.Add(new object[] { 1, "Tower"});
        t.Rows.Add(new object[] { 2, "Luxor" });
        t.Rows.Add(new object[] { 3, "American" });
        t.Rows.Add(new object[] { 4, "Festival" });
        t.Rows.Add(new object[] { 5, "Worldwide" });
        t.Rows.Add(new object[] { 6, "Continental" });
        t.Rows.Add(new object[] { 7, "Imperial" });

        Table = t;

    }

    // you should have this defined as a code snippet if you work with WPF
    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler h = PropertyChanged;
        if (h != null)
        {
            h(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // SelectedItem gets bound to this two-way
    public DataRowView Row
    {
        get { return _Row; }
        set
        {
            if (_Row != value)
            {
                _Row = value;
                OnPropertyChanged("Row");
            }
        }
    }

    // the search TextBox is bound two-way to this
    public string SearchText
    {
        get { return _SearchText; }
        set
        {
            if (_SearchText != value)
            {
                _SearchText = value;
                Row = Table.DefaultView.OfType<DataRowView>()
                    .Where(x => x.Row.Field<string>("text").Contains(_SearchText))
                    .FirstOrDefault();
            }
        }
    }

    public DataTable Table { get; private set; }
}

Et voici XAML qui l'utilise:

<Window x:Class="DataGridSelectionDemo.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml" 
        xmlns:dg="clr-namespace:Microsoft.Windows.Controls;Assembly=WPFToolkit"
        xmlns:DataGridSelectionDemo="clr-namespace:DataGridSelectionDemo" 
        Title="DataGrid selection demo" 
        Height="350" 
        Width="525">
    <Window.DataContext>
        <DataGridSelectionDemo:DataTableWrapper />
    </Window.DataContext>
    <DockPanel>
        <Grid DockPanel.Dock="Top">
        <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Label>Text</Label>
            <TextBox Grid.Column="1" 
                     Text="{Binding SearchText, Mode=TwoWay}" />
        </Grid>
        <dg:DataGrid DockPanel.Dock="Top"
                     ItemsSource="{Binding Table}"
                     SelectedItem="{Binding Row, Mode=TwoWay}" />
    </DockPanel>
</Window>
7
Robert Rossney

J'ai cherché une solution à un problème similaire et peut-être que ma façon de faire vous aidera, vous et tous ceux qui y font face.

J'ai utilisé SelectedValuePath="id" dans la définition XAML DataGrid, et la seule chose que je dois faire par programme est de définir DataGrid.SelectedValue sur la valeur souhaitée.

Je sais que cette solution a des avantages et des inconvénients, mais dans des cas spécifiques, elle est simple et rapide.

Meilleures salutations

Marcin

7
Marcin Sasin

// En général pour accéder à toutes les lignes //

foreach (var item in dataGrid1.Items)
{
    string str = ((DataRowView)dataGrid1.Items[1]).Row["ColumnName"].ToString();
}

// Pour accéder aux lignes sélectionnées //

private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    try
    {
        string str = ((DataRowView)dataGrid1.SelectedItem).Row["ColumnName"].ToString();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
2
Shan

J'ai changé le code de serge_gubenko et ça marche mieux 

for (int i = 0; i < dataGrid.Items.Count; i++)
{
    string txt = searchTxt.Text;
    dataGrid.ScrollIntoView(dataGrid.Items[i]);
    DataGridRow row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(i);
    TextBlock cellContent = dataGrid.Columns[1].GetCellContent(row) as TextBlock;
    if (cellContent != null && cellContent.Text.ToLower().Equals(txt.ToLower()))
    {
        object item = dataGrid.Items[i];
        dataGrid.SelectedItem = item;
        dataGrid.ScrollIntoView(item);
        row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        break;
    }
}
1
Marian Brestovansky

Si quelqu'un tombe par hasard ici a des problèmes avec la sélection de la grille interne après OnSelectionChanged - après avoir essayé sans succès tous les paramètres de sélection pendant une douzaine d'heures, la seule chose qui a fonctionné pour moi était de recharger et de repeupler DataGrid avec l'élément sélectionné. Pas élégant du tout, mais pour le moment, je ne suis pas sûr s'il existe une meilleure solution dans ma situation.

datagrid.ItemsSource = null
datagrid.ItemsSource = items;
datagrid.SelectedItem = selectedItem;
0
Mindaugas-kun

Je suis tombé sur cet article TechNet assez récent (comparé à l'âge de la question), qui inclut certaines des meilleures techniques que j'ai pu trouver sur le sujet:

WPF: Sélection et mise au point par programme d'une ligne ou d'une cellule dans un DataGrid

Il comprend des détails qui devraient couvrir la plupart des exigences. Il est important de se rappeler que si vous spécifiez des modèles personnalisés pour DataGridRow pour certaines lignes, celles-ci ne contiendront pas DataGridCells et les mécanismes de sélection normaux de la grille ne fonctionneront pas.

Vous aurez besoin d’être plus précis sur la source de données que vous avez fournie à la grille pour répondre à la première partie de votre question, comme l’ont dit les autres.

0
Andre Luus