none
WPF Datagrid (MVVM): Wie kann man einzelne Zellen markieren und Zellinhalt auswerten? RRS feed

  • Frage

  • Hallo zusammen,

    ich habe ein Datagrid, welches mit der Itemsource an eine DataTable gebunden ist. Die Property AutoGenerateColumns="True".

    Ich möchte nun eine einzelne Zelle in dem Datagrid auswählen und den Zellinhalt auswerten, um z.B. weitere Informationen zu diesem Inhalt in einem anderen UserControl anzuzeigen.

    Leider bleibt beim Selektieren immer die ganze Row selektiert. Es soll jedoch so sein, dass nur die gerade ausgewählte Zelle (wie bei Excel) markiert werden soll. Wie bekommt man so etwas "gebacken"?

    Weiter habe ich zwar den Event "SelectedCellsChanged". Dieser wird jedoch nur gefeuert, wenn man auf eine Zelle in einer anderen Row klickt. Ich möchte aber auch auf mehrere unterschiedliche Zellen in einer Row klicken können und deren Zellinhalt unterschiedlich auswerten können.

    Kann mir hier einer zu diesen Problemen eine Hilfestellung geben?

    Gruß Jürgen

    Freitag, 27. Januar 2017 06:49

Antworten

  • Hi Jürgen,
    hinzu kommt, dass Du in ExtendedDataGridJS auf eine relative URI zugreifst, die so im Designmodus nicht zur Verfügung steht. Wenn Du das auf Absolute änderst, kommt dieser Fehler nicht mehr. Die Frage ist allerdings, warum Du das gleiche Ressourcenverzeichnis sowohl im App.xaml als auch im Code in ExtendedDataGridJS hinzufügst.

    Wenn in Absolut geändert, dann kommen weitere Fehler (NullReferenceException). Da ist der Wurm drin.

    Unklar ist auch, warum bei solchen komplexem Programm nicht durchgehend mit MVVM gearbeitet wird und die Ereignisbearbeitungen nicht in Attached Behaviors ausgelagert werden.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks



    Montag, 30. Januar 2017 13:23

