Silverlight Animating Control in ListBox - silverlight

I've got a user control that I'm using as my DataTemplate for all the items in my ListBox. There's an animation in said UserControl that's pretty simple - it just expands a certain ListBox, and it works. The thing is, when I scroll, every Nth item ALSO has the ListBox expanded, where N depends on how big my browser is sized to (in other words, how many items the ListBox is holding at any one time.)
It seems as though new items getting loaded into the listbox as I scroll are tripping over this animation. Is there anything I can do about this?

If your outer ListBox contains only a few items then add this to its Xaml:-
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItesmPanelTemplate>
</ListBox.ItemsPanel>
By default the ListBox uses a VirtualizingStackPanel which only contains concrete instances of ListBoxItem that are currently being displayed. Items a generated as needed by a ItemContainerGenerator which will recycle existing items. I suspect some in there the state of an ListBoxItem is not entirely cleared when re-used to display another item from the ItemsSource.

Related

Combobox Text property not changing in user control

I have a list box that has a list of user controls. Each user control has 5 combo boxes. I want to be able to read the selected text of each combo box in each user control from the main application. However, when I change the selection in the combo box, the text property of the combo box in the user control doesn't change when I read it in the main application.
Code-behind:
radQueryParamList.Items.Add(new TCardQueryParameters());
Xaml (This is just a data template for how to display a TCardQueryParameters object):
<DataTemplate x:Key="TCardViewQueryParamDataTemplate">
<tcardqueryparam:TCardQueryParameters x:Name="TCardViewerParamUC" />
</DataTemplate>
<telerik:RadListBox Grid.Column="1" ItemTemplate="{StaticResource TCardViewQueryParamDataTemplate}" Name="radQueryParamList" VerticalAlignment="Top" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Grid.ColumnSpan="3">
Where I loop over the list of user controls
string test = radTESTGACC.Text;//TEST combo box, Text property changes
//radQueryParamList is a listbox of user controls where TCardQueryParameters is the UC
foreach(TCardQueryParameters param in radQueryParamList.Items)
{
//Each UC has a radGACC combo box in it, and I am reading what the user
//selected for each user control here in the main app, but the text property
//never changes
String gacc = param.radGACC.Text; //Text property DOESN'T CHANGE
}
I thought that each instance of the user control would keep its own state and I would just be able to read what the user selected for that combo box, but that doesn't seem to be the case.
You have not bound the SelectedItem, SelectedValue, or SelectedIndex property of your internal ComboBox to anything so it maintains its selection.
An ItemTemplate is like a cookie cutter. It contains the definition of the object, but not the object itself. Properties specific to the object's state are lost unless they are bound to something on the DataContext behind the template.
This is important to note for two aspects.
First off, to improve performance WPF usually unloads items which are not visible, which often results in items being re-created from their template anytime they are reloaded. An example would be when you minimize an application to the taskbar, then maximize it again. This is usually better on performance and memory usage, however it does mean you have to be sure you store the state of items that were created with a Template somewhere.
And second, by default ListBoxes use something called virtualization. A simple way of explaining this would be this:
Suppose you have a ListBox of 100,000 items. In your ListBox, only 10 items can be visible at a time. WPF will render roughly 14 items (the 10 visible ones, and then a few extra for a scroll buffer so you don't see anything unusual while scrolling). When you scroll to new items, WPF just re-uses the existing items that are already rendered, and just replaces the DataContext behind those items.
As you can guess, it is far better on performance to render 14 UI items instead of 100k items.
So to answer your question, you will probably want to bind either SelectedItem, SelectedValue, or SelectedIndex of your TCardQueryParameters UserControl to a property on the DataContext (which in your case appears to be another different UserControl).
It should probably be noted that what you are essentially doing is creating a list of UserControls, assigning them to the ListBox, and then telling the ListBox that it should draw each UserControl with another separate UserControl. So although you are changing the SelectedItem in the template UserControl, that change is not being reflected to your ListBox.Items copy of the UserControl.
I'm guessing you probably don't want that, so would recommend removing your ItemTemplate completely.
Or better yet, create a new class object containing all the data for your UserControl, and adding that to your ListBox.Items. Then tell the ListBox to draw that item using a TCardQueryParameters UserControl as the ItemTemplate, like you have now.

Data virtualization in a WPF listbox

I have a scenario where in I populate a listbox with a 1000 items. I set the ItemsSource property with a source of data.
I have a requirement where I need to strike out an item of the listbox based on certain criteria, when the UI loads. I am using styles + attached properties to achieve the same by setting ContentTemplate of ListBoxItem in the callback method of attached property.
My problem is when I try to generate a ListBoxItem using ItemContainerGenerator.ContainerFromItem, for an item which is at the end of the list, I get null. As a result, I cannot strike out items of the listbox which are at the bottom of the list.
Has it got something to do with virtualization. I want to obtain all those ListBoxItems on load.
Is there any workaround?
Thanks
This is definitely caused by virtualization. This is exactly what UI virtualization is supposed to do - only create ListBoxItem objects for items that are visible on the screen. You can easily see that this is indeed the cause by setting VirtualizingStackPanel.IsVirtualizing = false on your ListBox and see that ItemContainerGenerator.ContainerFromItem no longer returns null.
You can set a style for your ListBoxItems in your ListBox that will have the logic to strike out the items as needed. This should also work when virtualization is enabled. For example:
<ListBox>
<ListBox.Resources>
<Style TargetType=ListBoxItem>
...
</Style>
</ListBox.Resources>
</ListBox>

WPF Animation on listbox items

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>

Window size and TabControl with DataBinding (Size of Items)

The following scenario bothers me:
I have a simple WPF Window that has a TabControl as content. The ItemsSource of the TabControl is bound to a list of objects. In order to visualize the objects I defined some DataTemplates. As the list of objects may have different types, the right visualization is chosen by the default template selector. This works fine and does not cause any trouble.
The issue that came up is the size of the window. The DataTemplates have different sizes. And I want the dialog to have a size that the largest DataTemplate fits. When I use SizeToContent in the Window, the Window changes its size everytime I change the tabs.
So, my questions is, how can I achieve to make the window fit the largest TabItem (which size is determined by the DataTemplate)?
thanks,
Florian
The problem you are having, is that because the larger DataTemplate is not shown, it's size is not taken into account when sizing to content.
Your options:
1) Manually set (min)width/height on appropriate controls (tabcontrol, Window, DataTemplate, etc)
2) If you know that a certain tab is always going to be bigger than the rest, you can bind the width/height of the other tabs to the bigger tab:
<TabItem>
<StackPanel Name="stackPanelBiggest" />
</TabItem>
<TabItem>
<StackPanel Width="{Binding ElementName=stackPanelBiggest, Path=ActualWidth}" />
</TabItem>
I think that for the above to work, the biggest tab has to be first one shown. Although the tab control destroys the visual tree of the previous tab when swithching to another tab, this method is still working for me (despite the fact that the ActualWidth should be 0 or NaN, after switching tabs).

