I would like to have an animation when an item in ListView changes position, so it would slowly move to the new position. Either in a template or in code.
I've tried descending from a (Virtualizing)StackPanel and overriding ArrangeOverride to reposition and animate the items. the problem is that I don't know at what position the item was 'before' the update so I could transition to the new position nicely. I tried checking the TranslateTransform of the item, storing in a dictionary, overriding OnItemChanged and storing OldPosition/Position .. but none of there work because it seems the items are always recreated (from template).
Any other suggestions?
Use FluidMoveBehavior behaviour , it will make you life lot easier.
you can apply this to any itemscontrol in the following manner
<ItemsPanelTemplate x:Key="ItemsPanelTemplate">
<WrapPanel>
<i:Interaction.Behaviors>
<il:FluidMoveBehavior AppliesTo="Children" Duration="00:00:00.75"/>
</i:Interaction.Behaviors>
</WrapPanel>
</ItemsPanelTemplate>
you can find this behaviour in the Microsoft.Expression.Interactions.dll that is installed along with Blend 3
In fact this problem was perfectly solved by Dan Crevier back in 2006. Check out his PanelLayoutAnimator class.
Related
I'm looking to create a UI element in a WPF XAML UserControl with something that looks and works roughly like Google Suggest - a TextBox with a ListBox that appears beneath it showing suggestions that match the text entered in the TextBox. In simplified form, my XAML looks like this:
<StackPanel>
[...controls above...]
<TextBox ... />
<ListBox ItemsSource="{Binding SearchHints}"
Visibility="{Binding HasSearchHints}" MaxHeight="100" />
[...controls below...]
</StackPanel>
What I'm struggling to achieve is I want the ListBox to float above any content that may come below it, much like the dropdown part of a ComboBox does. Currently it pushes any controls below it downwards. I figure this must be possible because the ComboBox control essentially does exactly what I want to do. In CSS it would be a matter of setting the element to position:relative but there doesn't seem to be an immediately obvious equivalent in XAML. Any ideas?
Used Icepickle's comment - the element did exactly what I wanted.
Simple question. We want a divider to appear between each item in a horizontally styled listbox. Think the arrows between entries in a crumbtrail. However, we don't want that divider to be part of the selection, so it really should go between each ListBoxItem. Is it possible to do that?
The only two ways I can think to do this is to insert 'separator' items into the data and template them differently, but that requires mucking with the items collection, which you may not be able to do in a data bound situation.
The only other way would be to style the actual ListBoxItem to have a separator outside of the highlighting border, then template the last one in the list separately.
Can anyone think of any other way to do this?
Interesting question!
As you said, one way would be to mock the ItemsSource collection.
Such as:
<ListBox.ItemsSource>
<MultiBinding Converter="{StaticResource mockConverter}">
<Binding Path="YourCustomList" />
</MultiBinding>
</ListBox.ItemsSource>
Note that I used multibinding with only one binding. I believe that is the correct way in order for the converter to be "re-run". This way, when you modify items in "YourCustomList", it will retrigger the converter.
Unlike this, which will trigger converter only once(when ItemsSource is first binded against)
<ListBox ItemsSource="{Binding YourCUstomList, Converter={StaticResource mockCOnverter}}" />
^ wont work if you add new items.(Converter is only re-run when YourCustomList property changes)
MockConverter will be easy, just make sure you generate list with seperators. You need new class for a seperator. This way you can easily use DataTemplates(DataType). Next you probably have to set up a new trigger in ItemContainerStyle in order to set IsEnabled=false when object=Seperator.
It will be relatively painless and I see no complications. It's a little ugly because seperators should not be a ListBoxItems.
Perhaps this could also work.
You can override ListBox ItemsPanelTemplate with your own. In your own template, you can do whatever you want. Perhaps add seperators. This way, you wont touch ItemsSource.
Check out StackPanel.cs for code.
I can't provide with you a code right now, but the idea is this; you inherit from StackPanel, and overwrite Measure() and Arrange(). With those functions, you can calculate how big your stackPanel should be, and provide the location(X,y) where the seperators should be drawn at.
Note that the seperators have to be Children of StackPanel and they need to have IsHitTestVisible=False(so they wont generate events).
Later solution takes time, but if you are learning WPF, then why not?
DataTemplate is fine, I don't think you would have problems mocking the items collection. Why yould you ?
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Seperator/>
<TextBlock Text="{Binding .}"/>
</StackPanel/>
</DataTemplate>
I won't recommend the last option you had. That's just not right to have it separated style just to achieve what you want.
Any idea what approach I would follow to get the items of a databound listbox to "fly" into the their position in the listbox similarly to the effect you see when a deck of cards is dealt in those Windows Card Games? (I'm using WPF)
You would need to extend the Panel class and use it as the ItemsPanelTemplate of your ListBox.ItemsPanel property. It's really easy... there's just two methods to override; one to measure the items in the ListBox and one to arrange them. Here is a microsoft article on the subject.
Here is perhaps a more useful article [unfortunately, no longer available] that shows how to animate the items. For your effect, you would simply set the from value on your position animations to be the same location just off the viewable area for each of your items. For example, using a from position of 0, finalSize.Height would mean that each item would slide to its position from the bottom left corner of the ListBox.
You could use your new animated Panel as follows:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<YourXmlNamespace:YourAnimatedPanel AnyCustomProperty="value" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
alt text http://akashkava.com/blog/wp-content/uploads/2009/12/ListBoxItem-Selection-Problem.png
The following code, behaves little strange.
<ListBox SelectionMode="Multiple">
<StackPanel>
<TextBlock>Selection is Black</TextBlock>
</StackPanel>
<ListBoxItem>
<StackPanel>
<TextBlock>Selection is White</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
Note that first selection stays in black color, that is because the Item is not its own ItemContainer, however in 2nd case Item is its own ItemContainer so the style works correctly. Majority of time we populate data through templates and everytime our Item is not its own ItemContainer and thats why TextBlock behaves strange with respect to foreground color.
Important: I am looking for the answer to "Why this happens" not how to solve it, I know the workaround TextElement.Foreground="{TemplateBinding Foreground}", but I want to know why this is happening.
I guess, that's because the logical trees are different here:
alt logical tree http://img13.imageshack.us/img13/9082/logicaltree.jpg
When you explicitly specify ListBoxItem you add it to the logical tree, and Foreground is inherited from it. In case with StackPanel, logical tree is different, and Foreground is inherited from different parent... I'm sure you've read this before, but just for the log. There is really great article from Mike Hillberg: Of logical and visual trees in WPF. He explains this behavior very clearly.
Cheers
I have a databound and itemtemplated ListBox:
<ListBox x:Name="lbLista"
ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Deleteable, Mode=TwoWay}" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ites show fine and they come from an ObservableCollection.
The problem is the scrollbar which appears but is not usable - it does not have a handle to grab. I've tried setting some ScrollView attached properties on ListBox, but they do not affect the situation.
I pasted your code into test project, added about 20 items and I get usable scroll bars, no problem, and they work as expected. When I only add a couple items (such that scrolling is unnecessary) I get no usable scrollbar. Could this be the case? that you are not adding enough items?
If you remove the ScrollViewer.VerticalScrollBarVisibility="Visible" then the scroll bars only appear when you have need of them.
ListBox will try to expand in height that is available.. When you set the Height property of ListBox you get a scrollviewer that actually works...
If you wish your ListBox to accodate the height available, you might want to try to regulate the Height from your parent controls.. In a Grid for example, setting the Height to Auto in your RowDefinition might do the trick...
HTH
I have never had any luck with any scrollable content placed inside a stackpanel (anything derived from ScrollableContainer. The stackpanel has an odd layout mechanism that confuses child controls when the measure operation is completed and I found the vertical size ends up infinite, therefore not constrained - so it goes beyond the boundaries of the container and ends up clipped. The scrollbar doesn't show because the control thinks it has all the space in the world when it doesn't.
You should always place scrollable content inside a container that can resolve to a known height during its layout operation at runtime so that the scrollbars size appropriately. The parent container up in the visual tree must be able to resolve to an actual height, and this happens in the grid if you set the height of the RowDefinition o to auto or fixed.
This also happens in Silverlight.
-em-
Thnaks for answer. I tried it myself too to an Empty Project and - lo behold allmighty creator of heaven and seven seas - it worked. I originally had ListBox inside which was inside of root . For some reason ListBox doesn't like being inside of StackPanel, at all! =)
-pom-