Quelle est la bonne façon de gérer plusieurs datagrids dans un contrôle d'onglet pour que les cellules quittent le mode édition lorsque les onglets sont changés?

Dans wpf-je configurer un contrôle onglet qui se lie à une collection d'objets, chaque objet a un modèle de données avec une grille de données présentation des données. Si je sélectionne une cellule particulière et que je la mets en mode édition, en quittant la grille en allant dans un autre onglet, l'exception ci-dessous sera lancée en retournant le datagrid:

'DeferRefresh" n'est pas autorisée au cours d'une AddNew ou EditItem transaction.

il semble que la cellule n'a jamais quitté edit mode. Est-il un moyen facile de prendre des la cellule en mode edit, ou quelque chose d'autre se passe ici?

mise à Jour: C'est comme si je ne lient pas l'onglet contrôle à la source de données, mais plutôt de définir explicitement chaque onglet, puis liez chaque élément de la source de données à un contrôle du contenu de ce problème disparaît. Ce n'est pas vraiment une bonne solution, donc je voudrais encore savoir comment lier la collection directement à la commande d'onglet.

mise à jour: donc ce que j'ai fait pour ma propre solution est d'utiliser un ListView et un contrôle de contenu à la place d'un contrôle d'onglet. J'utilise un style à faire l'affichage de la liste regarder onglet comme. Le modèle de vue expose un ensemble de modèles de vue enfant et permet à l'utilisateur d'en sélectionner un via la vue Liste. Le contrôle de contenu présente alors le modèle de vue sélectionné et chaque modèle de vue a un modèle de données associé qui contient la grille de données. Avec cette configuration passer entre voir les modèles en mode édition sur la grille va correctement terminer le mode édition et enregistrer les données.

voici le xaml pour configurer ceci:

<ListView ItemTemplate="{StaticResource MakeItemsLookLikeTabs}" 
          ItemsSource="{Binding ViewModels}"  
          SelectedItem="{Binding Selected}" 
          Style="{StaticResource MakeItLookLikeATabControl}"/>

<ContentControl Content="{Binding Selected}">

J'accepte la réponse de Phil car cela devrait fonctionner aussi, mais pour moi la solution ci-dessus semble comme il sera plus portable entre les projets.

20
demandé sur Jon 2010-07-28 20:10:02

5 réponses

j'ai réussi à contourner ce problème en détectant lorsque l'utilisateur clique sur un TabItem et puis commettre des modifications visibles DataGrid dans le TabControl . Je suppose que l'utilisateur s'attendra à ce que leurs modifications soient toujours là quand ils cliqueront en arrière.

"151940920 extrait de Code":

// PreviewMouseDown event handler on the TabControl
private void TabControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    if (IsUnderTabHeader(e.OriginalSource as DependencyObject))
        CommitTables(yourTabControl);
}

private bool IsUnderTabHeader(DependencyObject control)
{
    if (control is TabItem)
        return true;
    DependencyObject parent = VisualTreeHelper.GetParent(control);
    if (parent == null)
        return false;
    return IsUnderTabHeader(parent);
}

private void CommitTables(DependencyObject control)
{
    if (control is DataGrid)
    {
        DataGrid grid = control as DataGrid;
        grid.CommitEdit(DataGridEditingUnit.Row, true);
        return;
    }
    int childrenCount = VisualTreeHelper.GetChildrenCount(control);
    for (int childIndex = 0; childIndex < childrenCount; childIndex++)
        CommitTables(VisualTreeHelper.GetChild(control, childIndex));
}

C'est dans le code derrière.

7
répondu Phil Gan 2011-05-04 16:26:58

j'ai implémenté un comportement pour le DataGrid basé sur le code que j'ai trouvé dans ce" fil .

Utilisation: <DataGrid local:DataGridCommitEditBehavior.CommitOnLostFocus="True" />

Code:

using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;

/// <summary>
///   Provides an ugly hack to prevent a bug in the data grid.
///   https://connect.microsoft.com/VisualStudio/feedback/details/532494/wpf-datagrid-and-tabcontrol-deferrefresh-exception
/// </summary>
public class DataGridCommitEditBehavior
{
    public static readonly DependencyProperty CommitOnLostFocusProperty =
        DependencyProperty.RegisterAttached(
            "CommitOnLostFocus", 
            typeof(bool), 
            typeof(DataGridCommitEditBehavior), 
            new UIPropertyMetadata(false, OnCommitOnLostFocusChanged));

    /// <summary>
    ///   A hack to find the data grid in the event handler of the tab control.
    /// </summary>
    private static readonly Dictionary<TabPanel, DataGrid> ControlMap = new Dictionary<TabPanel, DataGrid>();

    public static bool GetCommitOnLostFocus(DataGrid datagrid)
    {
        return (bool)datagrid.GetValue(CommitOnLostFocusProperty);
    }

    public static void SetCommitOnLostFocus(DataGrid datagrid, bool value)
    {
        datagrid.SetValue(CommitOnLostFocusProperty, value);
    }

    private static void CommitEdit(DataGrid dataGrid)
    {
        dataGrid.CommitEdit(DataGridEditingUnit.Cell, true);
        dataGrid.CommitEdit(DataGridEditingUnit.Row, true);
    }

