Error when Databinding with mode=twoway - silverlight

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>

Related

WPF: Can I have multiple ItemsControls with different ItemsSources?

In my WPF App, I want to have several items that are generated by the user on runtime. These are from different classes and hence, my original idea was to add them to different Observable Collections and then use them as ItemsSources od different ItemsControls. However, WPF gives me the error System.InvalidOperationException: Items collection must be empty before using ItemsSource. I'm not a WPF expert, but the answer to THIS SO question seems to indicate that I can only have 1 ItemsControl.
THIS SO question indicates that maybe I should use the CompositeCollection Class, but unlike in the cited question, I have several completely different Observable Collections for completely different tasks.
Here is the relevant part of my XAML.CS with two Collections: 1 of a custom Interface type and 1 of custom Class type
public MainWindow()
{
InitializeComponent();
DefaultWindowDefinition.ItemsSource = ProcessElements = new ObservableCollection<IProcessSimulator>();
PathControl.ItemsSource = PathElements = new ObservableCollection<VisualPath>();
}
And here is the relevant part of the XAML I tried to use:
<Grid x:Name="MainGrid"
Background="{StaticResource Alternating}"
MouseLeftButtonUp="grid_MouseLeftButtonUp"
ShowGridLines="True">
<ItemsControl Name="DefaultWindowDefinition"
ItemsSource="{Binding ProcessElements}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!--HERE IS A LONG LIST OF ELEMENTS-->
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!--TEMPLATE FOR THE 1ST ITEMSCONTROL-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<!--STYLE PROPERTIES FOR THE 1ST ITEMSCONTROL-->
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<ItemsControl Name="PathControl"
ItemsSource="{Binding PathElements}">
<DataTemplate>
<!--HERE IS A LIST OF OTHER TYPE OFELEMENTS-->
</DataTemplate>
</ItemsControl>
</Grid>
How should I approach this problem or rather, what C#/WPF element should I use? A reference and some words of simple explanation is more than enough, I can google the rest myself, I just don't know, what to look for really.
You should set the ItemTemplate property of the "PathControl" to your DataTemplate:
<ItemsControl Name="PathControl" ItemsSource="{Binding PathElements}">
<ItemsControl.ItemTemplate> <!-- NOTE THIS -->
<DataTemplate>
<!--HERE IS A LIST OF OTHER TYPE OFELEMENTS-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you omit the <ItemsControl.ItemTemplate> element, you are adding a DataTemplate to the Items collection of the ItemsControl and you can't also set its ItemsSource property.
Trying to do so will results in an System.InvalidOperationException exception being thrown with the error message you are getting.
It is perfectly fine for several ItemsControl to bind to the same source collection.
It seems like you are setting the ItemsSource twice. Once in the code behind and once in the XAML. Remove the code behind that sets the Items source and just initialize the observable collections. The XAML should take care of binding to the collections.

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.

StackPanel not updating

I am having an issue with a directly bound ObservableCollection not updating a StackPanel when new items are added. Any initial items show up correctly. Only items that are added later on fail to display.
XAML:
<ItemsControl x:Name="ImageTable" ItemsSource="{Binding Path=SystemObjectViewItems, Converter={StaticResource UIElementWrapper}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<ContentPresenter Content="{Binding Path=Value.View}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I am using Prism MVVM so I am binding to my ViewModel which has a property:
public ObservableCollection<SystemObjectViewPresentationModel> SystemObjectViewItems {get; set; }
The basic converter and binding are working as can be demonstrated by the fact that my initial item displays correctly. It’s only items that are added to the collection after initial binding that do not show up.
Any ideas?
Thanks,
Rick
I'm going to take a wild guess and say it's the StaticResource you're using.
If you're not returning an ObservableCollection from it and you're not binding it to watch the original ObservableCollection changes it won't work.
Can you post the code to the converter?

WPF: Problem with ItemsControl datatemplate as a user control

This works fine:
<ItemsControl ItemsSource="{Binding Persons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
But, I want to make use of a User Control in place of the stack panel with two TextBlocks using something like:
<DataTemplate>
<jasControls:NameView/>
</DataTemplate>
But, with this and a million other syntaxes, the NameView shows nothing, yet it works fine in a separate test, outside the ItemsControl, with explicitly set (dependency) property - e.g.
<jasControls:NameView FName="{Binding Path=Person.FirstName}" />
I can find hardly any examples of this anywhere, and need to know how to specify the properties of the user control - or how can the user control 'receive' the individual item type (a Person)? What syntax to use? Do I need to specify the datatype? Is ItemsControl causing a problem or should any similar control do e.g. ListBox? Can I design it so that the user control gets an entire Person object? What dependency properties do I need in the user control? In short, how to get data into the user control?! The actual business types involved will be a bit more complicated involving some logic in the user control, hence my desire to use a user control.
ANY direction on this will be very gratefully received - TIA
Assuming your Persons collection has many Person objects in it, the DataContext property of your NameView control will automatically be set to the Person object.
You wouldn't need any dependency properties to achieve this feat. Your NameView usercontrol would simply be:
<UserControl ...>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding LastName}"/>
</StackPanel>
</UserControl>
You should have no codebehind to get this information to display.
From there, you should be able to access the Person object from the DataContext property:
Person person = this.DataContext as Person;

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