none
Verhalten des Datagrids wenn es den Fokus bekommt RRS feed

  • Frage

  • Hallo,

    ich habe ein Problem mit dem Verhalten des Datagrids, wenn es erneut den Fokus (per Tab-Taste)bekommt. Es wird stets die erste (sichtbare) Zelle angesprungen. Dieses Verhalten ist recht verwirrend - erst recht bei SelectionUnit = Cell, da dann zwar die letzte fokussierte Zelle selektiert bleibt, aber die erste Zelle wieder den Fokus hat. Ausserdem ändert sich dadurch mein aktuelles Data-Item. Für unsere Kunden ist das schlichtweg ein Programmfehler (für mich eigentlich auch).

    Denn nun muss man erst wieder neu positionieren und so wirklich erwartet man den Wechsel wohl nicht. (Ich muss hier auch anmerken, dass es sich um Rechnungswesen-Software handelt und Buchhalter fast ausschließlich mit der Tastatur arbeiten und gerade beider Erfassung von Belegen nicht unbedingt auf den Bildschirm schauen - da kann das sogar fatale Auswirkungen haben!).
    Nun - wie dem auch sei - ich habe versucht bei (Preview-)GotFocus wieder auf die letzte Zelle/Zeile zu positionieren. Aber abgesehen davon, dass das recht unangenehm zu programmieren ist, habe ich nicht geschafft zu verhindern, dass auf jeden Fall erst mal die besagt erste Zelle angesprungen wird - was man zwar nicht sieht, aber der Item-Wechsel wirkt sich halt in meinem ViewModel aus (automatischer Commit mit optionaler Fehlermeldung und so... :-( ).

    Hat dazu jemand eine Idee? Evtl. habe ich ja etwas übersehen...
    Danke und Gruß
    Detlef

    Mittwoch, 12. Dezember 2012 10:35

Antworten

  • So, ich habe die Lösung nun selbst gefunden. Ich habe Resourcen mit ein bisschen Code-Behind dafür verwendet. Damit gilt das dann für jedes Datagrid innerhalb der Anwendung und es ist keine Ableitung notwendig. Hier zunächst mal das Resource-XAML (die hatte ich schon vorher - ich beschränke mich aber hier auf das Problem...):

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:controls="clr-namespace:VWE.Framework.Controls"
                        xmlns:classes="clr-namespace:VWE.Framework.Classes"
                        x:Class="VWE.Framework.Ressources.DataGridRessources">
        <Style TargetType="{x:Type DataGrid}">
            <EventSetter Event="Loaded" Handler="GridLoaded"/>
        </Style>    
    </ResourceDictionary>

    Und die zugehörige Code-Datei:

        public partial class DataGridRessources
        {
            private DataGrid mGrid;
            private DataGridCellInfo? mLastCell;
            private delegate void RefocusDelegate(DataGridCellInfo value);
    
            public void GridLoaded(object sender, RoutedEventArgs e)
            {
                if (!(sender is DataGrid))
                {
                    return;
                }
                mGrid = (sender as DataGrid);
                mGrid.PreviewGotKeyboardFocus += mGrid_PreviewGotKeyboardFocus;
                mGrid.PreviewLostKeyboardFocus += mGrid_PreviewLostKeyboardFocus;
            }
           void mGrid_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
                mLastCell = null;
                DataGridCell newCell = null;
                if (e.NewFocus is DataGridCell)
                {
                    newCell = (e.NewFocus as DataGridCell);
                }
                if ((e.NewFocus as FrameworkElement).Parent != null)
                {
                    if ((e.NewFocus as FrameworkElement).Parent is DataGridCell)
                    {
                        newCell = (e.NewFocus as FrameworkElement).Parent as DataGridCell;
                    }
                }
                if (newCell != null)
                {
                    if (mGrid.Columns.Contains(newCell.Column))
                    {
                        //Andere Zelle im gleichen Grid? Dann müssen wir uns nichts merken.
                        return;
                    }
    
                }
                //Ok, es scheint als würde das Grid den Fokus komplett verlieren...
                mLastCell = mGrid.CurrentCell;
            }
    
            void mGrid_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
            {
                if (mLastCell != null)
                {
                    mGrid.Dispatcher.BeginInvoke(new RefocusDelegate(RefocusGrid),
                        System.Windows.Threading.DispatcherPriority.Background, new object[1] { mLastCell });
                    mLastCell = null;
                    e.Handled = true;
                }
            }
    
            private void RefocusGrid(DataGridCellInfo value)
            {
                DataGridCell aCell = DataGridRessources.GetDataGridCellFromCellInfo(mGrid, value);
                if (aCell != null)
                {
                    aCell.Focus();
                }
                mGrid.CurrentCell = value;
            }
    
    Die grundsätzliche Idee ist, sich die letzte "CurrentCell" zu merken, bevor das Datagrid komplett den Fokus verliert. "Komplett", weil auch das navigieren von Zelle zu Zelle entsprechende Events auslöst.
    In diesem Fall sind die Events uninteressant. Hat das Grid also den Fokus komplett verloren und soll ihn erneut bekommen, wird das Event abgebrochen (damit eben NICHT die erste Zelle angesprungen wird).
    Dann kommt ein Delegat ins Spiel (RefocusGrid). Dort wird dann die entsprechende Zelle fokussiert und die CurrentCell gesetzt (muß man vielleicht nicht unbedingt - ist in meinem Fall aber wichtig).
    "GetDataGridCellFromCellInfo" ist eine Hilfsmethode, die anhand einer "DataGridCellInfo"-Instanz die entsprechende DataGridCell zurückgibt.
    • Als Antwort markiert Detlef Ernst Montag, 17. Dezember 2012 10:48
    Montag, 17. Dezember 2012 10:48