| 

.NET C# Java Javascript Exception

2
Hallo.

Ich habe wie folgend zu sehen ein TabControl mit einem DataTemplate.
Nun möchte ich aus dem Code die Eigenschafteh des ImageControls abhängig für jedes TabItem verändern aber ich komme nicht an das ImageControl. Es geht mir also nicht darum ein anderes Template zuzuweisen sondern das Element direkt anzusprechen...

Versucht habe ich folgendes (wobei ti das TabItem ist dessen Image ich ändern möchte):

Private Sub Test
...
Dim cp = FindVisualChild(Of ContentPresenter)(ti)
Dim dt = cp.ContentTemplate
Dim z = dt.FindName("imgTabItem", cp)
...
End Sub


Private Function FindVisualChild(Of childItem As DependencyObject)(ByVal obj As DependencyObject) As childItem
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(obj) - 1
Dim child As DependencyObject = VisualTreeHelper.GetChild(obj, i)
If child IsNot Nothing AndAlso TypeOf child Is childItem Then
Return CType(child, childItem)
Else
Dim childOfChild As childItem = FindVisualChild(Of childItem)(child)
If childOfChild IsNot Nothing Then
Return childOfChild
End If
End If
Next i
Return Nothing
End Function



Leider gibt das nur eine Fehlermeldung

Danke im voraus.

<TabControl x:Name="tcProperties"
Grid.Row="3"
Grid.Column="1"
TabStripPlacement="Left">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<ContentPresenter>
<ContentPresenter.Content>
<StackPanel Orientation="Horizontal"
x:Name="sp">
<Image Height="16"
Name="imgTabItem"
Source="/tpwk;component/Images/Enable16.png">
</Image>
<TextBlock Margin="4"
FontSize="12"
Text="{TemplateBinding Content}"
Width="90" />
</StackPanel>
</ContentPresenter.Content>
</ContentPresenter>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
News:
17.06.2012
MaHo 61 3
"Leider gibt das nur eine Fehlermeldung".
Die da wäre? Und in welcher der beiden Methoden? Fehlermeldungen enthalten häufig interessante Texte die auf die Fehlerursache hinweisen :)
Jens Duczmal 17.06.2012
Hallo Jens,

sorry Du hast recht. ich bin davon ausgegangen das ich einen so offensichtlichen Fehler gemacht habe den alle außer mir sofort sehen würden.

Folgend der Text der Exception:
System.InvalidOperationException: Dieser Vorgang ist nur gültig für Elemente, auf die diese Vorlage angewendet wird.

Ausgelöst wird er durch die Funktion „FindName“.
Ich habe daraufhin einen Blick in die ChildNames Auflistung des DataTemplates geworfen, dort finde ich aber genau meinen gesuchten Namen „imgTabItem“.

Danke.
MaHo 18.06.2012
Ich kann es leider gerade nicht ausprobieren. Eine der ersten Fragen die ich mir aber selbst gestellt habe war, ob Du nicht doch über das TabControl und nicht über das TabItem suchen musst. Das DataTemplate gilt zwar für das TabItem aber indirekt hängt es ja am TabControl. Die Meldung klingt ein wenig so, als wäre das TabItem nicht das gültige Element.

Wenn Du einen Tip gibts, was Du mit dem Bild machen möchtest, findet sich vielleicht eine andere Lösung. Du kannst das Bild sicher auch an etwas binden, falls Du es abhängig vom Tabitem wechseln möchtest.
Jens Duczmal 18.06.2012
Der Benutzer soll eine visuelle Rückmeldung erhalten ob auf dem jeweiligen TabItem noch eine Eingabe erforderlich ist…
Image Rot = Eingabe erforderlich
Image Grün=Alle Eingaben abgeschlossen
Damit er nicht ständig alle einsehen muss…
MaHo 18.06.2012
2 Antworten
0
Hallo Jens,
18.06.2012
MaHo 61 3
0
Ich antworte jetzt einmal hier. Wenn Du lediglich ein rotes oder grünes Bild anzeigen möchtest, je nachdem ob ein TabItem noch Eingaben erfordert, so kannst Du das recht problemlos über Binding erledigen.

Je nachdem ob Du mit MVVM oder mit CodeBehind arbeitest, wirst Du evtl. für jedes TabItem ein eigenes ViewModel haben. In dem Fall gibtst Du dem ViewModel einfach eine Property mit.
Wenn die Property auf True ist, zeigst Du das eine Icon an, sonst das andere. Zudem muss Deine Klasse "INotifyPropertyChanged" implementieren, WPF wird damit benachrichtigt, wenn sich Eigenschaften verändern und damit können dann auch Steuerelemente aktualisiert werden.

