none
Datagrid und MVVM - nach dem Hinzufügen einer Zelle BeginEdit auf die erste Spalte RRS feed

  • Frage

  • Hallo Forum,

    ich benutze MVVM/WPF für einen View, dem das entsprechende ViewModel zugrunde liegt.

    1.) ich habe einen Button eingefügt, der eine neue Zeile hinzufügt (das Command also an das ViewModel weiterleitet und dort wird in der Observable Collection ein neues Item erzeugt) - das klappt so weit gut - ich will das jedoch bei einem bestimmten KeyDown Event auslösen (zB Enter oder Tab an einer bestimmten Stelle)

    2.) die neue Zeile wird hinzugefügt - ich habe auch das Attribut SelectedItem="{Binding Path=CurrentItem, Mode=TwoWay}" in Verwendung und die Zeile wird markiert - für den Nutzer ist es aber sinnfrei, da er dann erst recht auf die erste Zelle doppelklicken muss um das Editieren zu starten. (es ist die ganze Zeile markiert).
    Ich hätte gedacht ich kann das "AddingNewItem" Event verwenden, dieses triggert aber nur wenn es dem User erlaubt ist selbst über das Datagrid Zeilen hinzuzufügen und nicht wenn ich das über MVVM und einen Button selbst tue.

    Für Infos wäre ich dankbar.

    Donnerstag, 26. September 2013 12:43

Antworten

  • Konnte es jetzt mit Hilfe eines Beispiels aus dem Web lösen.
    Hinzufügen und Löschen von Einträgen durch den Nutzer erlauben.

    Für Tab:

    using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; namespace mynamespace.Helpers { public class NewLineOnTabBehavior : Behavior<DataGrid> { private bool _monitorForTab; protected override void OnAttached() { base.OnAttached(); AssociatedObject.BeginningEdit += _EditStarting; AssociatedObject.CellEditEnding += _CellEnitEnding; AssociatedObject.PreviewKeyDown += _KeyDown; } private void _EditStarting(object sender, DataGridBeginningEditEventArgs e) { if (e.Column.DisplayIndex == AssociatedObject.Columns.Count - 1) _monitorForTab = true; } private void _CellEnitEnding(object sender, DataGridCellEditEndingEventArgs e) { _monitorForTab = false; } private void _KeyDown(object sender, KeyEventArgs e) { if (_monitorForTab && e.Key == Key.Tab) { AssociatedObject.CommitEdit(DataGridEditingUnit.Row, false); } } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.BeginningEdit -= _EditStarting; AssociatedObject.CellEditEnding -= _CellEnitEnding; AssociatedObject.PreviewKeyDown -= _KeyDown; _monitorForTab = false; } } }

    Eingebunden wird dies durch: (Caliburn.micro - deswegen Binding über x:Name)

    <DataGrid Grid.Row="0" HorizontalAlignment="Stretch" CurrentItem="{Binding Currentwaregroup}" VerticalAlignment="Stretch" x:Name="waregroups" AutoGenerateColumns="False"
                      CanUserAddRows="True" SelectionMode="Single" CanUserDeleteRows="True" KeyboardNavigation.TabNavigation="Contained">            
                <i:Interaction.Behaviors>
                    <icustom:NewLineOnTabBehavior />
                </i:Interaction.Behaviors>     

    Wenn der Nutzer Zeilen löschen will wird zuerst geprüft ob diese Zeilen als FK in Verwendung sind:

    using System;
    using System.Collections.Generic;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    
    namespace mynamespace.Helpers
    {
        class DelKeyEventTrigger : EventTrigger
        {      
    
                public DelKeyEventTrigger()
                    : base("KeyDown")
                {
                }
    
                protected override void OnEvent(EventArgs eventArgs)
                {
                    var e = eventArgs as KeyEventArgs;
                    if (e != null && e.Key == Key.Delete)
                        this.InvokeActions(eventArgs);                    
                }
           
        }
    }

    eingebunden innerhalb des <Grid> tags (wieder caliburn.micro) durch:

     
     <i:Interaction.Triggers>
                    <icustom:DelKeyEventTrigger EventName="PreviewKeyDown" AutomationProperties.ItemStatus="">
                        <cal:ActionMessage MethodName="keyDown">                                                                       
                            <cal:Parameter Value="$eventArgs" />                        
                        </cal:ActionMessage>
                    </icustom:DelKeyEventTrigger>
           

    • Als Antwort markiert diBianco Dienstag, 1. Oktober 2013 09:29
    Dienstag, 1. Oktober 2013 09:28