How to bind StackPanel Children from a ViewModel?

New to Silverlight. I'm working on a chat application where new chat messages are added to the bottom of a list. I had a working version that used as StackPanel inside a ScrollViewer and then in some code behind used StackPanel.Children.Add().
I'm trying to convert this to a View-ViewModel approach, and I can't figure out how to bind the Children of the StackPanel to any collection property. I've tried this:
<ScrollViewer Name="scrollMessages" Grid.Row="2" Margin="0,0,0,0" VerticalScrollBarVisibility="Visible">
<StackPanel x:Name="pnlMessages" Orientation="Vertical" Children="{Binding Path=ExampleTBs}" />
</ScrollViewer>
where ExampleTBs is a collection of TextBlocks created in code. This fails XAML parsing, the Children property isn't bindable in this way.
Is the approach of binding to the StackPanel itself fixable? Should I be using a different container type? I saw another question where the guy created the entire StackPanel in code and then used a ContentPresenter...
Bottom line, I'd like to find a way to databind my view to a viewmodel using something like a StackPanel as a container where successive items will be added to the container over time. Best approach?
Use a ListBox (or any other ItemsControl) and bind the ItemsSource property to an ObservableCollection in your ViewModel.
Do you need to use a StackPanel? If you use an ItemsControl instead, this still presents each chat message in a vertical list, and also allows for binding of the data.

Resources