ユーザーがセルを編集モードにして、セルが含まれている行をシングルクリックで強調表示できるようにしたいのですが。デフォルトでは、これはダブルクリックです。
これをオーバーライドまたは実装するにはどうすればよいですか?
回答:
これが私がこの問題をどのように解決したかです:
<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>
このDataGridはCollectionViewSource(ダミーのPersonオブジェクトを含む)にバインドされています。
魔法はそこで起こります:DataGridCell.Selected = "DataGridCell_Selected"。
DataGridセルのSelectedイベントをフックし、DataGridでBeginEdit()を呼び出すだけです。
以下は、イベントハンドラのコードビハインドです。
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のプロパティをに設定すると、すでに選択されている行の問題を回避できますCell
。
grd.BeginEdit(e)
、そのセルのTextBoxにフォーカスを設定します。どうやってやるの?FindName("txtBox")
DataGridCellとDataGridの両方を呼び出してみましたが、nullが返されました。
Micael Bergeronからの回答は、私にとって有効な解決策を見つけるための良い出発点でした。既に編集モードになっている同じ行のセルに対してもシングルクリック編集を許可するには、少し調整する必要がありました。SelectionUnit Cellを使用することは私にとって選択肢ではありませんでした。
行のセルが初めてクリックされたときにのみ発生するDataGridCell.Selectedイベントを使用する代わりに、DataGridCell.GotFocusイベントを使用しました。
<DataGrid DataGridCell.GotFocus="DataGrid_CellGotFocus" />
そうすると、常に正しいセルがフォーカスされ、編集モードになりますが、セル内のコントロールはフォーカスされません。これは次のように解決しました
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>
コードビハインド:
//
// 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からのソリューションは私にとってはうまくいきましたが、ResourceDictionaryで定義されたスタイルを使用してすべてのDataGridに対して有効にしました。リソースディクショナリでハンドラーを使用するには、ハンドラーに分離コードファイルを追加する必要があります。方法は次のとおりです。
これは 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>
ルート要素のx:Class属性に注意してください。クラスファイルを作成します。この例では、DataGridStyles.xaml.csになります。このコードを中に入れます:
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šanKneževićの提案に基づいてこの方法を好みます。それだけです))
<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のIsEditingプロパティをTrueに設定するトリガーを追加することで解決しました。それは私の問題のほとんどを解決しました。コンボボックスでも動作します。
<Style TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
MVVMでシングルクリックでセルを編集することを探していますが、これは別の方法です。
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>
EditCellOnSingleClickBehaviorクラスは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);
}
}
出来上がり!
user2134678の回答には2つの問題があります。1つは非常にマイナーであり、機能的な影響はありません。もう1つはかなり重要です。
最初の問題は、GotFocusが実際にはDataGridCellではなくDataGridに対して呼び出されていることです。XAMLのDataGridCell修飾子は冗長です。
私が答えで見つけた主な問題は、Enterキーの動作が壊れていることです。Enterキーを押すと、通常のDataGridの動作で、現在のセルの下にある次のセルに移動します。ただし、実際に舞台裏で行われるのは、GotFocusイベントが2回呼び出されることです。現在のセルがフォーカスを失ったとき、および新しいセルがフォーカスを取得したとき。ただし、BeginEditがその最初のセルで呼び出されている限り、次のセルはアクティブ化されません。要は、ワンクリックで編集できるということですが、文字通りグリッドをクリックしていない人は不便を感じることになるでしょう。ユーザーインターフェイスの設計者は、すべてのユーザーがマウスを使用していると想定しないでください。(キーボードのユーザーは、Tabを使用してそれを回避することができますが、それでも、必要のないはずのフープを飛び越えていることを意味します。)
この問題の解決策は?セルのイベントKeyDownを処理し、KeyがEnterキーの場合は、BeginEditが最初のセルで発火しないようにするフラグを設定します。Enterキーは正常に動作します。
まず、DataGridに次のスタイルを追加します。
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" x:Key="SingleClickEditingCellStyle">
<EventSetter Event="KeyDown" Handler="DataGridCell_KeyDown" />
</Style>
</DataGrid.Resources>
ワンクリックを有効にする列の「CellStyle」プロパティにそのスタイルを適用します。
次に、コードビハインドのGotFocusハンドラーに次のコードを記述します(ここではVBを使用していることに注意してください。これは、「ワンクリックデータグリッドリクエスト」クライアントが開発言語として求めていたためです)。
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)
.
.
.
次に、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
これで、すぐに使用できる実装の基本的な動作を変更せず、シングルクリック編集をサポートするDataGridができました。
私はパーティーに少し遅れていることを知っていますが、同じ問題があり、別の解決策を考え出しました:
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>
ご覧のとおり、DataGridBoundColumnからすべてを継承する独自のDataGridTextColumnを作成しました。GenerateElementメソッドをオーバーライドしてTextboxコントロールをすぐに返すことにより、編集要素を生成するためのメソッドが呼び出されることはありません。別のプロジェクトでこれを使用してDatepicker列を実装したので、これはチェックボックスとコンボボックスでも機能するはずです。
これは、残りのデータグリッドの動作に影響を与えるようには見えません。
セルがテキストボックスのままで問題ない場合の簡単な解決策(編集モードと非編集モードを区別しない)。このように、シングルクリック編集はそのまま使用できます。これは、コンボボックスやボタンなどの他の要素でも機能します。それ以外の場合は、更新プログラムの下のソリューションを使用してください。
<DataGridTemplateColumn Header="My Column header">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MyProperty } />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
私はここで、そしてGoogleで見つけたすべてを試し、自分のバージョンを作成してみました。しかし、すべての回答/解決策は主にテキストボックス列で機能しましたが、他のすべての要素(チェックボックス、コンボボックス、ボタン列)では機能しませんでした。データグリッドを醜い方法で動作させ、ハックを作成するように強制してくれたマイクロソフトに感謝します。そのため、他の列に影響を与えることなく、テキストボックス列にスタイルを直接適用できるバージョンを作成することにしました。
私はこのソリューションと@myの答えを使用し、それらを添付された動作になるように変更しました。 http://wpf-tutorial-net.blogspot.com/2016/05/wpf-datagrid-edit-cell-on-single-click.html
このスタイルを追加します。これBasedOn
は、データグリッドに派手なスタイルを使用していて、それらを失いたくない場合に重要です。
<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>
次のようにスタイルCellStyle
をそれぞれに適用しますDataGridTextColumns
。
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="My Header" Binding="{Binding Comment}" CellStyle="{StaticResource SingleClickEditStyle}" />
</DataGrid.Columns>
</DataGrid>
次に、このクラスをMainViewModelと同じ名前空間(または別の名前空間。ただし、以外の名前空間接頭辞を使用する必要があります)に追加しますlocal
。アタッチされた動作の醜い定型コードの世界へようこそ。
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;
}