    private static DataGrid GetParentDatagrid(UIElement element)
    {
        UIElement childElement; // element from which to start the tree navigation, looking for a Datagrid parent

        if (element is ComboBoxItem)
        {
            // Since ComboBoxItem.Parent is null, we must pass through ItemsPresenter in order to get the parent ComboBox
            var parentItemsPresenter = VisualTreeFinder.FindParentControl<ItemsPresenter>(element as ComboBoxItem);
            var combobox = parentItemsPresenter.TemplatedParent as ComboBox;
            childElement = combobox;
        }
        else
        {
            childElement = element;
        }

        var parentDatagrid = VisualTreeFinder.FindParentControl<DataGrid>(childElement);
        return parentDatagrid;
    }

    private static TabPanel GetTabPanel(TabControl tabControl)
    {
        return
            (TabPanel)
                tabControl.GetType().InvokeMember(
                    "ItemsHost", 
                    BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance, 
                    null, 
                    tabControl, 
                    null);
    }

    private static void OnCommitOnLostFocusChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = depObj as DataGrid;
        if (dataGrid == null)
        {
            return;
        }

        if (e.NewValue is bool == false)
        {
            return;
        }

        var parentTabControl = VisualTreeFinder.FindParentControl<TabControl>(dataGrid);
        var tabPanel = GetTabPanel(parentTabControl);
        if (tabPanel != null)
        {
            ControlMap[tabPanel] = dataGrid;
        }

        if ((bool)e.NewValue)
        {
            // Attach event handlers
            if (parentTabControl != null)
            {
                tabPanel.PreviewMouseLeftButtonDown += OnParentTabControlPreviewMouseLeftButtonDown;
            }

            dataGrid.LostKeyboardFocus += OnDataGridLostFocus;
            dataGrid.DataContextChanged += OnDataGridDataContextChanged;
            dataGrid.IsVisibleChanged += OnDataGridIsVisibleChanged;
        }
        else
        {
            // Detach event handlers
            if (parentTabControl != null)
            {
                tabPanel.PreviewMouseLeftButtonDown -= OnParentTabControlPreviewMouseLeftButtonDown;
            }

            dataGrid.LostKeyboardFocus -= OnDataGridLostFocus;
            dataGrid.DataContextChanged -= OnDataGridDataContextChanged;
            dataGrid.IsVisibleChanged -= OnDataGridIsVisibleChanged;
        }
    }

    private static void OnDataGridDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var dataGrid = (DataGrid)sender;
        CommitEdit(dataGrid);
    }

    private static void OnDataGridIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var senderDatagrid = (DataGrid)sender;

        if ((bool)e.NewValue == false)
        {
            CommitEdit(senderDatagrid);
        }
    }

    private static void OnDataGridLostFocus(object sender, KeyboardFocusChangedEventArgs e)
    {
        var dataGrid = (DataGrid)sender;

        var focusedElement = Keyboard.FocusedElement as UIElement;
        if (focusedElement == null)
        {
            return;
        }

        var focusedDatagrid = GetParentDatagrid(focusedElement);

        // Let's see if the new focused element is inside a datagrid
        if (focusedDatagrid == dataGrid)
        {
            // If the new focused element is inside the same datagrid, then we don't need to do anything;
            // this happens, for instance, when we enter in edit-mode: the DataGrid element loses keyboard-focus, 
            // which passes to the selected DataGridCell child
            return;
        }

        CommitEdit(dataGrid);
    }

    private static void OnParentTabControlPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var dataGrid = ControlMap[(TabPanel)sender];
        CommitEdit(dataGrid);
    }
}

public static class VisualTreeFinder
{
    /// <summary>
    ///   Find a specific parent object type in the visual tree
    /// </summary>
    public static T FindParentControl<T>(DependencyObject outerDepObj) where T : DependencyObject
    {
        var dObj = VisualTreeHelper.GetParent(outerDepObj);
        if (dObj == null)
        {
            return null;
        }

        if (dObj is T)
        {
            return dObj as T;
        }

        while ((dObj = VisualTreeHelper.GetParent(dObj)) != null)
        {
            if (dObj is T)
            {
                return dObj as T;
            }
        }

        return null;
    }
}
10
répondu Robert Hahn 2013-01-07 07:48:14

J'ai accepté ce que Phil Gan a répondu que, pour travailler autour de cette question devrait être en détectant lorsque l'utilisateur clique sur un TabItem et puis en commettant des modifications sur les Datagrades visibles dans le TabControl.

vous pouvez voir ce lien pour problème de similarité...

Gridview pas visible même après la liaison....

J'espère que ça pourrait aider...

0
répondu Algem Mojedo-MCPD-EA 2017-05-23 11:45:33

ce bogue est résolu dans le Framework .net 4.5. Vous pouvez le télécharger à ce lien .

0
répondu Morgan M. 2013-09-03 08:12:46

ce que je pense que vous devriez faire est assez proche de ce que @myermian a dit. Il y a un événement appelé CellEditEnding end cet événement vous permettrait d'intercepter et de prendre la décision de laisser tomber la rangée non désirée.

private void dataGrid1_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
    {
        DataGrid grid = (DataGrid)sender;
        TextBox cell = (TextBox)e.EditingElement;
        if(String.IsNullOrEmpty(cell.Text) && e.EditAction == DataGridEditAction.Commit)
        {
            grid.CancelEdit(DataGridEditingUnit.Row);
            e.Cancel = true;
        }            
    }
0
répondu Oakcool 2016-06-15 04:52:03