Class DeineKlasse
Implements INotifyPropertyChanged

Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged

Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub


Private _inputCompleted As Boolean
Public Property InputCompleted() As Boolean
Get
Return _inputCompleted
End Get
Set(ByVal value As Boolean)
_inputCompleted = value
NotifyPropertyChanged("InputCompleted")
End Set
End Property

End Class


Danach erstellt Du eine neue Klasse mit einer Methode, die Boolean nach Visiblity konvertiert.
Imports System.Globalization
Public NotInheritable Class BooleanToVisibilityConverter
Implements IValueConverter
Public Property IsReversed() As Boolean
Get
Return m_IsReversed
End Get
Set(ByVal value As Boolean)
m_IsReversed = value
End Set
End Property
Private m_IsReversed As Boolean
Public Property UseHidden() As Boolean
Get
Return m_UseHidden
End Get
Set(ByVal value As Boolean)
m_UseHidden = value
End Set
End Property
Private m_UseHidden As Boolean
Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
Dim val = System.Convert.ToBoolean(value, CultureInfo.InvariantCulture)
If Me.IsReversed Then
val = Not val
End If
If val Then
Return Visibility.Visible
End If
Return If(Me.UseHidden, Visibility.Hidden, Visibility.Collapsed)
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class


Du fügst einen bzw. zwei Converter für Boolean nach Visiblity ein (Es gibt bessere Converter, aber ich habe den gerade nicht zur hand)
<Window.Ressources>
<local:BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<local:BooleanToVisibilityConverter IsReversed="true" x:Key="NotBoolToVisibility"/>

</Window.Ressources>

Dein DataTemplate änderst Du wie folgt ab
<StackPanel Orientation="Horizontal" x:Name="sp">
<Image Height="16"
Source="/tpwk;component/Images/Enable16.png"
Visibility={Binding InputCompleted,
Converter={StaticResource BoolToVisibility}/>
<Image Height="16"
Source="/tpwk;component/Images/Disable16.png"
Visibility={Binding InputCompleted,
Converter={StaticResource NotBoolToVisibility}/>
<TextBlock Margin="4" FontSize="12"
Text="{TemplateBinding Content}"
Width="90" />
</StackPanel>


Jedes mal, wenn Du die "InputCompleted"-Eigenschaft neu setzt, sollte sich nun auch die Anzeige des Icons aktualisieren.

Je nach Aufbau Deines Programmes, müsstest Du für jedes Tab-Item halt eine eigene Eigenschaft einbauen (InputCompleted1, InputCompleted2 etc.). Das ist vielleicht nicht die sauberste Lösung aber vermutlich die, die gerade am nächsten liegt.
18.06.2012
Jens Duczmal 2,6k 3 9
Hallo Jens,
danke für die ausführliche Antwort.

Werde es jetzt so machen !

Aber interessieren würde mich schon noch ob der Ansatz falsch ist auf das Template zugreifen zu wollen…
Danke nochmal.
MaHo 19.06.2012
Nun ich weiss nicht, ob man hier direkt von falsch oder richtig reden kann. Von der Idee her ist es nicht notwendig, da WPF für solche Dinge eben andere Möglichkeiten bietet. Allerdings ist das halt mitunter mit vielen Änderungen versehen. Dein Ansatz wäre WPF-Fenster mit "CodeBehind" also Quellcode direkt am WPF. Meiner mit MVVM (such hier mal danach, Du wirst viel darüber finden). Dann nur wegen eines Icons "umzusteigen" rechtfertigt den Aufwand evtl. nicht. MVVM ist halt nur die empfohlende Vorgehensweise. Deshalb muss sie nicht die einzig richtige Lösung sein. Ich warte Infos anderer hier.
Jens Duczmal 19.06.2012
Das eigentliche Programm ist nach dem MVVM Konzept aufgebaut…keine Frage für größere Projekte ...nur bei dem "kleinen" Tool handelt es sich um ein Datenmigrationsprogramm das ich eigentlich ohne die Trennung UI / Logik aufbauen wollte.
MaHo 20.06.2012
Ah, sorry. Tja, da ich mit Templates/Styles so halbwegs auf Kriegsfuss stehe bzw. mich immer noch in WPF einarbeite, war das meine einzige Lösung :) Mehr mangels Wissen.
Jens Duczmal 20.06.2012

Stelle deine .net-Frage jetzt!