|
Combobox Bindung (Nachschlagetabelle)
Letzter Beitrag 11-14-2008 9:08 von wallace693. 9 Antworten.
-
11-11-2008 12:31
|
|
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
Combobox Bindung (Nachschlagetabelle)
Hallo,
ich habe ein Verständnisproblem mit der Datenbindung einer Combobox (Nachschlagetabelle). Hier ein vereinfachtes Beispiel:
Datenbank MS-SQL-Server 2005, Visual Studio 2008, WPF-Anwendung
Tabelle Personen:
| Name |
Rufname |
AnredeID |
| Conrad |
Uta |
3 |
| Conrad |
Annemarie |
3 |
|
Tabelle Anredearten
| AnredeID |
AnredeArtText |
| 1 |
Herrn |
| 2 |
Frau und Herr |
| 3 |
Frau |
Die Tabellen sind per Foreign-Key verbunden.
Ich möchte ein Eingabeformular mit 2 Textboxen (zusätzlich eine Textbox zur Prüfung der korrekten AnredeID) und einer Combobox(Auswahl der Anrede aus der Tabelle Anredearten) erstellen. Dafür habe ich mit Hilfe der Linq2SQL Klassen die Verbindung zur Datenbank hergestellt.
XAML: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| <Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="204" Width="314" Loaded="Window_Loaded">
<Grid>
<TextBox Margin="18,14,0,0"
Name="textBox1"
VerticalAlignment="Top"
Text="{Binding Name}"
HorizontalAlignment="Left"
Width="135"></TextBox>
<TextBox Margin="17,48,0,0"
Name="textBox2"
VerticalAlignment="Top"
Text="{Binding Rufname}"
HorizontalAlignment="Left"
Width="135" />
<TextBox Margin="17,79,0,0"
Name="textBox3"
Text="{Binding AnredeID}"
HorizontalAlignment="Left"
Width="136" Height="26"
VerticalAlignment="Top" />
<ComboBox Margin="17,114,0,0"
Name="comboBox1" HorizontalAlignment="Left"
Width="136" Height="25" VerticalAlignment="Top" />
</Grid>
</Window>
|
Code behind:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| namespace WpfApplication1
{
/// <summary>
/// Interaktionslogik für Window1.xaml
/// </summary>
public partial class Window1 : Window
{
DataClasses1DataContext dc;
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
dc = new DataClasses1DataContext();
this.DataContext =
dc.Personens
.Where(person => person.Name.StartsWith("conr"))
.Select(person => person);
}
}
} |
Die Textfelder sind korrekt gebunden, die Daten werden wie erwartet angezeigt. Wie muss jetzt die Bindung der Combobox erfolgen, so dass die Einträge der Stammtabelle in der Liste enthalten sind, und der korrekte Eintrag automatisch ausgewählt ist? Die Einträge in der Liste habe ich so erstellt:
1
2
3
4
5
6
| comboBox1.ItemsSource =
dc.AnredeArtens
.OrderBy(anrede => anrede.AnredeArtText)
.Select(anrede => anrede);
comboBox1.DisplayMemberPath = "AnredeArtText";
|
Mir sind die verschiedenen Eigenschaften der Combobox nicht klar.
DisplayMemberPath - welcher Teil der Liste (hier Tabellenspalte) soll angezeigt werden - ist das richtig? SelectedIndex - ??? SelectedItem - ??? SelectedValue - ??? SelectedValuePath - ???
Welche dieser Eigenschaften muss ich jetzt die AnredeID binden ({Binding AnredeID})???
Vielen Dank im Voraus für Eure Hilfe, Sven
|
|
-
-
FantaMango77



- Registriert am 05-07-2008
- Magdeburg, DE
- Beiträge 134
- Punkte 2.405

