Hallo zusammen,
nachdem Google und Copilot nicht mehr weiter helfen können, hoffe ich hier auf Antworten.
Es handelt sich um eine WPF Anwendung für ein Nagelstudio mit dem DesignPattern MVVM.
SORRY jetzt schon, falls die Vorgeschichte so lang ist, aber ich denke es ist wichtig zu verstehen was da wo aufgerufen wird.
Ich habe ein MainWindow, dazu das MainWindowViewModel. Im MainWindow habe ich im oberen und im linken Bereich Buttons zum Navigieren von Views welche ich im rechten Bereich anzeige:
<!-- Ansicht -->
<Grid Grid.Row="1">
<Grid.Resources>
<DataTemplate DataType="{x:Type viewmodels:KundenViewModel}">
<views:KundenView></views:KundenView>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:KundeAddEditViewModel}">
<views:KundeAddEditView></views:KundeAddEditView>
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding CurrentView}">
</ContentControl>
</Grid>
Das ganze funktioniert auch inzwischen 😃
Im KundenView habe ich ein DataGrid wo ich alle Kunden anzeige.
ItemsSource="{Binding KundenItems}" SelectedItem="{Binding SharedService.SelectedKundeStore.SelectedKunde}"
Wenn KundenView aufgerufen wird:
public partial class KundenView : UserControl
{
public KundenView()
{
InitializeComponent();
this.Loaded += KundenView_Loaded;
}
private void KundenView_Loaded(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"DataContext: {this.DataContext}");
}
}
Ist der DataContext= KundenViewModel
Das ist auch korrekt und es werden auch alle Kunden angezeigt.
Über einen Button "Neuer Kunde" im MainWindow kann ich einen neuen Kunden erstellen. Dazu wird die View "KundeAddEditView" im "CurrentView" angezeigt. Das funktioniert auch, mit einem Klick auf den Button "Speichern" in der KundeAddEditView wird der neue Kunde auch in die DatenBank gespeichert und mein CurrentView wechselt zurück zur KundenView wo ich alle Kunden sehe inklusive den neu erstellten. Alles Gut alles Top. Testweise habe ich eine 2. Command, die zwar das gleiche macht, aber einen SelectedKunde mitübernimmt. Wenn ich also einen Kunden aus der DataGrid selektiere und ich auf "Neuer Kunde" klicke, wird wieder die gleiche View "KundeAddEditView", mit all den Werten des selektierten Kunden, angezeigt. Passt auch, alles super bis jetzt.
Aber nun zum Problem:
Mein DataGrid mit den Kunden hat auch noch eine Spalte mit 2 Buttons: Bearbeiten, Löschen.
<Button Style="{StaticResource gridEditButton}"
Command="{Binding DataContext.NavigateKundeEditCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding}">
<Icon:PackIconMaterial Kind="PencilOutline" Style="{StaticResource gridButtonIcon}"/>
</Button>
Ich wollte wie in meinem Test oben wieder die KundeAddEditView aufrufen und den Kunden bearbeiten. Leider wird das Command einfach nicht ausgelöst.
Ich habe auch das probiert:
<Button Style="{StaticResource gridEditButton}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor},Path=DataContext.NavigateKundeEditCommand}"
CommandParameter="{Binding}">
<Icon:PackIconMaterial Kind="PencilOutline" Style="{StaticResource gridButtonIcon}"/>
</Button>
und das:
<Button Style="{StaticResource gridEditButton}"
Command="{Binding ElementName=_Root, Path=DataContext.NavigateKundeEditCommand}"
CommandParameter="{Binding}">
<Icon:PackIconMaterial Kind="PencilOutline" Style="{StaticResource gridButtonIcon}"/>
</Button>
KundeView, was ja ein UserControl ist, habe ich auch noch das im UserControl Header meiner KundenView.xaml eingefügt
<UserControl
.
.
.
d:DataContext="{d:DesignInstance Type=ViewModels:KundenViewModel}"
x:Name="_Root"
>
Wollte versuchen ob ich das Command über den ElementName auslösen kann. Leider auch nichts.
Und das habe ich natürlich auch versucht:
<Button Style="{StaticResource gridEditButton}"
Command="{Binding NavigateKundeEditCommand}"
CommandParameter="{Binding}">
<Icon:PackIconMaterial Kind="PencilOutline" Style="{StaticResource gridButtonIcon}"/>
</Button>
Der letzte Versuch hat mir zumindest BindingFehler ausgegeben. Die Eigenschaft "NavigateKundeEditCommand" wurde im Objekt vom Typ "Kunde" (meine Model-Klasse) nicht gefunden.
Und ich denke da liegt der Hund begraben. Das Binding vom Command zeigt mir glaub ich nicht auf mein KundenViewModel (wo ja mein NavigateKundeEditCommand ist).
Ich verstehe es nicht was ich falsch mache. Da die 3 oberen Command-Binding Versuche keine BindingFehler haben und ich jetzt auch nicht der Ober C#/VisualStudio Profi bin weiß ich auch nicht genau wo diese Versuche hin "gebindet" haben oder bzw ich da detailierter debuggen könnte.
Es gäbe theoretisch noch einen Plan B und einen Plan C:
Plan B: einen zentralen Button zum Bearbeiten, dann sollte theoretisch das Binding wie mit der DataGrid funktionieren. Sieht aber nicht so Stylisch aus 😉
Plan C: die einzelnen Kunden in der DataGrid in ein "KundenListeItemViewModel" zu verpacken und dort mein Command zu feuern. Aber dann müsste ich viel im Code umbasteln und ich hab ein bisschen Angst es dann zu "verschlimmbessern" und alles kaputt zu machen 😃
Ich hoffe das Problem detailiert genug beschrieben zu haben und freue mich über JEDE Hilfe!
Vielleicht hat ja der ein oder andere ein ähnliches oder vielleicht sogar das gleiche Problem gehabt.
Vielen herzlichen Dank schon mal im Voraus!
UPDATE:
Ich habe das Problem gefunden, bin aber ins nächste kleine Problem gelaufen.
In diesem DataGrid benutze ich eigene Styles, wo auch das Event "PreviewMouseLeftButtonDown" auslöse. Weil ich dort eine Methode ausführe die mir mit LinksKlick eine Zeile selektiert und mit erneutem Linksklick auf die gleiche Zeile die Auswahl wieder aufhebt. Ich habe es versucht zu umgehen indem ich nach der Spalte frage wo meine Buttons liegen, damit da nichts passiert, aber leider sobald der Handler fertig ist, wird auch der Command Teil übersprungen. Habe auch veruscht als Click Event auszulösen, aber PreviewMouseLeftButtonDown gewinnt immer und alle anderen Ereignisse scheinen unberührt zu bleiben. Der Klick auf einen Button in der Spalte "Aktionen" markiert mir jetzt nicht mehr die Zeile aber der Button bleibt unberührt.
public void DoSelectedRow(object sender, MouseButtonEventArgs e)
{
DataGridCell? cell = sender as DataGridCell;
if (cell != null && !cell.IsEditing)
{
// Überprüfe, ob die Zelle aus der "Aktionen"-Spalte stammt
if (cell.Column.Header != null && cell.Column.Header.ToString() == "Aktionen")
{
// Verhindere die Auswahl der Zeile
e.Handled = true;
return;
}
DataGridRow? row = FindVisualParent<DataGridRow>(cell);
if (row != null)
{
row.IsSelected = !row.IsSelected;
e.Handled = true;
}
}
}
public static Parent? FindVisualParent<Parent>(DependencyObject child) where Parent : DependencyObject
{
DependencyObject parentObject = child;
while (!((parentObject is System.Windows.Media.Visual)
|| (parentObject is System.Windows.Media.Media3D.Visual3D)))
{
if (parentObject is Parent || parentObject == null)
{
return parentObject as Parent;
}
else
{
parentObject = ((FrameworkContentElement)parentObject).Parent;
}
}
parentObject = VisualTreeHelper.GetParent(parentObject);
if (parentObject is Parent || parentObject == null)
{
return parentObject as Parent;
}
else
{
return FindVisualParent<Parent>(parentObject);
}
}
Ich möchte den User nicht dazu zwingen mit STRG+Klick auf die Zeile die Auswahl aufzuheben, also hab ich unschön ein event "PreviewMouseRightButtonDown" eingebunden, dass mir die Auswahl der Zeile aufhebt. Es ist scheinbar die einzige Möglichkeit, weil LeftButtonDown zu mächtig ist 😃
Hab ausßerdem Plan C, KundeItemViewModl durchgeführt. Das hat dann letztendlich funktioniert mit dem binden und ich kann jetzt bearbeiten.
Falls jemand eine schönere/bessere/effektivere Variante hat, bitte gerne 😃
Cool wäre es "unselect Row" mit klick auf einer freien Fläche, aber ich habe nichts "brauchbares" googlen können.