Kullanıcının hücreyi düzenleme moduna getirebilmesini ve hücrenin içerdiği satırı tek bir tıklama ile vurgulayabilmesini istiyorum. Varsayılan olarak, bu çift tıklamadır.
Bunu nasıl geçersiz kılabilirim veya uygularım?
Kullanıcının hücreyi düzenleme moduna getirebilmesini ve hücrenin içerdiği satırı tek bir tıklama ile vurgulayabilmesini istiyorum. Varsayılan olarak, bu çift tıklamadır.
Bunu nasıl geçersiz kılabilirim veya uygularım?
Yanıtlar:
İşte bu sorunu nasıl çözdüm:
<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>
Bu DataGrid, bir CollectionViewSource'a bağlıdır (sahte Kişi nesneleri içerir).
Sihir orada gerçekleşir: DataGridCell.Selected = "DataGridCell_Selected" .
Ben sadece DataGrid hücresinin Seçili Olayını bağlarım ve DataGrid üzerinde BeginEdit () i çağırırım.
İşte olay işleyicinin arkasındaki kod:
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
DataGrid üzerindeki özelliği olarak ayarlayarak, önceden seçilmiş satır problemini aşabilirsiniz Cell
.
grd.BeginEdit(e)
, bunu hücrede TextBox odağı olmasını istiyorum. Bunu nasıl yapabilirim? FindName("txtBox")
Hem DataGridCell hem de DataGrid'i aramayı denedim , ancak benim için boş döndürüyor.
Micael Bergeron'un cevabı, benim için işe yarayan bir çözüm bulmam için iyi bir başlangıçtı. Zaten düzenleme modunda olan aynı satırdaki Hücreler için de tek tıklamayla düzenlemeye izin vermek için, biraz ayarlamam gerekiyordu. SelectionUnit Cell kullanmak benim için bir seçenek değildi.
Yalnızca bir satırın hücresine tıklandığında ilk kez tetiklenen DataGridCell.Selected Event'i kullanmak yerine DataGridCell.GotFocus Olayını kullandım.
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
Bunu yaparsanız, her zaman doğru hücre odaklı ve düzenleme modunda olursunuz, ancak hücrede hiçbir kontrol odaklanmayacaktır, bu şekilde çözdüm
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;
}
From: 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 ARKASI:
//
// 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;
}
Http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing'in çözümü benim için harika çalıştı, ancak bunu bir ResourceDictionary içinde tanımlanan bir Stil kullanarak her DataGrid için etkinleştirdim. İşleyicileri kaynak sözlüklerinde kullanmak için ona bir arka plan kod dosyası eklemeniz gerekir. İşte bunu nasıl yapıyorsun:
Bu bir DataGridStyles.xaml Kaynak Sözlüğüdür:
<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>
Kök öğedeki x: Class niteliğine dikkat edin. Bir sınıf dosyası oluşturun. Bu örnekte, DataGridStyles.xaml.cs olacaktır . Bu kodu içine koyun:
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.
}
Dušan Knežević'in önerisine dayanarak bu yolu tercih ediyorum. tıklarsın ve işte bu kadar)
<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>
DataGridCell'in IsEditing özelliğini, fare üzerindeyken True olarak ayarlayan bir tetikleyici ekleyerek çözdüm. Sorunlarımın çoğunu çözdü. Kombine kutularla da çalışır.
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
MVVM'de tek tıklamayla hücre düzenlemeyi arıyorum ve bu, bunu yapmanın başka bir yolu.
Xaml'de davranış ekleme
<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>
EditCellOnSingleClickBehavior sınıfı System.Windows.Interactivity.Behavior'ı genişletir;
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!
User2134678'in cevabında iki sorun var. Biri çok küçüktür ve işlevsel bir etkisi yoktur. Diğeri oldukça önemli.
İlk sorun, GotFocus'un gerçekte DataGridCell'e değil, aslında DataGrid'e karşı çağrılmasıdır. XAML'deki DataGridCell niteleyicisi fazlalıktır.
Cevapta bulduğum ana sorun, Enter tuşu davranışının bozuk olmasıdır. Enter, normal DataGrid davranışında sizi geçerli hücrenin altındaki sonraki hücreye taşımalıdır. Ancak, perde arkasında gerçekte olan, GotFocus olayı iki kez çağrılacak. Bir zamanlar mevcut hücre odak kaybederken ve bir kez yeni hücre odak kazanıyor. Ancak bu ilk hücrede BeginEdit çağrıldığı sürece, sonraki hücre asla etkinleştirilmeyecektir. Sonuç olarak, tek tıklamayla düzenlemeniz var, ancak tam anlamıyla ızgaraya tıklamayan herkes rahatsız olacak ve bir kullanıcı arayüzü tasarımcısı tüm kullanıcıların fare kullandığını varsaymamalıdır. (Klavye kullanıcıları Tab'ı kullanarak bir nevi etrafından dolaşabilirler, ancak bu yine de ihtiyaç duymamaları gereken çemberlerden atladıkları anlamına gelir.)
Peki bu sorunun çözümü? Hücre için KeyDown olayını işleyin ve Anahtar Enter tuşu ise, BeginEdit'in ilk hücreye ateşlenmesini durduran bir bayrak ayarlayın. Şimdi Enter tuşu olması gerektiği gibi davranır.
Başlamak için DataGrid'inize aşağıdaki Stili ekleyin:
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
Bu stili, tek tıklamayı etkinleştirmek istediğiniz sütunların "CellStyle" özelliğine uygulayın.
Ardından arkadaki kodda, GotFocus işleyicinizde aşağıdakiler bulunur (burada VB'yi kullandığımı unutmayın, çünkü "tek tıklamayla veri ızgarası isteyen" istemcimizin geliştirme dili olarak istediği buydu):
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)
.
.
.
Ardından, KeyDown olayı için işleyicinizi eklersiniz:
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
Artık, kullanıma hazır uygulamanın temel davranışını değiştirmemiş ve tek tıklamayla düzenlemeyi destekleyen bir DataGrid'e sahipsiniz.
Partiye biraz geç kaldığımı biliyorum ama aynı sorunu yaşadım ve farklı bir çözüm buldum:
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>
Gördüğünüz gibi DataGridBoundColumn'daki her şeyi miras alan kendi DataGridTextColumn'umu yazdım. GenerateElement Metodunu geçersiz kılarak ve orada bir Textbox kontrolü döndürerek, Düzenleme Öğesini oluşturma Metodu hiçbir zaman çağrılmaz. Farklı bir projede bunu bir Datepicker sütunu uygulamak için kullandım, bu yüzden bu onay kutuları ve birleşik giriş kutuları için de çalışmalıdır.
Bu, datagrid davranışlarının geri kalanını etkilemiyor gibi görünüyor ... En azından şimdiye kadar herhangi bir yan etki fark etmedim veya herhangi bir olumsuz geri bildirim almadım.
Hücrenizin bir metin kutusu olarak kalmasını istiyorsanız basit bir çözüm (düzenleme ve düzenleme dışı mod arasında ayrım yoktur). Bu şekilde, tek tıklamayla düzenleme, kutunun dışında çalışır. Bu, birleşik giriş kutusu ve düğmeler gibi diğer öğelerle de çalışır. Aksi takdirde güncellemenin altındaki çözümü kullanın.
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Burada ve Google'da bulduğum her şeyi denedim ve hatta kendi sürümlerimi oluşturmayı bile denedim. Ancak her yanıt / çözüm esas olarak metin kutusu sütunları için çalıştı, ancak diğer tüm öğelerle (onay kutuları, birleşik giriş kutuları, düğmeler sütunları) çalışmadı, hatta diğer öğe sütunlarını kırdı veya başka yan etkileri oldu. Datagrid'in bu kadar çirkin davranmasını sağladığı ve bizi bu hack'leri oluşturmaya zorladığı için Microsoft'a teşekkürler. Bu nedenle, bir metin kutusu sütununa diğer sütunları etkilemeden doğrudan bir stil ile uygulanabilecek bir sürüm yapmaya karar verdim.
Bu çözümü ve @ benim yanıtını kullandım ve bunları ekli bir davranış olacak şekilde değiştirdim. http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
Bu Stili ekleyin. BasedOn
Bazı kullandığınızda önemlidir fantezi stilleri sizin datagrid'e için ve bunları kaybetmek istiyorum yok.
<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>
Stili aşağıdaki gibi CellStyle
her birine uygulayın DataGridTextColumns
:
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
Ve şimdi bu sınıfı MainViewModel'inizle aynı ad alanına ekleyin (veya farklı bir Ad Alanı. Ama o zaman bundan başka bir ad alanı öneki kullanmanız gerekecek local
). Ekli davranışların çirkin standart kod dünyasına hoş geldiniz.
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;
}