WPF ListBoxItemTemplate Problem
-
grr - grad mal probiert - funktioniert mit dem focus auch nicht - find ich aber bloed das man anscheinend doch das komplette ding neu zeichnen muss -
also meine bisherige loesung waere genau wie du das gemacht hast, im style template das setzen, wo man aber dann auch den border usw auch selber zeichnen muss (und das find ich doof)
-
Es gibt noch eine Möglichkeit, welche ich nun aus Interesse gefunden habe. Dazu muss man allerdings auf C# Code zurückgreifen. Ob es einfacher ist, sei mal dahingestellt
Ich habe eine kleine Testanwendung geschrieben, das Wichtigste zeige ich mal kurz.Zuerst einmal den XAML Code der ListBox:
<ListBox Name="m_lsbTest" SelectionChanged="listboxSelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Name="textBlock" Text="{Binding}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>
Wichtig hier ist, dass ich auf SelectionChanged reagiere und dem TextBlock im Template einen Namen gebe.
In der Windowklasse brauchen wir 3 weitere Variablen:
Storyboard m_selectedAnimation; Storyboard m_unselectedAnimation; // Diese Variable, weil ich eine Unselect Animation drin habe. // Es macht die Aufgabe ein wenig leichter. TextBlock m_lastSelection;
Im Konstruktor oder halt irgendwo, wo der Initialisierungscode zu finden ist, werden die Animationen erstellt, zudem fülle ich noch die ListBox mit ein paar Werten.
m_lastSelection = null; m_selectedAnimation = new Storyboard(); m_unselectedAnimation = new Storyboard(); // Erste Animation (Selected) DoubleAnimation animation = new DoubleAnimation(); animation.To = 16; Storyboard.SetTargetProperty(animation, new PropertyPath(TextBlock.FontSizeProperty)); m_selectedAnimation.Children.Add(animation); // Zweite Animation (Unselected) animation = new DoubleAnimation(); animation.To = 12; Storyboard.SetTargetProperty(animation, new PropertyPath(TextBlock.FontSizeProperty)); m_unselectedAnimation.Children.Add(animation); // Ein paar Werte in die ListBox setzen. m_lsbTest.Items.Add("Firefighter"); m_lsbTest.Items.Add("Mr Evil"); m_lsbTest.Items.Add("Dravere");
Schlussendlich kommt noch der RoutedEvent Handler:
public void listboxSelectionChanged(object sender, SelectionChangedEventArgs args) { int selectedIndex = m_lsbTest.SelectedIndex; ItemContainerGenerator generator = m_lsbTest.ItemContainerGenerator; ListBoxItem item = (ListBoxItem)generator.ContainerFromIndex(selectedIndex); if(null != m_lastSelection) { m_unselectedAnimation.Begin(m_lastSelection); } ContentPresenter presenter = findVisualChild<ContentPresenter>(item); DataTemplate dataTemplate = presenter.ContentTemplate; TextBlock textBlock = (TextBlock)dataTemplate.FindName("textBlock", presenter); m_selectedAnimation.Begin(textBlock); m_lastSelection = textBlock; }
findVisualChild ist eine eigene Funktion. Ich habe sie in der MSDN gefunden in einem HowTo, welches erklärt, wie man ein Element aus einem DataTemplate findet. So kam ich auch zum Teil auf den Code im Handler.
public ChildT findVisualChild<ChildT>(DependencyObject obj) where ChildT : DependencyObject { for(int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); ++i) { DependencyObject child = VisualTreeHelper.GetChild(obj, i); if(null != child && child is ChildT) { return (ChildT)child; } else { ChildT result = findVisualChild<ChildT>(child); if(null != result) { return result; } } } return null; }
http://msdn.microsoft.com/en-us/library/bb613579.aspx
Und es funktioniert ... auch wenn es etwas kompliziert ist. Aber ich denke da kann man sich eine Hilfsklasse daraus bauen, falls man das mehrmals macht und ab in die eigene Utilities Bibliothek damit
Kann man Animationen auch in den Ressourcen speichern? Wenn ja, dann wäre das auch noch eine Möglichkeit, um die Animationen abzulegen und einheitlich über das ganze Projekt hinweg zu benutzen. In der Hilfsklasse dann gleich eine entsprechende Möglichkeit einbauen.
Grüssli
-
Dravere schrieb:
.. Kann man Animationen auch in den Ressourcen speichern? ..
ja kann man, das Storyboard direkt
also
<Bla.Resources>
<Storyboard x:Key=".."
..
</Storyboard>
</Bla.Resources>[/quote]ich hab da auch n bissl drueber nach gedacht und dachte da aber eher an einem Decorator
"System.Windows.Controls.Decorator"
hab sowas aber bisher aber noch nicht gemacht - waer evtl ne uebung wert
-
Interessante Diskussion, mir erscheint die letzte Variante von Dravere auch als angemessen, wenn auch etwas mit der Kirche ums Dorf, aber effizient. Danke Dravere und Mr Evil;)
-
naja was heisst angemessen
man nehme das style was dravere schon postete und erweitert es um ein border (um das alte aussehen wieder her zu stellen) schon hat man das selbe ergebnis + ist kuerzer<Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Border BorderThickness="1" BorderBrush="Black" Fill="LightBlue"> <TextBlock x:Name="ItemText" Text="{Binding}" /> </Border> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="ListBoxItem.Selected"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="ItemText" Storyboard.TargetProperty="FontSize" From="12" To="16" AutoReverse="True" /> </Storyboard> </BeginStoryboard> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
wenn man es noch korrekter machen moechte dann passt man eine kopie an:
(das kann man ja auch noch anpassen indem man unnoetiges raus schmeisst und manches nach belieben aendert)<Style TargetType="{x:Type ListBoxItem}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> <Setter Property="Padding" Value="2,0,0,0"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> <TextBlock x:Name="ItemText" Text="{Binding Name}" /> </Border> <ControlTemplate.Triggers> <EventTrigger RoutedEvent="ListBoxItem.Selected"> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetName="ItemText" Storyboard.TargetProperty="FontSize" From="12" To="16" AutoReverse="True" /> </Storyboard> </BeginStoryboard> </EventTrigger> <Trigger Property="IsSelected" Value="true"> <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/> </Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="true"/> <Condition Property="Selector.IsSelectionActive" Value="false"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> </MultiTrigger> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
ich find das die richtige und angemessene methode - so hat man wenigstens auch eine klare trennung - und es ist stand-alone xaml
(es sein denn man kann ein decorator anpassen, nur dann glaub ich muss man das template auch ueberschreiben, kann kann man es auch direkt so machen)//Dazuedit: was natuerlich nicht bedeuten soll das dravere sein code schlecht waere {o;
-
Wirklich sehr sauber, mr evil.Danke für die Ausführung.
-
Mr Evil schrieb:
... schon hat man das selbe ergebnis
Ist das eigentlich garantiert? Ich meine, du probierst in deinem Style das Standardverhalten nachzubilden, wenn ich das richtig verstanden habe. Allerdings kopierst du nicht das Standardverhalten, wenn dieses also in einer neuen Version irgendwie verändert wird, dann wird das in deiner Version nicht übernommen, richtig?
Mr Evil schrieb:
... + ist kuerzer
Najaaaaa, ob das wirklich kürzer ist?
Mr Evil schrieb:
//Dazuedit: was natuerlich nicht bedeuten soll das dravere sein code schlecht waere {o;
LoL, kann schon sein dass der Code schlechter ist, ich habe mich nur in meiner vertrauten Umgebung bewegt, also im Bereich wo es Code hat. Mit XAML bin ich immer noch nicht so vertraut
Grüssli
-
Dravere schrieb:
Mr Evil schrieb:
... schon hat man das selbe ergebnis
Ist das eigentlich garantiert? Ich meine, du probierst in deinem Style das Standardverhalten nachzubilden, wenn ich das richtig verstanden habe. Allerdings kopierst du nicht das Standardverhalten, wenn dieses also in einer neuen Version irgendwie verändert wird, dann wird das in deiner Version nicht übernommen, richtig?
Doch, durch die ganzen bindings (mein laengeres beispiel) uebernimmt man das vom system
Dravere schrieb:
Mr Evil schrieb:
... + ist kuerzer
Najaaaaa, ob das wirklich kürzer ist?
zaehl doch mal die zeilen zusammen #gg
habs mal zum spass gemacht:
meins: 39 zeilen
deins: 37 zeilen (comments, leere oder "{" zeilen weg gelassen
stimmt - deins is kuerzer - aber dafuer ist deins verteilter #ggDravere schrieb:
Mr Evil schrieb:
//Dazuedit: was natuerlich nicht bedeuten soll das dravere sein code schlecht waere {o;
LoL, kann schon sein dass der Code schlechter ist, ich habe mich nur in meiner vertrauten Umgebung bewegt, also im Bereich wo es Code hat. Mit XAML bin ich immer noch nicht so vertraut
Grüssli
so hab ich auch angefangen, hatte anfangs viele animation dann per c# geloest da es mir in xaml dann zu kompliziert wurde - das ist aber nu vorbei
zusammen mit MVVM weiss der c# code eh nichts mehr von der ui (und ich entwickel nur noch MVVM (in der code behind ist kein custom code mehr, also leer)
-
Mr Evil schrieb:
Doch, durch die ganzen bindings (mein laengeres beispiel) uebernimmt man das vom system
Wenn ich dein Beispiel richtig verstanden habe, dann übernimmst du doch nur die Farben und nicht das Verhalten? Wenn sich also das Standardverhalten ändert, dann würde das nicht übernommen werden, oder?
Mr Evil schrieb:
meins: 39 zeilen
deins: 37 zeilen (comments, leere oder "{" zeilen weg gelassen
stimmt - deins is kuerzer - aber dafuer ist deins verteilter #gggah ... Wenn ich gewettet hätte, dann hätte ich gesagt, dass deins, auch mit weglassen der Kommentare und Leerzeilen bei meinem, kürzer wäre.
Gegen die Verteilung könnte man aber durch die Hilfsklasse durchaus noch etwas entgegenwirkenMr Evil schrieb:
zusammen mit MVVM weiss der c# code eh nichts mehr von der ui (und ich entwickel nur noch MVVM (in der code behind ist kein custom code mehr, also leer)
MVVM?
Model View Con ... eh ne ... View Model??Grüssli
-
Dravere schrieb:
Mr Evil schrieb:
Doch, durch die ganzen bindings (mein laengeres beispiel) uebernimmt man das vom system
Wenn ich dein Beispiel richtig verstanden habe, dann übernimmst du doch nur die Farben und nicht das Verhalten? Wenn sich also das Standardverhalten ändert, dann würde das nicht übernommen werden, oder?
welches verhalten ? select und das alles wird ja nicht beeinfluss - ein template ist ja _nur_ fuer das aussehen - aendert an dem verhalten nichts, und wenn du oben das lange mal schaust siehst du das durch multitrigger selektieren usw alles abgearbeitet wird, duerfte eigendlich nichts fehlen
und wenn doch - macht doch nichts, denn es ist ja eh nur fuer den speziellen fall der groessenaenderung beim select, da kann man die listbox gleich noch schicker machen als system default {o; ein groesser werden der font ist ja auch nicht gerade default #ggDravere schrieb:
Mr Evil schrieb:
meins: 39 zeilen
deins: 37 zeilen (comments, leere oder "{" zeilen weg gelassen
stimmt - deins is kuerzer - aber dafuer ist deins verteilter #gggah ... Wenn ich gewettet hätte, dann hätte ich gesagt, dass deins, auch mit weglassen der Kommentare und Leerzeilen bei meinem, kürzer wäre.
Gegen die Verteilung könnte man aber durch die Hilfsklasse durchaus noch etwas entgegenwirkenbei meinem haette ich ja auch die closing tags weg lassen koennen - dann wirds nochmal arg kuerzer #gg
aber wie gesagt, fuer diesen fall kan man ja auch ein paar sachen weglassen oder umformenDravere schrieb:
Mr Evil schrieb:
zusammen mit MVVM weiss der c# code eh nichts mehr von der ui (und ich entwickel nur noch MVVM (in der code behind ist kein custom code mehr, also leer)
MVVM?
Model View Con ... eh ne ... View Model??Grüssli
Model-View-ViewModel richtig
wpf ist die perfekte grundlage dafuer um von diesen code-behind zeug los zu werden
die buisness logic kennt die ui nicht, und die ui kennt die logic nicht - so kann man die ui beliebig austauschen (auch zur laufzeit) ohne nur ein fetzen c# code aendern zu muessen {o; (aber das hier weiter aus zu fuehren wuerde den ramen sprengen (und offtopic werden), wenn du genaueres wissen willst kannst du mir auch ne mail schreiben, (dann kann ich dir ne beispiel solution zuschicken) {o; mr.evil.evl[at]gmx[dot]net)