Alle Antworten

  • Ich habe jetzt zwar einen Ansatz - das Problem ist aber, dass beim Hinzufügen der neuen Zeile der Fokus nicht auf der eigentlichen Zelle liegt (im Debugger ist die korrekte Zelle gesetzt - also der Name) - der Fokus liegt aber seltsamerweise nicht auf der Zelle sondern am "Row" Selektor - heißt wenn ich einmalig "Tab" drücke springt der Fokus korrekt in die erste Spalte der neuen Zeile und ich kann direkt hineinschreiben.

    private void waregroups_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
            {
                if (e.AddedCells.Count == 0) return;
                //first focus the grid
                waregroups.Focus();
                DataGridCellInfo cellInfo = e.AddedCells[0]; 
                //set the cell to be the active one
                waregroups.CurrentCell = cellInfo;
                //scroll the item into view
                waregroups.ScrollIntoView(cellInfo.Item);
                //begin the edit
                waregroups.BeginEdit();
            }




    • Bearbeitet diBianco Freitag, 27. September 2013 09:17 Codeverbesserung
    Freitag, 27. September 2013 09:05
  • Hallo diBanco,
    probiers mal so:

    Kurz zur Erläuterung: Die Property "Items" ist die ItemsSource für das DataGrid. Und Person ist das Model das im DataGrid gespeichert wird.

     public partial class Window1 : Window {
          public Window1() {
             this.InitializeComponent();
             this.DataContext = new ViewModel();
          }
          public ViewModel Model {
             get {
                return this.DataContext as ViewModel;
             }
          }
          private void DataGrid_KeyDown(object sender, KeyEventArgs e) {
             if (e.Key == Key.Enter) {
                this.Model.Items.Add(new Person());
                this.dgv.Focus(); 
             }
          }
       }
    Ich hoffe ich konnte dir weiterhelfen...

    Viele Grüße Holger M. Rößler

    Freitag, 27. September 2013 13:40
  • Danke, das mit dem Key habe ich inzwischen gelöst indem ich im XAML einen Interaction Trigger und damit zusammenhängend eine Custom Klasse verwendet die filtert ob der "korrekte" Key gedrückt wurde.
    (in diesem Fall für PreviewKeyDownEvent, da Enter bzw. Tab sonst nicht abgefangen werden)

    Damit kann ich den Befehl an das ViewModel weitergeben, sehen ob die currentRow gleich der letzten Row (in der ObservableCollection) ist und entsprechend handeln.
    Was ich leider nicht geschafft habe ist es an Tab zu binden, weil ich im ViewModel keine Möglichkeit habe zu sehen ob ich auch in der "letzten Spalte" bin (die sich ändern kann) ohne zumindest Teile des Views zu übergeben, was ja gegen den Grundsatz von MVVM sprechen würde.

    Samstag, 28. September 2013 05:42
  • Konnte es jetzt mit Hilfe eines Beispiels aus dem Web lösen.
    Hinzufügen und Löschen von Einträgen durch den Nutzer erlauben.

    Für Tab:

    using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; namespace mynamespace.Helpers { public class NewLineOnTabBehavior : Behavior<DataGrid> { private bool _monitorForTab; protected override void OnAttached() { base.OnAttached(); AssociatedObject.BeginningEdit += _EditStarting; AssociatedObject.CellEditEnding += _CellEnitEnding; AssociatedObject.PreviewKeyDown += _KeyDown; } private void _EditStarting(object sender, DataGridBeginningEditEventArgs e) { if (e.Column.DisplayIndex == AssociatedObject.Columns.Count - 1) _monitorForTab = true; } private void _CellEnitEnding(object sender, DataGridCellEditEndingEventArgs e) { _monitorForTab = false; } private void _KeyDown(object sender, KeyEventArgs e) { if (_monitorForTab && e.Key == Key.Tab) { AssociatedObject.CommitEdit(DataGridEditingUnit.Row, false); } } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.BeginningEdit -= _EditStarting; AssociatedObject.CellEditEnding -= _CellEnitEnding; AssociatedObject.PreviewKeyDown -= _KeyDown; _monitorForTab = false; } } }

    Eingebunden wird dies durch: (Caliburn.micro - deswegen Binding über x:Name)

    <DataGrid Grid.Row="0" HorizontalAlignment="Stretch" CurrentItem="{Binding Currentwaregroup}" VerticalAlignment="Stretch" x:Name="waregroups" AutoGenerateColumns="False"
                      CanUserAddRows="True" SelectionMode="Single" CanUserDeleteRows="True" KeyboardNavigation.TabNavigation="Contained">            
                <i:Interaction.Behaviors>
                    <icustom:NewLineOnTabBehavior />
                </i:Interaction.Behaviors>     

    Wenn der Nutzer Zeilen löschen will wird zuerst geprüft ob diese Zeilen als FK in Verwendung sind:

    using System;
    using System.Collections.Generic;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    
    namespace mynamespace.Helpers
    {
        class DelKeyEventTrigger : EventTrigger
        {      
    
                public DelKeyEventTrigger()
                    : base("KeyDown")
                {
                }
    
                protected override void OnEvent(EventArgs eventArgs)
                {
                    var e = eventArgs as KeyEventArgs;
                    if (e != null && e.Key == Key.Delete)
                        this.InvokeActions(eventArgs);                    
                }
           
        }
    }

    eingebunden innerhalb des <Grid> tags (wieder caliburn.micro) durch:

     
     <i:Interaction.Triggers>
                    <icustom:DelKeyEventTrigger EventName="PreviewKeyDown" AutomationProperties.ItemStatus="">
                        <cal:ActionMessage MethodName="keyDown">                                                                       
                            <cal:Parameter Value="$eventArgs" />                        
                        </cal:ActionMessage>
                    </icustom:DelKeyEventTrigger>
           

    • Als Antwort markiert diBianco Dienstag, 1. Oktober 2013 09:29
    Dienstag, 1. Oktober 2013 09:28