|
AW: Combobox Bindung (Nachschlagetabelle)
Hallo Sven, Du hast die Liste der verfügbaren Elemente an eine Liste gebunden. Da diese Liste Objekte beliebiger Typen enthalten kann, muss man der Runtime mitteilen, welche Eigenschaft zur Anzeige innerhalb der Liste verwendet werden soll. Das hast du schon richtig mit dem Setzen von DisplayMemberPath getan. Wenn das aus der Liste ausgewählte Objekt über eine bestimmte Eigenschaft, wie z.B. einer ID, identifiziert werden soll, so gibt man das mit SelectedValuePath an. In der Eigenschaft SelectedValue steht dann genau der Wert der angegebenen Eigenschaft. In deinem Beispiel muss das AnredeID sein. Setze also SelectedValuePath auf "AnredeID". Damit enthält SelectedValue immer einen der Primärschlüssel aus der Anrede-Tabelle. Diesen Primärschlüssel brauchst du ja auch als Fremdschlüssel in deinen Personen-Objekten. Damit der ausgewählte Eintrag an das entsprechende Personen-Objekt "weitergereicht" wird, muss es an die Eigenschaft "AnredeID" der Person gebunden werden. (Achtung, diesmal ist AnredeID eine Eigenschaft des Person-Objektes und nicht das Primärschlüssel-Feld der Anrede. Sie heißen nur zufälligerweise gleich) Das Ergebnis ist nun: <ComboBox Margin="17,114,0,0" Name="comboBox1" HorizontalAlignment="Left" Width="136" Height="25" VerticalAlignment="Top" SelectedValuePath="AnredeID" SelectedValue="{Binding AnredeID}" />
Ciao, Jens
|
|
-
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
AW: Combobox Bindung (Nachschlagetabelle)
Hallo Jens,
vielen Dank für Deine schnelle Hilfe. Ich habe einen entscheidenden Fehler gemacht: In meiner Anredetabelle heisst mein PK nicht AnredeID sondern AnredeArtID - deshalb hat mich Deine Lösung zuerst nicht zum Ziel gebracht. Irgendwann ist mir dieser entscheidende Unterschied der Feldbezeichnungen aufgefallen, mit dem grossen Vorteil, dass mir jetzt der Unterschied zwischen SelectedValuePath und SelectedValue klar ist. Das hat mich jetzt viele Stunden gekostet, aber solche Fehler macht man nur einmal...
Die Lösung ist also: 1
2
3
4
5
6
7
8
9
| <ComboBox Margin="17,114,0,0"
Name="comboBox1"
SelectedValuePath="AnredeArtID"
SelectedValue="{Binding AnredeID}"
DisplayMemberPath="AnredeArtText"
IsSynchronizedWithCurrentItem="True"
HorizontalAlignment="Left"
Width="136" Height="19.277"
VerticalAlignment="Top" /> |
|
|
-
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
AW: Combobox Bindung (Nachschlagetabelle)
Es ist leider doch noch nicht ganz korrekt, es wird jetzt beim Aufruf des Datensatzes die korrekte Anrede angezeigt, eine neue Auswahl der Anrede aus der Combobox wird aber leider nicht in der Datenbank gespeichert. Die Speicherung ist doch so korrekt? : dc.SubmitChanges();
Gruß, Sven
|
|
-
-
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
AW: Combobox Bindung (Nachschlagetabelle)
Hallo Jens,
ich habe in meiner Liste oben nicht alle Felder aufgeführt. Es gibt ein Feld PersonID, dort liegt der Primary Key:
ALTER TABLE [Global].[Personen] ADD CONSTRAINT [PK_Personen] PRIMARY KEY CLUSTERED ([PersonID]) ON [PRIMARY]
Ich habe zum Testen zwei Label eingebaut, eins ist an die AnredeID der Person gebunden, eines an SelectedValue der Combobox. Dabei hat sich herausgestellt, dass beim Auswählen eines anderen Eintrages aus der Combobox SelectedValue geändert wird (keine Überraschung...), die daran gebundene AnredeID der Person jedoch nicht. Muss ich an der BindingMode noch etwas ändern? Das hier hat aber auch noch nichts gebracht: <ComboBox Grid.Column="1" Grid.Row="0" x:Name="cbAnrede" ToolTip="Herr, Frau, ... - Daten sind in einer Stammdatentabelle hinterlegt" ItemsSource="{Binding}" DisplayMemberPath="AnredeArtText" SelectedValuePath="AnredeArtID" SelectedValue="{Binding Path=AnredeID, Mode=TwoWay}"/>
Gruß Sven
|
|
-
-
FantaMango77



- Registriert am 05-07-2008
- Magdeburg, DE
- Beiträge 134
- Punkte 2.405