Alle Antworten

  • Hi Jürgen,
    über "Style TargetType="DataGridCell" ..." kannst Du auf die Zelldarstellung einwirken. Da kann man beispielsweise die Hintergrundfarbe setzen. Zu klären dabei ist lediglich, woher die Information kommen soll. Das kann beispielsweise eine Liste sein, die mit dem Click o.ä. gefüllt wird.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 27. Januar 2017 08:22
  • Hallo Peter,

    ich bin mittlerweile so weit, dass ich mir eine HelperClass erstellt habe, in der ich Methoden implementiert habe, die die MouseButtonEventArgs beim Klick auf eine Zelle eines DataGrids auswerten und dann
    - die Row und den RowIndex
    - die Property der angeklickten Zelle (i.d.R. die SpaltenÜberschrift)
    - den Value der angeklickten Zelle
    zurückgeben. Und das Klappt super!

    Einzig der DataGridCellStyle ist noch zu ändern.

    Dein Vorschlag über einen Style muss noch umgesetzt werden.

    Gruß Jürgen

    Freitag, 27. Januar 2017 10:06
  • Hi Jürgen,
    zur Demo habe ich mal ein Beispiel erstellt. In jeder angeklickten Zelle im DataGrid wird der Hintergrund auf "Rot" gesetzt.

    XAML:

    <Window x:Class="WpfApplication1.Window56"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication1"
            mc:Ignorable="d"
            Title="Window56" Height="300" Width="300">
      <Window.Resources>
        <local:Window56VM x:Key="vm"/>
        <Style TargetType="DataGridCell" >
          <Setter Property="local:Window56DataGridCellBehavior.UseBehavior" Value="True"/>
        </Style>
      </Window.Resources>
      <Grid DataContext="{StaticResource vm}">
        <DataGrid Grid.Row="1"  AutoGenerateColumns="False" ItemsSource="{Binding View1}">
          <DataGrid.Columns>
            <DataGridTextColumn Binding="{Binding Vorn}" Header="Vorname"/>
            <DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
            <DataGridTextColumn Binding="{Binding Info}" Header="Info"/>
          </DataGrid.Columns>
        </DataGrid>
      </Grid>
    </Window>

    Dazu der ViewModel:

    using System;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Media;
    using System.Windows.Interactivity;
    
    namespace WpfApplication1
    {
      public class Window56VM : INotifyPropertyChanged
      {
        #region  Properties
    
        private CollectionViewSource cvs1 = new CollectionViewSource();
    
        public ICollectionView View1
        {
          get
          {
            if (cvs1.Source == null) cvs1.Source = GetData();
            return cvs1.View;
          }
        }
        private ObservableCollection<Window56Data> GetData()
        {
          ObservableCollection<Window56Data> liste = new ObservableCollection<Window56Data>();
          for (int i = 1; i < 100; i++)
          {
            liste.Add(new Window56Data() { ID = i, Name = $"Name {i}", Vorn = $"Vorname {i}", Info = $"Info {i}" });
          }
          return liste;
        }
        #endregion
    
        #region  OnPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
        #endregion
      }
      public class Window56Data
      {
        public int ID { get; set; }
        public String Vorn { get; set; }
        public String Name { get; set; }
        public String Info { get; set; }
      }
    
      public class Window56DataGridCellBehavior : Behavior<DataGridCell>
      {
        public static DependencyProperty UseBehaviorProperty =
          DependencyProperty.RegisterAttached("UseBehavior", typeof(Boolean),
                                              typeof(Window56DataGridCellBehavior),
                                              new UIPropertyMetadata(false, OnUseBehaviorChanged));
    
        public static bool GetUseBehavior(DependencyObject obj) { return (bool)(obj.GetValue(UseBehaviorProperty)); }
        public static void SetUseBehavior(DependencyObject obj, bool value) { obj.SetValue(UseBehaviorProperty, value); }
    
        private static void OnUseBehaviorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
          if (e.NewValue.Equals(true)) Interaction.GetBehaviors(d).Add(new Window56DataGridCellBehavior());
        }
        protected override void OnAttached()
        {
          base.OnAttached();
          var dgc = this.AssociatedObject;
          dgc.GotFocus += (object sender, RoutedEventArgs e) =>
          {
            dgc.Background = Brushes.Red;
          };
        }
        protected override void OnDetaching() { base.OnDetaching(); }
      }
    }
    


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Freitag, 27. Januar 2017 12:49
  • Hallo Peter,

    muchos gracias. Top, funktioniert soweit, aber noch nicht zufriedenstellend.

    Leider wird der Background nicht zurück gesetzt wenn man eine andere Zelle auswählt:

    Außerdem wird beim Markieren einer Zeile immer die Zelle in der ersten Spalte markiert:

    Ich habe mein Testprojekt mal in meine DropBox gestellt. Du kannst es dir hier herunterladen.

    Wie du dann sehen kannst, ist der Fehler mit der nicht gefundenen Ressource immer noch da, obwohl man das Projekt ganz normal starten kann. Da komme ich auch nicht mehr weiter :-( Vielleicht findest du ja zufälligerweise die Ursache?!

    Kann man, statt den Hintergrund der angeklickten Zelle zu ändern, den "Border" der Zelle ändern?

    Wie man sieht, wird in dem Datagrid immer die letzte leere Spalte mit anzeigt. Wie kann man das unterbinden???

    Viele Grüße

    Jürgen

    Samstag, 28. Januar 2017 10:13
  • Zusatz:

    mit dem Zurücksetzen der BackgroundColor habe ich schon einen Lösungsanstz gefunden. Ich habe die Bevaior um:

    dgc.LostFocus += (object sender, RoutedEventArgs e) =>
                {
                    dgc.Background = null;
                };

    erweitert.

    Allerdings wird dann der Background beim Markieren der Row mit den vorher mal angeklickten Zellen nicht richtig angezeigt:

    Samstag, 28. Januar 2017 10:34
  • Hi Jürgen,
    setze im Style mal Background und Foreground:

        <Style TargetType="DataGridCell" >
          <Setter Property="local:Window56DataGridCellBehavior.UseBehavior" Value="True"/>
          <Setter Property="Background" Value="White"/>
          <Setter Property="Foreground" Value="Black" />
        </Style>


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Samstag, 28. Januar 2017 14:12
  • Hi Jürgen,
    Dein hochgeladenes Projekt bringt immer noch im Designmodus Fehler. Die sollten erst einmal beseitigt werden. Wenn ich Zeit habe, schaue ich mir das nochmals an.

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 29. Januar 2017 08:58
  • Hallo Peter,

    das wäre super, wenn du wegen des Fehlers im Designmodus noch mal gucken könntest. Ich habe mir da schon einen "Wolf" gesucht.

    Das muss wohl was mit dem Theming zu tun haben, obwohl ich die Ressourcen-Dictionarys in die App.xaml gepackt habe.

    Das mit dem Setzen des ForeGrounds und des Backgrounds habe ich in dem GenericStyle des ExtendedDataGridJS eingebaut.

    Gruß Jürgen

    Sonntag, 29. Januar 2017 11:26
  • Hi Jürgen,
    in diesem Thread hattest Du am 9. Januar 2017 16:39 geschrieben, dass das Problem gelöst sei? Was hast Du danach wieder verändert, so dass es jetzt wieder auftritt?

    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 29. Januar 2017 17:48
  • Hallo Peter,

    ja stimmt, das hatte ich leider voreiligerweise geschrieben, weil ich da nur das mit deinem Hinweis auf das CustomDG ausprobiert hatte.

    Leider trat der Designerfehler dann aber unter Nutzung des eigentlichen ExtendedDataGridJS wieder auf.

    Da habe ich nicht viel verändert, nur die ResourcesDictionarys für die betreffenden xaml-Resourcen in die App.xaml eingefügt.

    Gruß Jürgen

    Sonntag, 29. Januar 2017 19:08
  • Hi Jürgen,
    im Projekt sind zyklische Beziehungen, die zur Laufzeit zwar funktionieren, zur Designzeit aber scheinbar nicht ordentlich aufgelöst werden können:

    1. ExtendedDataGridJS erbt von CopyDg

    2. CopyDg erbt von CustomDg

    3. In CustomDg wird auf Instanzen vom Typ ExtendedDataGridJS zugegriffen.

    So etwas würde ich generell vermeiden bzw. nur Polymorphismus nutzen.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks

    Sonntag, 29. Januar 2017 21:40
  • Hi Jürgen,
    hinzu kommt, dass Du in ExtendedDataGridJS auf eine relative URI zugreifst, die so im Designmodus nicht zur Verfügung steht. Wenn Du das auf Absolute änderst, kommt dieser Fehler nicht mehr. Die Frage ist allerdings, warum Du das gleiche Ressourcenverzeichnis sowohl im App.xaml als auch im Code in ExtendedDataGridJS hinzufügst.

    Wenn in Absolut geändert, dann kommen weitere Fehler (NullReferenceException). Da ist der Wurm drin.

    Unklar ist auch, warum bei solchen komplexem Programm nicht durchgehend mit MVVM gearbeitet wird und die Ereignisbearbeitungen nicht in Attached Behaviors ausgelagert werden.


    --
    Viele Grüsse
    Peter Fleischer (ehem. MVP)
    Meine Homepage mit Tipps und Tricks



    Montag, 30. Januar 2017 13:23
  • Hallo Peter,

    danke für deine ganze Mühe. Der Hinweis von dir mit UriKind.Absolute hat bei mir den Fehler im Designer jetzt beseitigt.

    Ich hatte die Ressourcen testweise in die App.xaml gelegt. Ich habe diese jetzt da wieder entfernt.

    Das mit dem BackGround hat sich auch geklärt. Ich habe im DataGrid SelectionUnit="CellOrRowHeader" gesetzt. Damit ist deine Behavior für die DataGridCell nicht mehr erforderlich.

    Naja, zumindest läuft das ganze jetzt bei mir im Hauptprogramm bisher noch ohne Fehler/Exceptions.

    Dann kann ich diesen Thread erstmal abschließen.

    Viele Grüße

    Jürgen

    Montag, 30. Januar 2017 15:57