Benutzer mit den meisten Antworten
Binding mit Index funktioniert nicht

Frage
-
Hallo,
Ich habe eine Property (vereinfacht)
Private arrText(19) As String Public Property Text(index%) As String Get Return arrText(index) End Get Set(value As String) arrText(index) = value End Set End Property
und 20 TextBlocks, die ich im Code lade und an diese Property binde:For n = 0 To 19 Dim tbl As New TextBlock pathStr = String.Format("Text[{0}]", n) 'pathStr = String.Format("Text[(sys:Int32){0}]", n) Dim b As New Binding(pathStr) tbl.SetBinding(TextBlock.TextProperty, b) Panel.Children.Add(tbl) Next
Ich habe beide Versionen von pathStr ausprobiert, funktioniert aber nicht.Was mache ich falsch?
Antworten
-
Hier mal eine Demo, wie man das Binding mit Index per Code lösen kann.
Zuerst der XAML:
<Window x:Class="Window19" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window19" Height="300" Width="300"> <StackPanel Name="Panel"/> </Window>
Dazu der Codebehind und der ViewModel:
Public Class Window19 Private Sub Window19_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded Me.Panel.DataContext = New Window19VM For n = 0 To 19 Dim tbl As New TextBlock Dim b As New Binding(String.Format("[{0}]", n)) tbl.SetBinding(TextBlock.TextProperty, b) Me.Panel.Children.Add(tbl) Next End Sub End Class Public Class Window19VM Private arrText(19) As String Public Sub New() arrText(0) = "<leer>" For i = 1 To arrText.GetUpperBound(0) arrText(i) = String.Format("Text {0:00}", i) Next End Sub Private _item As String Default Public ReadOnly Property Item(ByVal index As String) As String Get Dim ind As Integer = 0 Integer.TryParse(index, ind) Return Me.arrText(ind) End Get End Property End Class
--
Viele Gruesse
Peter- Als Antwort vorgeschlagen Peter Fleischer Samstag, 30. Juli 2011 09:49
- Als Antwort markiert Elmar BoyeEditor Samstag, 30. Juli 2011 13:54
-
Hallo,
das geht deutlich einfacher in dem Du das Array selbst zur Verfügung stellst,
dann greift der Abschnitt aus Deinem Link (deutsch):Indexer einer Eigenschaft können in eckigen Klammern nach dem Eigenschaftennamen angegeben werden,
auf den der Indexer angewendet wird.Arrays wie Du wie verwendest sind aber der kleinste gemeinsame Nenner,
denn sie sind in der Größe unverländerlich. Besser wäre schon eine List(Of T),
denn dort könnten Elemente hinzugefügt oder gelöscht werden.
Das Optimum kriegst Du aber, wenn Du eine ObserverableCollection(Of T) verwendest.
Die ist speziell für die Datenbindung-Möglichkeiten von WPF entworfen worden und
unterstützt Änderungsbenachrichtigungen.Zur Illustration ein kleines Beispiel - wobei ich XAML verwendet habe
und mich der Kürze halber auf 3 Elemente beschränkt habe:dazu der Code-Behind:<Window x:Class="ElmarBoye.Samples.Wpf.TextWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Bindungen" Height="300" Width="300"> <StackPanel> <StackPanel Orientation="Horizontal" Height="200"> <StackPanel Margin="8,8"> <TextBlock x:Name="TextBlock1" Width="120" Text="{Binding Path=Text[0]}"/> <TextBlock x:Name="TextBlock2" Width="120" Text="{Binding Path=Text[1]}"/> <TextBlock x:Name="TextBlock3" Width="120" Text="{Binding Path=Text[2]}"/> </StackPanel> <ListBox x:Name="TextListBox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Text}"> <ListBox.ItemTemplate> <DataTemplate> <Border Margin="2,2" BorderBrush="Silver" BorderThickness="1"> <TextBlock Width="120" Text="{Binding}"/> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button Margin="8,8" Width="80" x:Name="AddButton" Content="Hinzufügen" Click="AddButton_Click"/> <Button Margin="8,8" Width="80" x:Name="RemoveButton" Content="Löschen" Click="RemoveButton_Click"/> </StackPanel> </StackPanel> </Window>
Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Windows Namespace ElmarBoye.Samples.Wpf Public Partial Class TextWindow Inherits Window Private _text As New ObservableCollection(Of String)() From { _ "7. Text", "8. Text", "9. Text" } ' Private _textArray As String() = New String() {"1. Text", "2. Text", "3. Text"} ' Private _textList As New List(Of String)() From { _ ' "4. Text", "5. Text", "6. Text" } Public Sub New() InitializeComponent() ' Bindung an sich selbst Me.DataContext = Me End Sub Public ReadOnly Property Text() As ObservableCollection(Of String) Get Return Me._text End Get End Property ' Array ist unveränderlich und ohne Benachrichtigung ' Public ReadOnly Property Text() As String() ' Get ' Return Me._textArray ' End Get ' End Property ' List ist ohne Benachrichtigung ' Public ReadOnly Property Text() As List(Of String) ' Get ' Return Me._textList ' End Get ' End Property Private Sub AddButton_Click(sender As Object, e As RoutedEventArgs) Me.Text.Add("Neue Zeile " & Me.Text.Count.ToString()) End Sub Private Sub RemoveButton_Click(sender As Object, e As RoutedEventArgs) If Me.Text.Count > 0 Then Me.Text.RemoveAt(0) End If End Sub End Class End Namespace
Oben auskommentiert sind die Abschnitte, die für die reine Bindung funktionieren würden.
Nur das man bei einem Array nicht ohne weiteres hinzufügen/oder löschen kann.
Und verwendest Du die List(Of) Variante passiert (ohne Nachhilfe) nichts auf dem Bildschirm.Daneben habe ich eine ListBox gestellt. Denn damit wird man die (20) Textblöcke los,
und kann sich (sofern die Voraussetzungen passen) das Programmierer-Leben weiter vereinfachen.Gruß Elmar
- Als Antwort markiert ubsch Samstag, 30. Juli 2011 09:36
Alle Antworten
-
Hi,
ich persönlich würde eher eine List( Of String ) anstelle des Array nehmen. Sollte aber hier keine Rolle spielen.
Ich bin nicht so der WPF Mensch, daher rate ich einfach mal :) Es fehlt:
b.Source = arrText
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hallo Stefan,
danke für deine Antwort. Ich habe vorher natürlich den DataContext entsprechend festgelegt. Ich habe zur Probe, ob die Bindung funktioniert, eine zusätzliche Property gebildet:
Public ReadOnly Property Text0 As String Get Return Text(0) End Get End Property
und in meinem Beispiel pathStr so gebildet:pathStr = String.Format("Text{0}", n)
Dann funktioniert die Bindung an den einen Textblock bestens. Entsprechend könnte ich natürlich auch Properties Text1 bis Text19 bilden. Aber das kann ja wohl nicht die Lösung sein.Es muss an dem Indexer der Text-Property liegen.
Gruß ubsch
-
Hallo,
ich habe jetzt eine Lösung gefunden, indem ich an das Objekt mit der Text-Property binde, einen Converter bilde und den Index an den ConverterParameter übergebe:
Public Class TextConverter Implements IValueConverter Public Function Convert(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.Convert Dim cls = CType(TryCast(value, TextClass), TextClass) If cls Is Nothing Then Return Nothing Dim ix% = CInt(parameter) Return cls.Text(ix) End Function Public Function ConvertBack(value As Object, targetType As System.Type, parameter As Object, culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IValueConverter.ConvertBack ' End Function End Class <br/>
und die Bindung:For n = 0 To 19 Dim tbl As New TextBlock Dim b As New Binding(".") b.Converter = New TextConverter b.ConverterParameter = n tbl.SetBinding(TextBlock.TextProperty, b) Panel.Children.Add(tbl) Next
Dann ist die Bindung nur ReadOnly, was in meinem Fall reicht, aber alles funktioniert.Trotzdem wüsste ich gerne, wie man eine Bindung mit Indexer erstellt. Ich meine, es so gemacht zu haben, wie hier beschrieben:
http://msdn.microsoft.com/en-us/library/ms752300.aspx#Path_Syntax
nur in Code statt in XAML
Ich hoffe noch auf Antwort.
Gruß, ubsch
-
Hallo,
das geht deutlich einfacher in dem Du das Array selbst zur Verfügung stellst,
dann greift der Abschnitt aus Deinem Link (deutsch):Indexer einer Eigenschaft können in eckigen Klammern nach dem Eigenschaftennamen angegeben werden,
auf den der Indexer angewendet wird.Arrays wie Du wie verwendest sind aber der kleinste gemeinsame Nenner,
denn sie sind in der Größe unverländerlich. Besser wäre schon eine List(Of T),
denn dort könnten Elemente hinzugefügt oder gelöscht werden.
Das Optimum kriegst Du aber, wenn Du eine ObserverableCollection(Of T) verwendest.
Die ist speziell für die Datenbindung-Möglichkeiten von WPF entworfen worden und
unterstützt Änderungsbenachrichtigungen.Zur Illustration ein kleines Beispiel - wobei ich XAML verwendet habe
und mich der Kürze halber auf 3 Elemente beschränkt habe:dazu der Code-Behind:<Window x:Class="ElmarBoye.Samples.Wpf.TextWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Bindungen" Height="300" Width="300"> <StackPanel> <StackPanel Orientation="Horizontal" Height="200"> <StackPanel Margin="8,8"> <TextBlock x:Name="TextBlock1" Width="120" Text="{Binding Path=Text[0]}"/> <TextBlock x:Name="TextBlock2" Width="120" Text="{Binding Path=Text[1]}"/> <TextBlock x:Name="TextBlock3" Width="120" Text="{Binding Path=Text[2]}"/> </StackPanel> <ListBox x:Name="TextListBox" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Text}"> <ListBox.ItemTemplate> <DataTemplate> <Border Margin="2,2" BorderBrush="Silver" BorderThickness="1"> <TextBlock Width="120" Text="{Binding}"/> </Border> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </StackPanel> <StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <Button Margin="8,8" Width="80" x:Name="AddButton" Content="Hinzufügen" Click="AddButton_Click"/> <Button Margin="8,8" Width="80" x:Name="RemoveButton" Content="Löschen" Click="RemoveButton_Click"/> </StackPanel> </StackPanel> </Window>
Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Windows Namespace ElmarBoye.Samples.Wpf Public Partial Class TextWindow Inherits Window Private _text As New ObservableCollection(Of String)() From { _ "7. Text", "8. Text", "9. Text" } ' Private _textArray As String() = New String() {"1. Text", "2. Text", "3. Text"} ' Private _textList As New List(Of String)() From { _ ' "4. Text", "5. Text", "6. Text" } Public Sub New() InitializeComponent() ' Bindung an sich selbst Me.DataContext = Me End Sub Public ReadOnly Property Text() As ObservableCollection(Of String) Get Return Me._text End Get End Property ' Array ist unveränderlich und ohne Benachrichtigung ' Public ReadOnly Property Text() As String() ' Get ' Return Me._textArray ' End Get ' End Property ' List ist ohne Benachrichtigung ' Public ReadOnly Property Text() As List(Of String) ' Get ' Return Me._textList ' End Get ' End Property Private Sub AddButton_Click(sender As Object, e As RoutedEventArgs) Me.Text.Add("Neue Zeile " & Me.Text.Count.ToString()) End Sub Private Sub RemoveButton_Click(sender As Object, e As RoutedEventArgs) If Me.Text.Count > 0 Then Me.Text.RemoveAt(0) End If End Sub End Class End Namespace
Oben auskommentiert sind die Abschnitte, die für die reine Bindung funktionieren würden.
Nur das man bei einem Array nicht ohne weiteres hinzufügen/oder löschen kann.
Und verwendest Du die List(Of) Variante passiert (ohne Nachhilfe) nichts auf dem Bildschirm.Daneben habe ich eine ListBox gestellt. Denn damit wird man die (20) Textblöcke los,
und kann sich (sofern die Voraussetzungen passen) das Programmierer-Leben weiter vereinfachen.Gruß Elmar
- Als Antwort markiert ubsch Samstag, 30. Juli 2011 09:36
-
Hier mal eine Demo, wie man das Binding mit Index per Code lösen kann.
Zuerst der XAML:
<Window x:Class="Window19" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window19" Height="300" Width="300"> <StackPanel Name="Panel"/> </Window>
Dazu der Codebehind und der ViewModel:
Public Class Window19 Private Sub Window19_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded Me.Panel.DataContext = New Window19VM For n = 0 To 19 Dim tbl As New TextBlock Dim b As New Binding(String.Format("[{0}]", n)) tbl.SetBinding(TextBlock.TextProperty, b) Me.Panel.Children.Add(tbl) Next End Sub End Class Public Class Window19VM Private arrText(19) As String Public Sub New() arrText(0) = "<leer>" For i = 1 To arrText.GetUpperBound(0) arrText(i) = String.Format("Text {0:00}", i) Next End Sub Private _item As String Default Public ReadOnly Property Item(ByVal index As String) As String Get Dim ind As Integer = 0 Integer.TryParse(index, ind) Return Me.arrText(ind) End Get End Property End Class
--
Viele Gruesse
Peter- Als Antwort vorgeschlagen Peter Fleischer Samstag, 30. Juli 2011 09:49
- Als Antwort markiert Elmar BoyeEditor Samstag, 30. Juli 2011 13:54
-
Hallo Elmar,
danke für deine ausführliche Antwort.
Habe ich richtig verstanden, dass eine Bindung mit Indexer nur funktioniert, wenn direkt an eine Auflistung (IEnumerable) gebunden wird?
Ansonsten ist meine Frage damit beantwortet.
Gruß ubsch
- Als Antwort markiert ubsch Samstag, 30. Juli 2011 09:36
- Tag als Antwort aufgehoben Thorsten DörflerModerator Samstag, 30. Juli 2011 10:18
-
Hi Elmar,so, wie ich es aber verstanden haben, will der OP dynamisch per Code die Elemente in der Oberfläche erzeugen, mit einer Indexkennung versehen und so binden, dass der Index über eine Eigenschaft das gewünschte Element aus einer Menge holt. Ob da ein Array oder eine Liste genutzt wird, ist sekundär. In meinem parallel gepostetem Beispiel habe ich es mal mit einem Array gezeigt.--
Viele Gruesse
Peter -
Hallo,
unter Indexer wird in der Beschreibung verstanden, was C# darunter versteht:
Indexer (C#-Programmierhandbuch)-Peter zeigt es in seinem Beispiel in Visual Basic, wo es durch Default als Standard-Eigenschaft gekennzeichnet wird
(und wo C# this verwendet schreibt man in Visual Basic Item (was C# im Hintergrund daraus macht).Visual Basic unterstützt Parameter bei beliebigen Eigenschaften - wie Du sie verwendet hast,
nur die werden von anderen Sprachen (wie C#) nicht unterstützt.
Und man sollte sie aus Gründen Interoperabilität (vor allem in Klassenbibliotheken) vermeiden.Gruß Elmar
-
Hallo Peter,
die "Dynamik" hatte ich weggelassen, um Alternativen in einem Beispiel zeigen zu können.
Das Verwenden einer Indexer-Eigenschaft (mit oder ohne ViewModel) ist möglich aber nicht zwingend.
WPF ist nunmal flexibel bis zum geht nicht mehr.
Gruß Elmar