Fragensteller
Binding Listbox

Frage
-
Hallo Community,
hab mal wieder ein Verständnisproblem welches ich allein nicht lösen kann.
Ich habe ein Viewmodel mit einer ObsevableCollection in der Adressdaten enthalten sind.
Diese Property binde ich an eine Listbox. In der Listbox befindet sich ein Expander, darin ein Grid mit TextBoxen and denen die einzelnen Were wie Staße, Plz usw. gebunden sind. Ohne Binding der Listbox and die Property der Collection sind die Texboxen und Textblöcke zu sehen. Nach dem Bindig ist alles weg.
Ist mein Binding falsch?
Hier die Codeteile:
Gruß
Jürgen
<ListBox Name="lbxAddInfo" ItemsSource="{Binding Companydet}" Height="150" Background="Transparent" Grid.Row="1"> <ListBox.ItemTemplate> <DataTemplate> <Expander x:Name="addInformation" IsExpanded="True"> <Expander.Header> <StackPanel> <TextBlock Text="Zusätzliche Informationen"/> </StackPanel> </Expander.Header> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="5.407"/> <ColumnDefinition Width="95.593"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock Text="UST-Id" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"/> <TextBlock x:Name="tbxTaxID" Text= "{Binding Taxid, Mode=TwoWay}" Grid.Column="2" Grid.Row="0"/> <TextBlock Text="Steuer-Nr." Grid.Column="3" Grid.Row="0"/> <TextBlock x:Name="tbxTaxNr" Text="{Binding Taxnbr, Mode=TwoWay}" Grid.Column="4" Grid.Row="0"/> <TextBlock Text="Branche" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/> <TextBox x:Name="tbxBranche" Text="{Binding Branche, Mode=TwoWay}" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="2" Grid.Row="1"/> <TextBlock Text="Zahlungsziel" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="3" Grid.Row="1"/> <TextBox x:Name="tbxPaymentGoal" Text="{Binding Payment, Mode=TwoWay}" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="4" Grid.Row="1"/> </Grid> </Expander> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
private ObservableCollection<contactModel> _Companydet;public ObservableCollection<contactModel> Companydet
{
get { return _Companydet; }
set { _Companydet = value;
OnPropertyChanged("Companydet");
}
}private void PgDetailComp(string companyid)
{ const string conString = "xxxx"; Companydet = new ObservableCollection<contactModel>(); using (SqlConnection connection = new SqlConnection(conString)) { try { connection.Open(); string firstSql = @"SELECT * FROM dbo.Company where company_id = @company_id"; // Company_id schein leer zu sein SqlCommand cmd = new SqlCommand(firstSql, connection); //Parsen der übergebenen ID von String in Int und übergabe an SQLPrameter if (int.TryParse(companyid, out int id)) { var companyid1 = new SqlParameter("@company_id", SqlDbType.Int); cmd.Parameters.Add(companyid1); cmd.Parameters["@company_id"].Value = id; } else { MessageBox.Show("Fehler beim Parsen"); } using (SqlDataReader reader = cmd.ExecuteReader()) { try { while (reader.Read()) { Companydet.Add(new contactModel() { CompanyID = reader.GetInt32(0), CompanyName = reader.GetString(1), Street = reader.GetString(2).ToString(), City = reader.GetString(3), Zip = reader.GetString(4), County = reader.GetString(5), Country = reader.GetString(6), Status = reader.GetString(7), Relation = reader.GetString(8), Lead_Level = reader.GetString(9), Phone = reader.GetString(10), Fax = reader.GetString(11), Web = reader.GetString(12), Mail = reader.GetString(13), Tag_1 = reader.GetString(14), Tag_2 = reader.GetString(15), Tag_3 = reader.GetString(16), Tag_4 = reader.GetString(17), Tag_5 = reader.GetString(18), Vat = reader.GetString(19), Branche = reader.GetString(20), Payment = reader.GetInt32(21), Tax_Nr = reader.GetString(22), Newsletter = reader.GetInt32(23), Revenue = reader.GetDouble(24), Tradefair = reader.GetInt32(25), Buyingbehavor = reader.GetString(26), Comments = reader.GetString(27), CreateAt = reader.GetDateTime(28), CreateBy = reader.GetString(29), ChangeAt = reader.GetDateTime(30), ChangeBy = reader.GetString(31) }); } reader.Close(); } catch (Exception ex) { MessageBox.Show("Fehler", ex.Message); } connection.Close(); } } catch (SqlException ex) { MessageBox.Show("Can't open connection to Database! ", ex.Message); } } return; }
Alle Antworten
-
Hallo Jürgen,
wo und wann rufst Du denn überhaupt die Methode "PgDetailComp" auf? Ohne die ist "Companydet" null und dann kann auch nichts angezeigt werden.
Ich für meinen Teil würde in dieser Methode nicht direkt mit der Eigenschaft "Companydet" arbeiten, sondern nur mit einer lokalen Variablen und dann ggfs. ganz am Ende die Variable der Eigenschaft zuordnen. Da Du die ObservableCollection ja x mal änderst, würden dann ggfs. auch immer wieder Aktualisierungsroutinen wg. der Bindungen laufen, was wohl eher nicht gewollt ist.
Falls das nur ein einziges mal gefüllt werden muss, kannst Du im Getter von Companydet prüfen, ob _Companydet null ist, falls ja, _Companydet =PgDetailComp() aufrufen und dann entsprechend zurückgeben.
Ansonsten ruf die Methode dann an den Stellen auf, an denen Du sie brauchst.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport- Bearbeitet Stefan Falz Dienstag, 13. November 2018 11:51
-
PgDetailComp wird weiter oben aufgerufen.
public PgDetailCompViewModel() { } public PgDetailCompViewModel(string companyID) { PgDetailComp(companyID); //Companydet1 = Companydet; }
die CompanyID kommt aus dem ViweModel der MainPage. Ist wahrscheinlich umständlich aber funktioniert.
Ich habe geprüft ob companydet gefüllt ist. companydet ist nicht null.
Aber stimmt, die Collection ändert sich immer, wenn ich in einem DataGrid eine andere Zeile auswähle.
Aber nichts desto trotz, aktuell stimmt mit dem Binding etwas nicht. Und da hänge ich in den Seilen.
Eigentlich sollte es funktionieren.
-
Hi,
aus dem Bauch raus, würde ich sagen, du solltest "Companydet = new ObservableCollection<contactModel>();" nur einmal im Constructor des ViewModel aufrufen.
Ansonsten erzeugst du immer eine neue Instanz der Collection, die dann nicht mehr gebunden ist.
Gruß
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
-
Hallo Jürgen,
da ich nicht weiß, auf welche Antwort Du dich mit "funktioniert nicht" beziehst, antworte ich mal so generell:
Doch, das funktioniert schon (sowohl Stefan Krömers als auch mein Vorschlag). Wenn man es denn richtig macht.
Da (zumindest für mich) aber unklar ist, wo Du was wie genau wann machst, versuch doch zuerst mal, das Problem auf etwas kleineres runterzubrechen und die grundsätzliche Vorgehensweise anhand eines vollständigen Beispiels zu zeigen.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET (2001-2018)
https://www.asp-solutions.de/ - IT Beratung, Softwareentwicklung, Remotesupport -
Hi,
ich habe aus Deinen Codestücken mal eine vollständige Demo erstellt. Und diese funktioniert problemlos. Du müsstest - wie Stefan schreibt - Dich etwas klarer (mehr Code) ausdrücken. Hier meine Demo:XAML:
<Window x:Class="WpfApp1.Window77" 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:WpfApp1" mc:Ignorable="d" Title="Window77" Height="450" Width="800"> <Window.Resources> <local:Window77VM x:Key="vm"/> </Window.Resources> <Grid DataContext="{StaticResource vm}"> <ListBox Name="lbxAddInfo" ItemsSource="{Binding Companydet}" Height="150" Background="Transparent" Grid.Row="1"> <ListBox.ItemTemplate> <DataTemplate> <Expander x:Name="addInformation" IsExpanded="True"> <Expander.Header> <StackPanel> <TextBlock Text="Zusätzliche Informationen"/> </StackPanel> </Expander.Header> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="5.407"/> <ColumnDefinition Width="95.593"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> <ColumnDefinition Width="100"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <TextBlock Text="UST-Id" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"/> <TextBlock x:Name="tbxTaxID" Text= "{Binding Taxid, Mode=TwoWay}" Grid.Column="2" Grid.Row="0"/> <TextBlock Text="Steuer-Nr." Grid.Column="3" Grid.Row="0"/> <TextBlock x:Name="tbxTaxNr" Text="{Binding Taxnbr, Mode=TwoWay}" Grid.Column="4" Grid.Row="0"/> <TextBlock Text="Branche" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"/> <TextBox x:Name="tbxBranche" Text="{Binding Branche, Mode=TwoWay}" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="2" Grid.Row="1"/> <TextBlock Text="Zahlungsziel" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="3" Grid.Row="1"/> <TextBox x:Name="tbxPaymentGoal" Text="{Binding Payment, Mode=TwoWay}" FontSize="14" FontFamily="Arial" FontStyle="Normal" Grid.Column="4" Grid.Row="1"/> </Grid> </Expander> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
Und dazu der ViewModel:
using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Data; namespace WpfApp1 { public class Window77VM { public Window77VM() { for (int i = 1; i < 10; i++) _Companydet.Add(new contactModel() { Taxid = i, Taxnbr = i.ToString(), Branche = i.ToString(), Payment = i.ToString() }); cvs.Source = _Companydet; } private ObservableCollection<contactModel> _Companydet = new ObservableCollection<contactModel>(); private CollectionViewSource cvs = new CollectionViewSource(); public ICollectionView Companydet { get { return cvs.View; } } } public class contactModel { public int Taxid { get; set; } public string Taxnbr { get; set; } public string Branche { get; set; } public string Payment { get; set; } } }
--
Viele Grüsse
Peter Fleischer (ehem. MVP für Developer Technologies)
Meine Homepage mit Tipps und Tricks- Als Antwort vorgeschlagen Peter Fleischer Samstag, 17. November 2018 07:00
-
Hallo,
erstmal Danke für die Hilfe. Es funktioniert immer noch nicht, auch wenn ich das Beispiel von Peter nachvollziehen kann.
Ich denke es liegt an der nachfolgenden Konstellation:
class PgDetailCompViewModel : MVVMViewModelBase { public PgDetailCompViewModel() { } public PgDetailCompViewModel(string companyID) { transferID = companyID; PgDetailComp(companyID); }
Zum einen habe ich den parametrlosen Konstruktor des VM.
Zum anderen den parametrisierten Konstruktor. Den brauche ich, weil er aus einem anderen VM die CompanyID aufnimmt und den zugehörigen Datensatz ausliest. Die Property Companydet mit der Collection beinhaltet die Daten, trotzdem wird nichts dargestellt. Die Page die in einem Frame eingebunden ist weiß und zeigt nur den "Pfad":
"ContactApp.ViewModell.PgDetailCompViewModell"
Ich kann mir es nur so erklären, das aufgrund der Tatsache das die View als DataContext parameterlosen Konstruktor des VM hat, das Binding nicht klappt.
<Page.Resources> <vm:PgDetailCompViewModel x:Key="vm"/> </Page.Resources> <Grid DataContext="{StaticResource vm}">
Aber vielleicht liege ich auch falsch. Ich habe keine Ahnung.
Gruß
Jürgen
-
Hi Jürgen,
Das Beispiel, was ich gepostet habe, funktioniert bei mir ohne Fehler. Wenn es bei Dir nicht funktioniert, dann hast Du es nicht vollständig rauskopiert. Wenn es jedoch bei Dir funktioniert und nach Deinen Anpassungen nicht mehr funktioniert, dann wäre interessant, was Du alles geändert hast.Wenn nur der vollqualifizierte Name (Namensraum.Typ)zu sehen ist, dann fehlt der konkrete Membername, dessen Inhalt da erscheinen soll.
Unklar ist auch, wie Du die konkrete Instanz (incl. Parameter) erzeugst und der Page zuweist. Vermutlich nutzt Du unterschiedliche Instanzen Deiner Klasse PgDetailCompViewModel.
--
Viele Grüsse
Peter Fleischer (ehem. MVP für Developer Technologies)
Meine Homepage mit Tipps und Tricks- Bearbeitet Peter Fleischer Mittwoch, 21. November 2018 19:31 Bemerkung gelöscht
-
Hi Jürgen,
entschuldige bitte, wenn ich mich da im Ton vergriffen habe, trotz Smileys. Es ist aber schwer, aus unvollständigen Codestücken und einer mir teilweise unverständlichen Beschreibung die Probleme zu erahnen.
--
Viele Grüsse
Peter Fleischer (ehem. MVP für Developer Technologies)
Meine Homepage mit Tipps und Tricks
- Bearbeitet Peter Fleischer Mittwoch, 21. November 2018 17:09
-
Hallo Jürgen,
das Problem liegt offensichtlich in der Struktur einer Anwendung. Und eine passende, funktionierende Antwort kannst du nur bekommen, wenn du uns deine Struktur erklärst.
Wie Peter schon geschrieben hat, ist das Problem in der Instanz, die scheinbar immer wieder neu erzeugt wird, wenn du im Datagrid eine neue Zeile auswählst. Denn die Logik ist ja im Constructor deines ViewModels. Wenn du also eine Page in einem Frame anzeigst, die an eine ViewModel-Instanz gebunden ist, du beim "klick" auf eine neue Zeile im DataGrid eine neue Instanz erzeugst, kann nichts angezeigt werden, das ist logisch!
Was mich zu der Frage bringt: Warum eine Page in einem Frame? Navigierst du durch mehrere Pages? Hast du dafür einen Navigation-Service erstellt, der dem MVVM Entwurfsmuster entspricht?
Wenn du also eine Page anzeigst, in der sich Daten ändern, musst du das der richtigen Instanz zuweisen. Da ich ja ein Freund von MVVMLight bin, käme der Messenger zum Einsatz. Wenn du aber generell nicht durch mehrere Pages navigierst, würde ich auch keine Page einsetzen.
Gruß
Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP
-
Hallo,
erst mal Sorry an Peter Fleischer. Hab Überreagiert.
Dann zum Thema. Ich bin kein Programmierer. Mich interessiert nur das Thema und ich versuche nachzuvollziehen wie Anwendungen funktionieren und bringe mir dabei ein bisschen Coden bei.
Im konkreten Fall habe ich eine MainView mit ViewModel in der MainView ist ein Frame integriert in das verschieden Pages eingebunden werden sollen.
Eine Page ist die DetailPage mit zugehörigem ViewModel. Diese Page zeigt immer die Details zu einer im nebenstehenden GridView ausgewählten Zeile (Kontakt).
Über dem Frame soll es RadioButtons geben über die ich verschiedene Pages in dieses Frame einbinden möchte. Soweit bin ich aber noch nicht.
Im ViewModel der MainPage habe ich eine Property SelcetedValue die die ID des ausgewählten Kontakts aufnimmt. Diese ID übergebe ich an das ViewModell der DetailPage.
FrameCompDet.Content = new PgDetailComp(_m.transcompanyid);
Dort kommt die ID auch an und über einen DataReader hole ich mir den zugehörigen kompletten Datensatz in eine ObservableCollection.
Hier die Property:
private string _selectedValue;
public string SelectedValue { get { return _selectedValue; } set { if (_selectedValue != value) { _selectedValue = value; if (PropertyChanged != null) { OnPropertyChanged("SelectedValue"); _m.transcompanyid = value; FrameCompDet = new Frame(); FrameCompDet.Content = new PgDetailComp(_m.transcompanyid); } } } }
Dann scheitere ich aber am Binding. Und ich vermute es liegt daran, dass ich in der View (siehe Oben) als Static Resource den parameterlosen Konstruktor binde, aber aus dem VM der MainView der parametrisierte Konstruktor instanziert wird (wegen der ID-Übergabe).
Es scheint als ob der parameterlose Konstruktor die Property Companydet (enthält die ObservableCollection siehe Oben) gar nicht anbietet.
public PgDetailCompViewModel() { } public PgDetailCompViewModel(string companyID) { PgDetailComp(companyID); }
Mein letzter Versuch war im CodeBehind der DetailPage View ein DataContext zu definieren und die ID auf diesem Weg zu übergeben (klappt auch) und das Binding hinzubekommen. Aber auch das scheitert.
namespace ContactApp.View { /// <summary> /// Interaktionslogik für PgDetailComp.xaml /// </summary> public partial class PgDetailComp : Page { public PgDetailComp(string id) :this(new PgDetailCompViewModel(id)) { //InitializeComponent(); } PgDetailComp(PgDetailCompViewModel viewModel) { InitializeComponent(); this.DataContext = viewModel; } } }
Sobald ich im XAML die Bindings definiere ist das Frame grau und mein Formular verschwunden. Nehme ich das Binding weg, ist das Formular wieder sichtbar.
Falls es noch Verständnisproblem gibt einfach Bescheid sagen.
Gruß
Jürgen
- Bearbeitet JKeitzl Dienstag, 27. November 2018 21:50
-
Hi Jürgen,
da Du keine der Antworten auf Deine Fragen als "beantwortet" kennzeichnest, ist es sehr schwer zu erkennen, was Du wirklich benötigst.Wie Stefan schon schrieb, ist die Struktur des Programme aus Deinen Ausführungen nicht erkennbar. Ich habe bisher verstanden, dass Du ein Frame für Pages nutzt, wobei jede Page für einen konkreten Index einer Zeile aus einem GridView die dazugehörenden Daten darstellt. Frame mit Pages haben den Vorteil dass man navigieren kann. Vermutlich ist es das, was Du nutzen willst. Der Nachteil der Navigation ohne eigenen NavigationService ist, dass beim Navigieren auf eine Seite diese immer neu instanziiert wird, außer bei einer neu hinzugefügten Page. Damit geht aber die Beziehung zum Index verloren. Das kann man umgehen, wenn man einen eigenen NavigationService implementiert, der sich die Indizes zur Aufrufliste merkt und dann entsprechend zuweist.
Wie ich Dir bereits geschrieben hatte, würde ich für eine derartige Aufgabenstellung UnserControls nutzen, deren Instanzen in einer Liste gehalten werden. Diese UserControls kann man beispielsweise in einer TabPage für eine Navigation bereitstellen.
".. hole ich mir den zugehörigen kompletten Datensatz in eine ObservableCollection." Wozu dieser Aufwand, wenn nur ein Datenobjekt geholt und gebraucht wird? Nach der Zuweisung des Datenobjektes als DataContext, kann man jede Eigenschaft des Datenobjektes für die Bindung nutzen.
Wenn Du eine statische Ressource für den DataContext nutzt, wird sie implizit instanziiert, und das üblicherweise mit dem parameterlosen Standard-Konstruktor.
--
Viele Grüsse
Peter Fleischer (ehem. MVP für Developer Technologies)
Meine Homepage mit Tipps und Tricks