wpf - son - pasos para crear un formulario en access
¿Cuál es la forma correcta de manejar múltiples áreas de datos en un control de pestaña para que las celdas salgan del modo de edición cuando se cambian las pestañas? (5)
Este error se resuelve en .NET Framework 4.5. Puedes descargarlo en este enlace .
En wpf configuro un control de pestaña que se enlaza a una colección de objetos, cada objeto tiene una plantilla de datos con una cuadrícula de datos que presenta los datos. Si selecciono una celda en particular y la puse en modo de edición, al salir de la cuadrícula yendo a otra pestaña, esto causará que se lance la excepción a continuación al devolver el datagrid:
''DeferRefresh'' no está permitido durante una transacción AddNew o EditItem.
Parece que la celda nunca salió del modo de edición. ¿Hay una manera fácil de sacar la celda del modo de edición, o está pasando algo más aquí?
Actualización: Parece que si no vinculo el control de pestañas al origen de datos, sino que en su lugar defino explícitamente cada pestaña y luego vinculo cada elemento del origen de datos a un control de contenido, este problema desaparece. Esta no es realmente una gran solución, por lo que me gustaría saber cómo vincular la colección directamente al control de pestañas.
Actualización: Entonces, lo que realmente he hecho para mi propia solución es usar un ListView y un control de contenido en lugar de un control de pestañas. Yo uso un estilo para hacer que la vista de lista se vea como la pestaña. El modelo de vista expone un conjunto de modelos de vista secundarios y permite al usuario seleccionar uno a través de la vista de lista. El control de contenido presenta el modelo de vista seleccionado y cada modelo de vista tiene una plantilla de datos asociada que contiene la cuadrícula de datos. Con esta configuración, el cambio entre modelos de vista mientras se encuentra en el modo de edición en la cuadrícula finalizará correctamente el modo de edición y guardará los datos.
Aquí está el xaml para configurar esto:
<ListView ItemTemplate="{StaticResource MakeItemsLookLikeTabs}"
ItemsSource="{Binding ViewModels}"
SelectedItem="{Binding Selected}"
Style="{StaticResource MakeItLookLikeATabControl}"/>
<ContentControl Content="{Binding Selected}">
Aceptaré la respuesta de Phil, ya que también debería funcionar, pero para mí, la solución anterior parece ser más portátil entre proyectos.
Estuve de acuerdo con lo que Phil Gan respondió, para solucionar este problema, se debe detectar cuando el usuario hace clic en un elemento de tabla y luego realiza las ediciones en DataGrid visible en TabControl.
Usted podría ver este enlace para el problema de similitud ...
La vista en cuadrícula no es visible incluso después de unir ...
Espero que pueda ayudar ...
Implementé un comportamiento para el DataGrid basado en el código que encontré en this hilo.
Uso: <DataGrid local:DataGridCommitEditBehavior.CommitOnLostFocus="True" />
Código:
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;
}
}
Lo que creo que deberías hacer es bastante parecido a lo que dijo @myermian. Hay un evento llamado CellEditEnding. Este evento le permitirá interceptar y tomar la decisión de eliminar la fila no deseada.
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;
}
}
Me las arreglé para TabItem
este problema detectando cuando el usuario hace clic en un TabItem
y luego TabItem
ediciones en DataGrid
visible en el TabControl
. Supongo que el usuario esperará que sus cambios sigan estando allí cuando vuelvan a hacer clic.
Fragmento de código:
// 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));
}
Esto está en el código detrás.