Chcę, aby użytkownik mógł jednym kliknięciem przełączyć komórkę w tryb edycji i zaznaczyć wiersz, w którym znajduje się komórka. Domyślnie jest to podwójne kliknięcie.
Jak to zastąpić lub zaimplementować?
Chcę, aby użytkownik mógł jednym kliknięciem przełączyć komórkę w tryb edycji i zaznaczyć wiersz, w którym znajduje się komórka. Domyślnie jest to podwójne kliknięcie.
Jak to zastąpić lub zaimplementować?
Odpowiedzi:
Oto jak rozwiązałem ten problem:
<DataGrid DataGridCell.Selected="DataGridCell_Selected"
ItemsSource="{Binding Source={StaticResource itemView}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Nom" Binding="{Binding Path=Name}"/>
<DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/>
</DataGrid.Columns>
</DataGrid>
Ten DataGrid jest powiązany z CollectionViewSource (zawierającym fikcyjne obiekty Person ).
Tam dzieje się magia: DataGridCell.Selected = "DataGridCell_Selected" .
Po prostu przechwytuję wybrane zdarzenie komórki DataGrid i wywołuję BeginEdit () na DataGrid.
Oto kod dla obsługi zdarzeń:
private void DataGridCell_Selected(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
}
}
SelectionUnit
właściwość DataGrid na Cell
.
grd.BeginEdit(e)
chcę, aby TextBox w tej komórce miał fokus. Jak mogę to zrobić? Próbowałem wywołać FindName("txtBox")
zarówno DataGridCell, jak i DataGrid, ale zwraca dla mnie wartość null.
Odpowiedź Micaela Bergerona była dla mnie dobrym początkiem do znalezienia rozwiązania, które będzie dla mnie skuteczne. Aby umożliwić edycję jednym kliknięciem również dla komórek w tym samym wierszu, który jest już w trybie edycji, musiałem go nieco dostosować. Korzystanie z SelectionUnit Cell nie było dla mnie opcją.
Zamiast używać zdarzenia DataGridCell.Selected, które jest uruchamiane tylko przy pierwszym kliknięciu komórki wiersza, użyłem zdarzenia DataGridCell.GotFocus.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Jeśli to zrobisz, zawsze będziesz mieć właściwą komórkę skupioną i w trybie edycji, ale żadna kontrola w komórce nie będzie skupiona, to rozwiązałem w ten sposób
private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
{
// Lookup for the source to be DataGridCell
if (e.OriginalSource.GetType() == typeof(DataGridCell))
{
// Starts the Edit on the row;
DataGrid grd = (DataGrid)sender;
grd.BeginEdit(e);
Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
if (control != null)
{
control.Focus();
}
}
}
private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
{
DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
if (child == null)
continue;
T castedProp = child as T;
if (castedProp != null)
return castedProp;
castedProp = GetFirstChildByType<T>(child);
if (castedProp != null)
return castedProp;
}
return null;
}
Od: http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing
XAML:
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type dg:DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
KOD ZA:
//
// SINGLE CLICK EDITING
//
private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
{
if (!cell.IsFocused)
{
cell.Focus();
}
DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
if (dataGrid != null)
{
if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
{
if (!cell.IsSelected)
cell.IsSelected = true;
}
else
{
DataGridRow row = FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
}
}
}
static T FindVisualParent<T>(UIElement element) where T : UIElement
{
UIElement parent = element;
while (parent != null)
{
T correctlyTyped = parent as T;
if (correctlyTyped != null)
{
return correctlyTyped;
}
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
Rozwiązanie z http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing działało świetnie, ale włączyłem je dla każdego DataGrid przy użyciu stylu zdefiniowanego w ResourceDictionary. Aby używać programów obsługi w słownikach zasobów, musisz dodać do nich plik związany z kodem. Oto jak to robisz:
To jest słownik zasobów DataGridStyles.xaml :
<ResourceDictionary x:Class="YourNamespace.DataGridStyles"
x:ClassModifier="public"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="DataGrid">
<!-- Your DataGrid style definition goes here -->
<!-- Cell style -->
<Setter Property="CellStyle">
<Setter.Value>
<Style TargetType="DataGridCell">
<!-- Your DataGrid Cell style definition goes here -->
<!-- Single Click Editing -->
<EventSetter Event="PreviewMouseLeftButtonDown"
Handler="DataGridCell_PreviewMouseLeftButtonDown" />
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Zwróć uwagę na atrybut x: Class w elemencie głównym. Utwórz plik zajęć. W tym przykładzie będzie to DataGridStyles.xaml.cs . Umieść ten kod w środku:
using System.Windows.Controls;
using System.Windows;
using System.Windows.Input;
namespace YourNamespace
{
partial class DataGridStyles : ResourceDictionary
{
public DataGridStyles()
{
InitializeComponent();
}
// The code from the myermian's answer goes here.
}
wolę ten sposób w oparciu o sugestię Dušana Kneževicia. klikasz i to wszystko))
<DataGrid.Resources>
<Style TargetType="DataGridCell" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver"
Value="True" />
<Condition Property="IsReadOnly"
Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="IsEditing"
Value="True" />
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
Rozwiązałem to, dodając wyzwalacz, który ustawia właściwość IsEditing DataGridCell na True, gdy wskaźnik myszy jest nad nim. To rozwiązało większość moich problemów. Działa również z comboboxami.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Szukam edycji komórki jednym kliknięciem w MVVM, a to jest inny sposób na zrobienie tego.
Dodawanie zachowania w XAML
<UserControl xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:myBehavior="clr-namespace:My.Namespace.To.Behavior">
<DataGrid>
<i:Interaction.Behaviors>
<myBehavior:EditCellOnSingleClickBehavior/>
</i:Interaction.Behaviors>
</DataGrid>
</UserControl>
Klasa EditCellOnSingleClickBehavior rozszerza System.Windows.Interactivity.Behavior;
public class EditCellOnSingleClick : Behavior<DataGrid>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.LoadingRow += this.OnLoadingRow;
this.AssociatedObject.UnloadingRow += this.OnUnloading;
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.LoadingRow -= this.OnLoadingRow;
this.AssociatedObject.UnloadingRow -= this.OnUnloading;
}
private void OnLoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus += this.OnGotFocus;
}
private void OnUnloading(object sender, DataGridRowEventArgs e)
{
e.Row.GotFocus -= this.OnGotFocus;
}
private void OnGotFocus(object sender, RoutedEventArgs e)
{
this.AssociatedObject.BeginEdit(e);
}
}
Voila!
Istnieją dwa problemy z odpowiedzią user2134678. Jeden jest bardzo niewielki i nie ma żadnego efektu funkcjonalnego. Drugi jest dość znaczący.
Pierwszą kwestią jest to, że GotFocus jest faktycznie wywoływany przeciwko DataGrid, a nie w praktyce DataGridCell. Kwalifikator DataGridCell w XAML jest nadmiarowy.
Główny problem, który znalazłem w odpowiedzi, polega na tym, że zachowanie klawisza Enter jest zepsute. Enter powinien przenieść cię do następnej komórki poniżej bieżącej komórki w normalnym zachowaniu DataGrid. Jednak to, co faktycznie dzieje się za kulisami, to zdarzenie GotFocus zostanie wywołane dwukrotnie. Raz na bieżącej komórce traci ostrość, a raz na nowej komórce, która zyskuje ostrość. Ale tak długo, jak BeginEdit jest wywoływana w tej pierwszej komórce, następna komórka nigdy nie zostanie aktywowana. Efekt jest taki, że edytujesz jednym kliknięciem, ale każdy, kto nie kliknie dosłownie siatki, będzie miał kłopoty, a projektant interfejsu użytkownika nie powinien zakładać, że wszyscy użytkownicy używają myszy. (Użytkownicy klawiatury mogą w pewnym sensie ominąć to za pomocą Tab, ale to nadal oznacza, że przeskakują przez obręcze, których nie powinni tego robić).
Więc rozwiązanie tego problemu? Obsłuż zdarzenie KeyDown dla komórki i jeśli klucz jest klawiszem Enter, ustaw flagę, która powstrzyma BeginEdit przed uruchomieniem na pierwszej komórce. Teraz klawisz Enter zachowuje się tak, jak powinien.
Na początek dodaj następujący styl do DataGrid:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Zastosuj ten styl do właściwości „CellStyle” w kolumnach, dla których chcesz włączyć jedno kliknięcie.
Następnie w kodzie za tobą znajduje się następujący kod w programie obsługi GotFocus (zwróć uwagę, że używam tutaj VB, ponieważ tego właśnie chciał nasz klient żądający siatki danych jednym kliknięciem jako język programowania):
Private _endEditing As Boolean = False
Private Sub DataGrid_GotFocus(ByVal sender As Object, ByVal e As RoutedEventArgs)
If Me._endEditing Then
Me._endEditing = False
Return
End If
Dim cell = TryCast(e.OriginalSource, DataGridCell)
If cell Is Nothing Then
Return
End If
If cell.IsReadOnly Then
Return
End If
DirectCast(sender, DataGrid).BeginEdit(e)
.
.
.
Następnie dodajesz procedurę obsługi dla zdarzenia KeyDown:
Private Sub DataGridCell_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.Key = Key.Enter Then
Me._endEditing = True
End If
End Sub
Teraz masz DataGrid, który nie zmienił żadnego podstawowego zachowania implementacji gotowej do użycia, a mimo to obsługuje edycję jednym kliknięciem.
Wiem, że trochę się spóźniłem na imprezę ale miałem ten sam problem i wymyśliłem inne rozwiązanie:
public class DataGridTextBoxColumn : DataGridBoundColumn
{
public DataGridTextBoxColumn():base()
{
}
protected override FrameworkElement GenerateEditingElement(DataGridCell cell, object dataItem)
{
throw new NotImplementedException("Should not be used.");
}
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var control = new TextBox();
control.Style = (Style)Application.Current.TryFindResource("textBoxStyle");
control.FontSize = 14;
control.VerticalContentAlignment = VerticalAlignment.Center;
BindingOperations.SetBinding(control, TextBox.TextProperty, Binding);
control.IsReadOnly = IsReadOnly;
return control;
}
}
<DataGrid Grid.Row="1" x:Name="exportData" Margin="15" VerticalAlignment="Stretch" ItemsSource="{Binding CSVExportData}" Style="{StaticResource dataGridStyle}">
<DataGrid.Columns >
<local:DataGridTextBoxColumn Header="Sample ID" Binding="{Binding SampleID}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Analysis Date" Binding="{Binding Date}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Test" Binding="{Binding Test}" IsReadOnly="True"></local:DataGridTextBoxColumn>
<local:DataGridTextBoxColumn Header="Comment" Binding="{Binding Comment}"></local:DataGridTextBoxColumn>
</DataGrid.Columns>
</DataGrid>
Jak widać, napisałem własną DataGridTextColumn dziedzicząc wszystko vom z DataGridBoundColumn. Zastępując metodę GenerateElement i zwracając kontrolkę Textbox bezpośrednio w tym miejscu, metoda generowania elementu edycji nigdy nie zostanie wywołana. W innym projekcie użyłem tego do zaimplementowania kolumny Datepicker, więc powinno to działać również dla pól wyboru i kombinacji.
Wydaje się, że to nie wpływa na resztę zachowań datagridów. Przynajmniej nie zauważyłem żadnych skutków ubocznych ani nie otrzymałem żadnych negatywnych opinii do tej pory.
Proste rozwiązanie, jeśli nie masz nic przeciwko temu, że Twoja komórka pozostaje polem tekstowym (bez rozróżnienia między trybem edycji a trybem bez edycji). W ten sposób edycja jednym kliknięciem działa po wyjęciu z pudełka. Działa to również z innymi elementami, takimi jak combobox i przyciski. W przeciwnym razie użyj rozwiązania poniżej aktualizacji.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Wypróbowałem wszystko, co znalazłem tutaj i w Google, a nawet próbowałem tworzyć własne wersje. Ale każda odpowiedź / rozwiązanie działało głównie dla kolumn z polami tekstowymi, ale nie działało ze wszystkimi innymi elementami (polami wyboru, polami kombinacji, kolumnami z przyciskami), a nawet uszkodziło te inne kolumny elementów lub miało inne efekty uboczne. Dzięki firmie Microsoft za sprawienie, że datagrid zachowywał się w tak brzydki sposób i zmusił nas do tworzenia takich hacków. Z tego powodu zdecydowałem się stworzyć wersję, którą można zastosować ze stylem bezpośrednio do kolumny pola tekstowego bez wpływu na inne kolumny.
Użyłem tego rozwiązania i odpowiedzi @ my i zmodyfikowałem je tak, aby były załączonym zachowaniem. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Dodaj ten styl. Jest BasedOn
to ważne, gdy używasz niektórych fantazyjnych stylów dla swojego datagrid i nie chcesz ich stracić.
<Window.Resources>
<Style x:Key="SingleClickEditStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Setter Property="local:DataGridTextBoxSingleClickEditBehavior.Enable" Value="True" />
</Style>
</Window.Resources>
Zastosuj styl CellStyle
do każdego z nich DataGridTextColumns
:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
A teraz dodaj tę klasę do tej samej przestrzeni nazw, co Twój MainViewModel (lub do innej przestrzeni nazw. Ale wtedy będziesz musiał użyć innego przedrostka przestrzeni nazw niż local
). Witamy w brzydkim, standardowym świecie kodu dołączonych zachowań.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace YourMainViewModelNameSpace
{
public static class DataGridTextBoxSingleClickEditBehavior
{
public static readonly DependencyProperty EnableProperty = DependencyProperty.RegisterAttached(
"Enable",
typeof(bool),
typeof(DataGridTextBoxSingleClickEditBehavior),
new FrameworkPropertyMetadata(false, OnEnableChanged));
public static bool GetEnable(FrameworkElement frameworkElement)
{
return (bool) frameworkElement.GetValue(EnableProperty);
}
public static void SetEnable(FrameworkElement frameworkElement, bool value)
{
frameworkElement.SetValue(EnableProperty, value);
}
private static void OnEnableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is DataGridCell dataGridCell)
dataGridCell.PreviewMouseLeftButtonDown += DataGridCell_PreviewMouseLeftButtonDown;
}
private static void DataGridCell_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
EditCell(sender as DataGridCell, e);
}
private static void EditCell(DataGridCell dataGridCell, RoutedEventArgs e)
{
if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
return;
if (dataGridCell.IsFocused == false)
dataGridCell.Focus();
var dataGrid = FindVisualParent<DataGrid>(dataGridCell);
dataGrid?.BeginEdit(e);
}
private static T FindVisualParent<T>(UIElement element) where T : UIElement
{
var parent = VisualTreeHelper.GetParent(element) as UIElement;
while (parent != null)
{
if (parent is T parentWithCorrectType)
return parentWithCorrectType;
parent = VisualTreeHelper.GetParent(parent) as UIElement;
}
return null;
}
}
}
<DataGridComboBoxColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="cal:Message.Attach"
Value="[Event MouseLeftButtonUp] = [Action ReachThisMethod($source)]"/>
</Style>
</DataGridComboBoxColumn.CellStyle>
public void ReachThisMethod(object sender)
{
((System.Windows.Controls.DataGridCell)(sender)).IsEditing = true;
}