|
AW: Combobox Bindung (Nachschlagetabelle)
Hi Sven, Du darfs die ItemsSource nicht mittels "{Binding}" binden. Das würde bedeuten, dass der aktuelle Kontext die Quelle für die Nachschlagewerte ist. Der aktuelle Kontext sind aber nicht die Nachschlagewerte, sondern ist die Person. Eine Kombobox bindest du gewissermaßen an zwei Datenquellen: - Über den DataContext an das zu bearbeitende Objekt. Hier eben ein Personen-Objekt.
Den DataContext hattest du in deinem ersten Beispiel im CodeBehind an das Ergebnis einer Linq-Abfrage gebunden.
- Über die ItemsSource-Eigenschaft an eine Liste von Nachschlagewerten. Die ItemsSource im CodeBehind an ein Linq-Ergebnis zu binden ist eine Möglichkeit, die du im ersten Beispiel bereits korrekt gemacht hattest.
Hier ist mein funktionierender Quelltext, der dein Beispiel umsetzt: Xaml: <Window x:Class="WpfApplication18.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" Loaded="Window_Loaded"> <StackPanel> <StackPanel Orientation="Horizontal"> <Label Content="Name:"/> </StackPanel Name="textBox1" VerticalAlignment="Top" Text="{Binding Name}" HorizontalAlignment="Left" Width="135"> <StackPanel Orientation="Horizontal"> <Label Content="Rufname:"/> </StackPanel Name="textBox2" VerticalAlignment="Top" Text="{Binding Rufname}" HorizontalAlignment="Left" Width="135"> <StackPanel Orientation="Horizontal"> <ComboBox Name="comboBox1" HorizontalAlignment="Left" Width="136" Height="25" VerticalAlignment="Top" SelectedValuePath="AnredeArtID" SelectedValue="{Binding AnredeID}" DisplayMemberPath="AnredeArtText"/> </StackPanel Name="textBox3" Content="{Binding ElementName=comboBox1, Path=SelectedValue}" HorizontalAlignment="Left" Width="136" Height="26" VerticalAlignment="Top"> <Button Content="Submit" Click="Button_Click"/> </StackPanel> </Window>
Codebehind: public partial class Window1 : Window { private DataClasses1DataContext dc;
public Window1() { InitializeComponent(); }
private void Window_Loaded(object sender, RoutedEventArgs e) { dc = new DataClasses1DataContext(); this.DataContext = dc.Personens .Where(person => person.Name.StartsWith("conr")) .Select(person => person);
comboBox1.ItemsSource = dc.Anredeartens .OrderBy(anrede => anrede.AnredeArtText) .Select(anrede => anrede); }
private void Button_Click(object sender, RoutedEventArgs e) { dc.SubmitChanges(); } } Linq-Entities:
|
|
-
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
AW: Combobox Bindung (Nachschlagetabelle)
Hallo Jens,
vielen Dank für Deine Engelsgeduld. Allerdings hatte ich das Binding auch schon deaktiviert, leider erfolglos. Also habe ich Dein Beispiel nachvollzogen, funktioniert. Dann habe ich zur besseren Übersicht ein Listview zugefügt - jetzt kann ich die Anrede wieder nicht mehr ändern (den Namen kann ich ändern ?!)
Ich schicke den Code mit, vielleicht kannst Du es nachvollziehen (in Deiner Testdatenbank heißt die Tabelle Anredearten, bei mir AnredeArten) - so wie das Window jetzt aufgebaut ist, funktioniert es nicht, aber wenn man die Listview auskommentiert, funktioniert alles. Weiteres Experimentieren hat gezeigt, dass das Auskommentieren der beiden Anrede-GridViewColumns ausreicht. Müssen diese Spalten anders gebunden werden, oder muss ich die komplette ListView an eine andere Datenquelle binden? Es gibt an der Person noch viele ähnliche Spalten (Titel, Hobby, Geschlecht, ...)
Gruß Sven
XAML: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
| <Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="346" Width="448" Loaded="Window_Loaded">
<StackPanel>
<StackPanel Name="spButtons"
Orientation="Horizontal">
<Button Width="30" Name="btnFirst" Content="<<" Click="btnFirst_Click"/>
<Button Width="30" Name="btnPrior" Content="<" Click="btnPrior_Click"/>
<Button Width="30" Name="btnNext" Content=">" Click="btnNext_Click"/>
<Button Width="30" Name="btnLast" Content=">>" Click="btnLast_Click"/>
<Button Content="Submit" Click="Button_Click"/>
</StackPanel>
<ListView
Name="lvPersonen"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
>
<ListView.View>
<GridView>
<GridViewColumn Header="ID" DisplayMemberBinding="{Binding PersonID}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}"/>
<GridViewColumn Header="Rufname" DisplayMemberBinding="{Binding Rufname}"/>
<!-- Es genügt, die kommenden beiden Spalten auszukommentieren -->
<GridViewColumn Header="Anrede" DisplayMemberBinding="{Binding AnredeArten.AnredeArtText}"/>
<GridViewColumn Header="AnredeID" DisplayMemberBinding="{Binding AnredeID}"/> <!-- -->
</GridView>
</ListView.View>
</ListView>
<StackPanel Orientation="Horizontal">
<Label Content="Name:"/>
<TextBox Name="textBox1"
VerticalAlignment="Top"
Text="{Binding Name}"
HorizontalAlignment="Left"
Width="135"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Rufname:"/>
<TextBox Name="textBox2"
VerticalAlignment="Top"
Text="{Binding Rufname}"
HorizontalAlignment="Left"
Width="135"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ComboBox Name="comboBox1"
HorizontalAlignment="Left"
Width="136"
Height="25"
VerticalAlignment="Top"
SelectedValuePath="AnredeArtID"
SelectedValue="{Binding AnredeID}"
DisplayMemberPath="AnredeArtText"/>
<Label Name="textBox3"
Content="{Binding ElementName=comboBox1, Path=SelectedValue}"
HorizontalAlignment="Left"
Width="136" Height="26"
VerticalAlignment="Top"/>
</StackPanel>
</StackPanel>
</Window>
|
CodeBehind:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
| using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace WpfApplication1
{
/// <summary>
/// Interaktionslogik für Window1.xaml
/// </summary>
public partial class Window1 : Window
{
DataClasses1DataContext dc;
ICollectionView view;
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
dc = new DataClasses1DataContext();
this.DataContext =
dc.Personens
.Where(person => person.Name.StartsWith("conr"))
.Select(person => person);
comboBox1.ItemsSource =
dc.AnredeArtens
.OrderBy(anrede => anrede.AnredeArtText)
.Select(anrede => anrede);
this.view = CollectionViewSource.GetDefaultView(this.DataContext);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
dc.SubmitChanges();
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
view.MoveCurrentToNext();
}
private void btnPrior_Click(object sender, RoutedEventArgs e)
{
view.MoveCurrentToPrevious();
}
private void btnLast_Click(object sender, RoutedEventArgs e)
{
view.MoveCurrentToLast();
}
private void btnFirst_Click(object sender, RoutedEventArgs e)
{
view.MoveCurrentToFirst();
}
}
}
|
|
|
-
-
FantaMango77



