Ottenere l'oggetto contenuto in una cella di un Datagrid WPF
Quote from Miky on 12/11/2020, 17:06Ciao
Come da oggetto, voi come fate?
Tenendo conto che:
1 - Ogni cella puo' contenere un oggetto di cui ignoro il tipo.
2 - Ogni riga puo' contenere un oggetto differente di cui ignoro il tipo.
Sembra assurdo ma e' così 😉
Parlo solo per la Datagrid di WPF, non Winform o Syncfusion.
Ciao
Come da oggetto, voi come fate?
Tenendo conto che:
1 - Ogni cella puo' contenere un oggetto di cui ignoro il tipo.
2 - Ogni riga puo' contenere un oggetto differente di cui ignoro il tipo.
Sembra assurdo ma e' così 😉
Parlo solo per la Datagrid di WPF, non Winform o Syncfusion.
Quote from sabrina_c on 12/11/2020, 17:39Indipendentemente dalla Datagrid che usi, una Grid WPF ha una property ItemsSource a cui assegni la collection che visualizzi
Quindi gli oggetti ce li hai già. Perché usualmente metti in Binding sull'oggetto CurrentItem o SelectedItem che trovi sempre sulla Datagrid un opportuna property del tuo datacontext.Pertanto l'oggetto correntemente attivo ce lo hai di certo.
Se gli oggetti sono eterogenei e li vedi nella grid, di ciascun oggetto vedi il risultato del metodo ToString della classe.
Se sono Interfacce, comunque tornando al CurrentItem o SelectedItem puoi ottenere quello che vuoi.
Se non ti serve l'oggetto presente nella riga ma il contenuto della cella attiva puoi implementare questo event handler:
private void GrActivities_CurrentCellChanged(object sender, EventArgs e) { try { DataGrid grid = sender as DataGrid; if (grid != null) { if (grid.CurrentCell.Item is ActivityItem) { CurrentActivity = grid.CurrentCell.Item as ActivityItem; } } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "ERRORE", MessageBoxButton.OK, MessageBoxImage.Error); } } Ho il sospetto che l'oggetto CurrentCell ti permetta l'accesso non solo all'Item collegato ma anche al contenuto della cella. Saluti
Indipendentemente dalla Datagrid che usi, una Grid WPF ha una property ItemsSource a cui assegni la collection che visualizzi
Quindi gli oggetti ce li hai già. Perché usualmente metti in Binding sull'oggetto CurrentItem o SelectedItem che trovi sempre sulla Datagrid un opportuna property del tuo datacontext.
Pertanto l'oggetto correntemente attivo ce lo hai di certo.
Se gli oggetti sono eterogenei e li vedi nella grid, di ciascun oggetto vedi il risultato del metodo ToString della classe.
Se sono Interfacce, comunque tornando al CurrentItem o SelectedItem puoi ottenere quello che vuoi.
Se non ti serve l'oggetto presente nella riga ma il contenuto della cella attiva puoi implementare questo event handler:
private void GrActivities_CurrentCellChanged(object sender, EventArgs e) { try { DataGrid grid = sender as DataGrid; if (grid != null) { if (grid.CurrentCell.Item is ActivityItem) { CurrentActivity = grid.CurrentCell.Item as ActivityItem; } } } catch (Exception ex) { MessageBox.Show(ex.ToString(), "ERRORE", MessageBoxButton.OK, MessageBoxImage.Error); } } Ho il sospetto che l'oggetto CurrentCell ti permetta l'accesso non solo all'Item collegato ma anche al contenuto della cella. Saluti
Quote from Miky on 12/11/2020, 18:46Grazie
Il codice che mi hai girato lo ho già piu' o meno testato ma non fa al caso mio, almeno in questa situazione, in quanto tutta la Grid e' generata da oggetti annidati e non da oggetti con relative property.
Questa che mi indichi sarebbe la parte di codice fondamentale:
CurrentActivity = grid.CurrentCell.Item as ActivityItem; Che però restituisce l'oggetto in binding con la riga e non con la cella.Se avessi una classe ActivityItem mi basterebbe recuperare il valore della property interessata da CurrentActivity come tu indichi.
Nel mio caso non ci sono property ma solo oggetti che sono stati messi in binding via codice in n DataGridBoundColumn le quali vengono "costruite" con altri valori di settings recuperati per generarle.
Finora sfruttavo il nome degli Header per recuperare l'oggetto con un codice tipo questo:
CurrentRow = grid.CurrentCell.Item as MyClass;
CurrentActivity = CurrentRow[Header.ToString()];
Ma visto che ho introdotto i settings per poter modificare appunto il testo dell'header, ora tale testo puo' non combaciare con l'oggetto da recuperare.
La soluzione che tenterò e' un'altra.
Ovvero creare un binding anche per l'Header il cui oggetto conterrà sia il testo da visualizzare che il testo utile per cercare l'oggetto contenuto nella cella.
Avrei preferito una strada piu' "leggera" ma non trovo come recuperare il valore della cella con altri metodi.
Grazie
Il codice che mi hai girato lo ho già piu' o meno testato ma non fa al caso mio, almeno in questa situazione, in quanto tutta la Grid e' generata da oggetti annidati e non da oggetti con relative property.
Questa che mi indichi sarebbe la parte di codice fondamentale:
CurrentActivity = grid.CurrentCell.Item as ActivityItem; Che però restituisce l'oggetto in binding con la riga e non con la cella.
Se avessi una classe ActivityItem mi basterebbe recuperare il valore della property interessata da CurrentActivity come tu indichi.
Nel mio caso non ci sono property ma solo oggetti che sono stati messi in binding via codice in n DataGridBoundColumn le quali vengono "costruite" con altri valori di settings recuperati per generarle.
Finora sfruttavo il nome degli Header per recuperare l'oggetto con un codice tipo questo:
CurrentRow = grid.CurrentCell.Item as MyClass;
CurrentActivity = CurrentRow[Header.ToString()];
Ma visto che ho introdotto i settings per poter modificare appunto il testo dell'header, ora tale testo puo' non combaciare con l'oggetto da recuperare.
La soluzione che tenterò e' un'altra.
Ovvero creare un binding anche per l'Header il cui oggetto conterrà sia il testo da visualizzare che il testo utile per cercare l'oggetto contenuto nella cella.
Avrei preferito una strada piu' "leggera" ma non trovo come recuperare il valore della cella con altri metodi.
Quote from sabrina_c on 13/11/2020, 09:55Ho verificato che usando questo:
grid.CurrentCell.Column.Header Header è un object, pertanto se usi un oggetto per l'header in cui metti la property con il titolo e la property con il nome del campo, usando il Tostring x restituire il titolo la colonna dovrebbe essere riportata correttamente e dall'oggetto header puoi ricavare quello che ti serve. Ovviamente se insisti ad usare la grid standard, io uso la grid Syncfusion che oltre a fornirmi il modo per risalire direttamente all'oggetto nella cella e ha già i dati relativi al mapping nei dati della colonna. saluti
Ho verificato che usando questo:
grid.CurrentCell.Column.Header Header è un object, pertanto se usi un oggetto per l'header in cui metti la property con il titolo e la property con il nome del campo, usando il Tostring x restituire il titolo la colonna dovrebbe essere riportata correttamente e dall'oggetto header puoi ricavare quello che ti serve. Ovviamente se insisti ad usare la grid standard, io uso la grid Syncfusion che oltre a fornirmi il modo per risalire direttamente all'oggetto nella cella e ha già i dati relativi al mapping nei dati della colonna. saluti
Quote from Miky on 13/11/2020, 14:21Certo
Ma come dicevo uso oggetti generici e quindi non ho specifiche property a cui fare riferimento.
Diciamo che sto realizzando una grid che eredita da DataGrid per un framework basato su oggetti di dati gerarchici generici .
Usare la grid di Syncfusion mi vincolerebbe a tale azienda.
Per intenderci, i miei dati sono tutti (ho semplificato la classe):
MyClass
{
string Key{get;set}
object Value{get;set}
}
Quindi:
MyClass1.Key = "Nome_Colonna"
MyClass1.Value= ObservableCollection<MyClass>
MyClass1.Value[0].Key = "Header"
MyClass1.Value[0].Value= "Nome Colonna"
MyClass1.Value[1].Key = "Type"
MyClass1.Value[1].Value= "ComboBox"
Quindi dovrei passare alla DataGridColumHeader l'oggeto MyClass1 il quale però, con il metodo ToString() non restituisce "Nome Colonna".
Facendo come dici tu, dovrei creare una classe MyHeader(MyClass myClass) con override del ToString() che restituisce MyClass.Value["Header"] e passarla a DataGridColumnHeader.
Certamente si puo' fare ma questo mi vincola a creare una classe MyHeader e quindi poi dovrei creare mille altre classi per ogni setting da gestire.
Se trovo il modo di risolvere la path, credo sia piu' elegante e generico qualcosa del genere:
var path = ??; //Trovare quale sia la Path da assegnare per recuperare il figlio con l'header. Tipo "MyClass["Header"].Value"
var binding = new Binding(path);
binding.Source = MyClass1 ;
BindingOperations.SetBinding(this, DataGridBoundColumn.HeaderProperty, binding);Questo perche', risolvendo questa situazione, risolvo anche tutti gli altri possibili binding futuri nel caso volessi impostare altri settings partendo dalla struttura dati che sto utilizzando.
Ora devo capire come risolvere la path.
Certo
Ma come dicevo uso oggetti generici e quindi non ho specifiche property a cui fare riferimento.
Diciamo che sto realizzando una grid che eredita da DataGrid per un framework basato su oggetti di dati gerarchici generici .
Usare la grid di Syncfusion mi vincolerebbe a tale azienda.
Per intenderci, i miei dati sono tutti (ho semplificato la classe):
MyClass
{
string Key{get;set}
object Value{get;set}
}
Quindi:
MyClass1.Key = "Nome_Colonna"
MyClass1.Value= ObservableCollection<MyClass>
MyClass1.Value[0].Key = "Header"
MyClass1.Value[0].Value= "Nome Colonna"
MyClass1.Value[1].Key = "Type"
MyClass1.Value[1].Value= "ComboBox"
Quindi dovrei passare alla DataGridColumHeader l'oggeto MyClass1 il quale però, con il metodo ToString() non restituisce "Nome Colonna".
Facendo come dici tu, dovrei creare una classe MyHeader(MyClass myClass) con override del ToString() che restituisce MyClass.Value["Header"] e passarla a DataGridColumnHeader.
Certamente si puo' fare ma questo mi vincola a creare una classe MyHeader e quindi poi dovrei creare mille altre classi per ogni setting da gestire.
Se trovo il modo di risolvere la path, credo sia piu' elegante e generico qualcosa del genere:
var path = ??; //Trovare quale sia la Path da assegnare per recuperare il figlio con l'header. Tipo "MyClass["Header"].Value"
var binding = new Binding(path);
binding.Source = MyClass1 ;
BindingOperations.SetBinding(this, DataGridBoundColumn.HeaderProperty, binding);
Questo perche', risolvendo questa situazione, risolvo anche tutti gli altri possibili binding futuri nel caso volessi impostare altri settings partendo dalla struttura dati che sto utilizzando.
Ora devo capire come risolvere la path.
Quote from Miky on 13/11/2020, 15:26Per chiudere il post ho risolto in un altro modo anche se non sono contento
La mia grid usa colonne che ereditano da DataGridBoundColum
Detto ciò, e' stato molto semplice aggiungere una proprietà RawColumName alla MyColumn e quindi:
var key = column.RawColumnName;
selectedItem = SelectedItem as MyClass;
currentCell = selectedItem[key];Per quando riguarda il sistema per risolvere la path di un binding (per come serve a me), indagherò meglio, ma dalle prime ricerche non sembra possibile.
Per chiudere il post ho risolto in un altro modo anche se non sono contento
La mia grid usa colonne che ereditano da DataGridBoundColum
Detto ciò, e' stato molto semplice aggiungere una proprietà RawColumName alla MyColumn e quindi:
var key = column.RawColumnName;
selectedItem = SelectedItem as MyClass;
currentCell = selectedItem[key];
Per quando riguarda il sistema per risolvere la path di un binding (per come serve a me), indagherò meglio, ma dalle prime ricerche non sembra possibile.
Quote from sabrina_c on 15/11/2020, 17:56Solo una risposta tecnica,
il Forum è molto semplice, il moderatore non "chiude" i post.
Il moderatore interviene solo se i post sono giudicati non pertinenti o se il contenuto è offensivo.
saluti
Solo una risposta tecnica,
il Forum è molto semplice, il moderatore non "chiude" i post.
Il moderatore interviene solo se i post sono giudicati non pertinenti o se il contenuto è offensivo.
saluti