DataTemplate is reevaluated on resize of ItemsControl - wpf

I found some unexpected behavior in my WPF program. I have a DataTemplate to visualize my data in an ItemsControl.
<ItemsControl ItemsSource="{Binding All}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Result:ResultItem/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WPFLibrary:MyPanel Rows="2" MinRows="4" MaxColumns="2" IsItemsHost="true" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The strange thing is that the ResultItem view is newly created every time MyPanels MeasureOverride is called and hence calls Measure of its childs.
Is there a way to "force" WPf to reuse the view?
Thank in advance

My mistake. The problem was that every time you create a new ObservableCollection the whole view is updated even if the content is not changed at all.

Related

Listbox reversed items order

I've spend couple of hours trying to reverse the order of listbox (bottom to top) as described on the picture:
That means that newly added items should be inserted at the top of the listbox. I do not understand that something so trivial can be so difficult to implement in wpf...
I do not want to use transient effects on panel and items itself because it causes strange behavior on scrollviewer. I can not use some sort definitions on ICollectionView because I want to use a data virtualization which provides ordered data and I have to add them as is.
This is my Listbox (ListView) definition:
<ListView ItemsSource="{Binding Collection}"
VirtualizingPanel.VirtualizationMode="Recycling"
VirtualizingPanel.ScrollUnit="Pixel"
ScrollViewer.IsDeferredScrollingEnabled="True"
IsSynchronizedWithCurrentItem="True"> <!--To manage scrolling from view model using ICollectionView.ScrollTo()-->
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
<!--To auto scroll to bottom (via calling ICollectionView.ScrollToBottom()-->
<i:Interaction.Behaviors>
<Behaviors:ScrollIntoCurrentItemBehavior />
</i:Interaction.Behaviors>
<!--To pin listbox panel to bottom-->
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel VerticalAlignment="Bottom"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
Is there any acceptable solution for that? I really appreciate any help! thanks

WPF itemscontrol child alignment

I have a WPF MVVM application with an itemscontrol. The number of horizontal child items is influenced by the itemscontrol width. There is only 1 thing i haven't solved. I'm trying to align the child elements in a way that there are always centered. I have made quick pictures in paint to demonstrate what i mean.
How it's now:
If the width further inceares then a forth item will be added horizontally. This function needs to stay.
How I would like to see it:
If there is enough room for a forth item then it needs to be added. I'm thinking that the answer could be a simple XAML property. Anybody an idea?
Put the ItemsControl in a Grid and set its HorizontalAlignment to Center:
<Grid>
<ItemsControl ItemsSource="{Binding Images}" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" .../>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>

Image binding works in Image not in ItemsControl

I am a bit baffled here. I have a List<BitmapImage> in a Viewmodel that is populating. I am trying to display the list on the view with an ItemsControl, but the Images don't show up. Oddly, I can access the same collection and get an image to display if I am using an Image tag.
<ItemsControl ItemsSource="{Binding Path=Images}" MinHeight="80">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" MinWidth="80" MinHeight="80" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Image HorizontalAlignment="Left" Name="image1" Stretch="Uniform" VerticalAlignment="Top" Source="{Binding Path=Images[0]}" MinHeight="80" MaxHeight="200" />
Note that they both point to Images. The Image shows up, the ItemsControl stays empty. What is going on?
You'll solve this by using an ObservableCollection as opposed to a List. The ItemsControl isn't going to rebind on the change notification of a List. You won't have to worry about explicitly calling the change notification for the List either if you use an ObservableCollection.
I was able to replicate your scenario and simply using an ObservableCollection solves the issue. As to why: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx
Specifically, see this quote in the Remarks section:
For change notification to occur in a binding between a bound client and a data source, your bound type should either: Implement the INotifyPropertyChanged interface (preferred). Provide a change event for each property of the bound type. Do not do both.

Error when Databinding with mode=twoway

All,
I'm pounding my head against the wall here. What I need is simple, and I'm sure there is a simple answer, but I can't seem to find it.
Situation: I have a Silver light 4.0 app and I'm currently binding a list of strings to an Items control. In the data template for that I have a simple text box that I was doing very basic Binding "{Binding}". I need to update the binding to be twoway so the edits are automatically pushed back to my datacontext.
Here is the Items control before I update the binding:
<ItemsControl x:Name="spLiftHeader" ItemsSource="{Binding Path=WeekLabels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="spLiftHeader" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
**<TextBox x:Name="txtWeekLbl" Text="{Binding}" Foreground="Black" Width="125" TextAlignment="Center"/>**
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the items control after the binding change:
<ItemsControl x:Name="spLiftHeader" ItemsSource="{Binding Path=WeekLabels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel x:Name="spLiftHeader" Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
**<TextBox x:Name="txtWeekLbl" Text="{Binding Mode=TwoWay}" Foreground="Black" Width="125" TextAlignment="Center"/>**
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I've just simply added the "Mode=TwoWay" to the binding.
After updating that, I get the amazingly useless error 4004
"System.Windows.Markup.XamlParseException: Provide value on 'System.Windows.Data.Binding' threw an exception" and the Line/Position reference points right do my updated Binding.
How does one add the Two Way mode to the simple binding?
Thanks in advance.
Nick
Two-way binding to an entire object (a string in this case) makes no sense to Silverlight so it is correct to throw an exception. Shame it is not a more useful error message :)
When there is no Path in the binding the ItemsControl can fetch a value using Object.ToString(), but where will it store the result back? It can't replace the string as that would require placing a new string object back in the collection. Two-way binding is done via reflection against a property of an object.
Instead of a simple list of strings, use a list of some new object that contains a string property and explicitly bind to that property. It will make everything a lot easier. (Make sure your new class and property implement INotifyPropertyChanged).
I am not 100% sure, but I don't think the the Mode=TwoWay should be set in the TextBox itself.
If that doesn't work, try it on both. However, in either case do not use List<T> as a data source behind that items control. Lists do not fire changed event when one of its items has changed. Use ObservableCollection<T> (from the System.Collections.ObjectModel namespace) instead of the List<T>

WPF Error: "Items collection must be empty before using ItemsSource."

Does anyone know why I keep getting the "Items collection must be empty before using ItemsSource"-Error?
Here is the code:
<ScrollViewer Margin="8,8,8,8" Grid.Row="3" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<WrapPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding}" x:Name="CustomerList" >>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal">
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<View:UserControlCustomerDetails>
</View:UserControlCustomerDetails>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</WrapPanel>
</ScrollViewer>
This is what I do in my Code-Behind:
CustomerList.ItemsSource = _mainViewModel.CustomerCollection;
Note that CustomerCollection is just a simple List<Customers>.
Thanks for your help!
Cheers
Is this code copied verbatim? Do you really have two right angle brackets (>>) at the end of the <ItemsControl... line? If so, the second right angle bracket might be getting treated as text content, which is getting added to the Items collection.
First, remove ItemsSource="{Binding}" from your ItemsControl. This should fix your error i believe.
Secondly, I'm not sure if your WrapPanel is going to work as expected in this case. From my understanding, WrapPanel will do wrapping when it has multiple children that extend out of bounds. In this case, your WrapPanel only has 1 child, an ItemsControl.
Apparently you're using the MVVM pattern. In that case you shouldn't explicitly assign a collection to the ItemsSource property... instead, you should assign a ViewModel to the DataContext of the Window (or UserControl). If your DataContext is _mainViewModel, your binding should be :
<ItemsControl ItemsSource="{Binding CustomerCollection}" ...
Use DataGrid.Items.Clear();
I hope it will be Helpfull...

Resources