在选项卡控件中处理多个数据网格的正确方法是什么,以便在更改选项卡时单元格退出编辑模式?

Jon*_*Jon 20 wpf datagrid tabcontrol

在wpf中,我设置了一个标签控件,它绑定到一个对象集合,每个对象都有一个数据模板,数据网格显示数据.如果我选择一个特定的细胞,并把它进入编辑模式,转到另一个选项卡离开电网,这将导致异常下面要在返回数据网格抛出:

在AddNew或EditItem事务期间不允许"DeferRefresh".

看来该单元格从未离开编辑模式.是否有一种简单的方法可以将单元格从编辑模式中取出,或者是否还有其他功能?

更新:它看起来像如果我不绑定选项卡控件到数据源,而是明确定义每个选项卡,然后在数据源中的每个项目绑定到内容控制这个问题消失.这不是一个很好的解决方案,所以我仍然想知道如何将集合直接绑定到选项卡控件.

更新:所以我实际上为自己的解决方案所做的是使用ListView和内容控件来代替选项卡控件.我使用样式使列表视图看起来像.视图模型公开一组子视图模型,并允许用户通过列表视图选择一个.然后,内容控件呈现所选择的视图模型,并且每个视图模型具有包含数据网格的关联数据模板.通过此设置在视图模型之间切换,而在网格上的编辑模式下,将正确结束编辑模式并保存数据.

这是设置它的xaml:

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

<ContentControl Content="{Binding Selected}">
Run Code Online (Sandbox Code Playgroud)

我会接受Phil的答案,因为它也应该有效,但对我来说,上面的解决方案似乎在项目之间更容易移植.

Rob*_*ahn 10

我基于线程中找到的代码实现了DataGrid的行为.

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

码:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


Phi*_*Gan 7

我已成功通过检测,当用户点击一个以解决此问题TabItem上是可见的,然后提交编辑DataGridTabControl.我假设用户在点击后会发现他们的更改仍然存在.

代码段:

// 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));
}
Run Code Online (Sandbox Code Playgroud)

这是在代码背后.