- Registriert am 05-07-2008
- Magdeburg, DE
- Beiträge 134
- Punkte 2.405

|
AW: Combobox Bindung (Nachschlagetabelle)
Hi, Ein Blick in das Output Fenster verrät den Übeltäter: A first chance exception of type 'System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException' occurred in WpfApplication18.exe System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=AnredeID; DataItem='DataQuery`1' (HashCode=61617225); target element is 'ComboBox' (Name='comboBox1'); target property is 'SelectedValue' (type 'Object') TargetInvocationException:'System.Reflection.TargetInvocationException: Ein Aufrufziel hat einen Ausnahmefehler verursacht. ---> System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException: Der Vorgang ist aufgrund des aktuellen Zustands des Objekts ungültig. Das ist wohl eine Beschränkung von Linq2Sql. Ich nehme an, das hat was mit den Lazy-Loading-Mechanismen von Linq2Sql zu tun. Meine Anpassungen sehen nun wie folgt aus: Die beiden GridViewColumns: <GridViewColumn Header="Anrede" DisplayMemberBinding="{Binding AnredeArten.AnredeArtText}"/>
<GridViewColumn Header="AnredeID" DisplayMemberBinding="{Binding AnredeArten.AnredeArtID}"/>Die ComboBox: <ComboBox Name="comboBox1" HorizontalAlignment="Left" Width="136" Height="25" VerticalAlignment="Top"
SelectedItem="{Binding AnredeArten}"
DisplayMemberPath="AnredeArtText"/>Jetzt wird direkt an die Anrede-Eigenschaft des Personen-Objektes gebunden und nicht mehr indirekt über den Fremdschlüssel. Damit das Label hinter der ComboBox noch einen ordentlichen Wert anzeigt, habe ich es so angepasst: <Label Name="textBox3"
Content="{Binding ElementName=comboBox1, Path=SelectedItem.AnredeArtID}"
HorizontalAlignment="Left"
Width="136" Height="26"
VerticalAlignment="Top"/>Ciao, Jens
|
|
-
-
wallace693



- Registriert am 06-26-2008
- Weiden in der Oberpfalz
- Beiträge 31
- Punkte 455
|
AW: Combobox Bindung (Nachschlagetabelle)
Hallo Jens,
perfekt, ich danke Dir.
|
|
Seite 1 von 1 (10 Treffer)
|
|
|