WPF itemscontrol binding problems - wpf

<ItemsControl ItemsSource="{Binding ExportFormat, UpdateSourceTrigger=PropertyChanged}" Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" Margin="5" Height="50" Width="70" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.setExportFormat, UpdateSourceTrigger=PropertyChanged}" CommandParameter="{Binding}"></Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
So i have that code in my xaml and the button gets filed with a list of string. Depending on what the users chooses on previous Usercontrol that item will be filed with differents items. The problem is if the user select one option at the first run the button will be filled correctly but if the user go back and select the other option the control doenst update and shows the same as before..
My english is not the best but i think i could made me understand! Any idea?!
PS: the bindind on Button is to a readOnly property so i cant define it to Mode="TwoWay".. i took a look on the debug and the property ExportFormat gets updates with the new items but the usercontrol still displays the first option!!
Sincerely Rui Nunes

You didn't provide code-behind so I'm gonna take a few shots in the dark here:
The ExportFormatcollection is not an ObservableCollection (or more generally, doesn't implement INotifyCollectionChanged).
If it actually is an ObservableCollection, you assign it directly, instead of clearing its items and adding the new ones. example:
ExportFormat = MyNewObsCollection; //Bad
ExportFormat.Clear();
foreach(var newItem in myNewObsCollection)
{
ExportFormat.Add(newItem); //Good
}
Side note: ExportFormat should be readonly

Thanks to #Baboon for giving me some lights on this problem. So the Solution to my problem is:
So my ExportFormat Property was defined as:
Private _ExportFormat As New List(Of String)
Public Property ExportFormat As List(Of String)
Get
Return _ExportFormat
End Get
Set(value As List(Of String))
_ExportFormat = value
NotifyPropertyChanged("ExportFormat")
End Set
End Property
and i just had to change the List(of String) to ObjectModel.ObservableCollection(Of String)..
Private _ExportFormat As New ObjectModel.ObservableCollection(Of String)
Public Property ExportFormat As ObjectModel.ObservableCollection(Of String)
Get
Return _ExportFormat
End Get
Set(value As ObjectModel.ObservableCollection(Of String))
_ExportFormat = value
NotifyPropertyChanged("ExportFormat")
End Set
End Property
And my problems got solved.. Thanks once again!

Related

Listbox using a data template and dynamic data loading based on selection Enumeration Error

To anyone that can help or show me a better way. I have tried to do this with an observable collection, a list based on a custom class, global/non-global collections, using the listbox's itemssource, synclocking, and finally emptying and manually entering in items.
It tends to work fine, but every once in a while I get the error "Collection was modified; enumeration operation may not execute." which seems to occur at System.Collections.ArrayList.ArrayListEnumeratorSimple.MoveNext()
I have tried a few different things over the past couple of weeks and I am able to avoid it sometimes, but then I can reproduce it in a different area.
Essentially, I have three listboxes Parent, Current, Children. These have captioned images in them.
When an Image is selected the images in the Parent, Current, and Children empty and reload based on the selected image.
I've written it in vb, but I can convert it to C# if that will help. The code has been changed many times and so the latest one has a lot of commented code in it. Any Help or suggestions would be greatly appreciated. Anything to simplify it, get it to work, or performance enhancements would be great.
Imagine the code in triplicate. The code is nearly identical with the exception of names.
<Page.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=CurrentList}"
x:Key="CurrentList" />
</Page.Resources>
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Disabled" PanningMode="VerticalOnly" FlowDirection="RightToLeft" HorizontalScrollBarVisibility="Auto"
>
<ListBox Name="CurrentListbox" VerticalAlignment ="Stretch" Margin=" 8" Background="Transparent"
Height="Auto" BorderThickness="0" HorizontalContentAlignment="Center" FlowDirection="LeftToRight" HorizontalAlignment="Center"
ItemsSource="{Binding CurrentList}">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="ListBoxItem.Selected" Handler="CurrentListBoxItem_Selected" HandledEventsToo="false"/>
</Style>
</ListBox.Resources>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#FF0000AC" Style="{StaticResource ResourceKey=ThumbnailBorder}">
<!--<Viewbox MaxWidth ="100" >-->
<StackPanel>
<Grid>
<Border Name="ItemBorder" Style="{StaticResource ResourceKey=ThumbnailInnerBorder}"/>
<Image Name="PersonImage" MaxHeight ="200" MaxWidth ="200" Source="{Binding ProfileImagePath}"
HorizontalAlignment="Center" >
<Image.OpacityMask>
<VisualBrush Visual="{Binding ElementName=ItemBorder}"/>
</Image.OpacityMask>
</Image>
</Grid>
<Viewbox MaxWidth ="190" Margin="5,0,5,0" MaxHeight="15">
<TextBlock Name="Person" Text="{Binding ProfileName}" Tag="{Binding UserID}" HorizontalAlignment="Center" />
</Viewbox>
</StackPanel>
<!--</Viewbox>-->
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
The Collection is stored in a global Variable and populated using a SQlCe Command.
Public Shared CurrentPeopleList As New CollectionViewSource
Public Shared Current_People_List As New ObservableCollection(Of Currents)()
Public Shared Property CurrentList() As ObservableCollection(Of Currents)
Get
Return Current_People_List
End Get
Set(ByVal value As ObservableCollection(Of Currents))
Current_People_List = value
End Set
End Property
The Custom Class of Currents:
Public Class Currents
Implements INotifyPropertyChanged
Private ProfileNameValue As String
Private UserIDValue As Integer
Private ProfileImagePathValue As String
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
#Region "Properties Getters and Setters"
Public Property ProfileName() As String
Get
Return Me.ProfileNameValue
End Get
Set(ByVal value As String)
Me.ProfileNameValue = value
OnPropertyChanged("ProfileName")
End Set
End Property
Public Property UserID() As Integer
Get
Return Me.UserIDValue
End Get
Set(ByVal value As Integer)
If value < 0 Then
Throw New ArgumentException("User ID must be greater than 0 ")
End If
Me.UserIDValue = value
OnPropertyChanged("UserID")
End Set
End Property
Public Property ProfileImagePath() As String
Get
Return Me.ProfileImagePathValue
End Get
Set(ByVal value As String)
Me.ProfileImagePathValue = RelativeProgramPath() & "Media\Pictures\" & value
OnPropertyChanged("ProfileImagePath")
End Set
End Property
#End Region
Public Sub New(ByVal UserID As Integer, ByVal ProfileName As String, ByVal ProfileImagePath As String)
Me.ProfileNameValue = ProfileName
Me.UserIDValue = UserID
Me.ProfileImagePathValue = If(ProfileImagePath Like "pack://*", ProfileImagePath, RelativeProgramPath() & "Media\Pictures\" & ProfileImagePath)
End Sub
Protected Sub OnPropertyChanged(ByVal name As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
End Sub
End Class
Finally, the way these are populated is by using a common function
Private Sub Reload(ByVal CurrentID As Integer)
Try
Try
ParentList = Nothing
CurrentList = Nothing
ChildrenList = Nothing
Catch
End Try
Try
Me.ParentListbox.ItemsSource = Nothing
Me.CurrentListbox.ItemsSource = Nothing
Me.ChildListbox.ItemsSource = Nothing
Catch
End Try
Try
CurrentPersonID = CurrentID
'Load the Images Based on the people.
Dim Ch = Load_Children_People(CurrentID)
Dim C = Load_Current_People(CurrentID)
Dim P = Load_Parent_People(CurrentID)
ParentList = If(P.Count > 0, P, Nothing)
CurrentList = If(C.Count > 0, C, Nothing)
ChildrenList = If(Ch.Count > 0, Ch, Nothing)
Catch
End Try
Try
If ParentList IsNot Nothing Then _
Me.ParentListbox.ItemsSource = CollectionViewSource.GetDefaultView(ParentList)
If CurrentList IsNot Nothing Then _
Me.CurrentListbox.ItemsSource = CollectionViewSource.GetDefaultView(CurrentList)
If ChildrenList IsNot Nothing Then _
Me.ChildListbox.ItemsSource = CollectionViewSource.GetDefaultView(ChildrenList)
Catch
End Try
Catch
End Try
'For some reason this will not work without a pause. Not sure why.
'FIXME Remove Pause Delay When able.
'Thread.Sleep(200)
End Sub
I used a lot of try-catch's to attempt to catch the error, but have been unsuccessful thus far. My suspicion is that the error occurs during a UI thread that I cannot seem to pin down. As I mentioned earlier. any assistance would be great.

Binding list to Dropdown in SIlverlight not working

I am using Silverlight Application in that I am using Datagrid and binding Data based on Observable Collection, but when I am trying to Bind the Observable Collection to Dropdown it's not binding, do we need to write code to Bind in the xaml Code behind.
My Code :
<sdk:DataGridTemplateColumn Header="lab Validated?" CanUserSort="True">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Height="Auto" HorizontalAlignment="Left"
Name="cboLabValidated" VerticalAlignment="Center" Width="80"
ItemsSource="{Binding Path=LabValidatedList}">
</ComboBox>
</Grid>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
ViewModel :
Public LabValidatedList As New List(Of String)() From { _
"Yes", _
"No"
}
In order to be usable as the source of a binding, LabValidatedList has to be a public property, not a field:
Private labValidatedListValue As New List(Of String)() From { _
"Yes", _
"No"
}
Public Property LabValidatedList() As List(Of String)
Get
Return Me.labValidatedListValue
End Get
Set(ByVal value As List(Of String))
Me.labValidatedListValue = value
End Set
End Property
Sorry if the above does not compile immediately, but VB is not my language. Note also that a List(Ofd String) is not an ObservableCollection.

An ItemsControl is inconsistent with its items source - happening in nested ListView

This given page shows all cities for a particular state. I am displaying a ListView within a ListView. The outer ListView shows a listing of cities. The inner ListView shows all notes attached to each particular city.
Everything loads up properly. I can add a note, which gets added to the database. However, after adding a note, clicking the scroll bar in the NotesListView causes the exception:
An ItemsControl is inconsistent with its items source
I understand the problem...the listing of notes attached to the ListView becomes out of sync, after I add a note to the city.Notes property in the viewmodel... But how do I force this ListView to refresh?
Here is my XAML (edited for brevity):
<ListView x:Name="CityListView" ItemsSource="{Binding CityDetails, Mode=OneWay}">
<!--City Name and other city details. go here-->
<ListView x:Name="NotesListView" ItemsSource="{Binding Notes}">
<TextBlock Text="{Binding Path=Note}" />
</ListView>
<StackPanel>
<TextBlock Text="Add Note:" />
<TextBox Text="{Binding Path=DataContext.Note, RelativeSource={RelativeSource AncestorType=UserControl}}" />
<Button Content="ADD NOTE" Command="{Binding Path=DataContext.AddNoteCommand, RelativeSource={RelativeSource AncestorType=UserControl}}" CommandParameter="{Binding}" />
</StackPanel>
</ListView>
Here is the AddNote() method in the ViewModel that is hooked into the AddNoteCommand:
Protected _stateService As IStateService
Protected _state As State
Protected Sub OnAddNote(city As City)
Dim note As Note = Nothing
If Not String.IsNullOrWhiteSpace(Me.Note) Then
note = New Note() With {
.Note = Me.Note,
.NoteDate = DateTime.Now,
}
city.Notes.Add(note)
_stateService.SaveExistingState(_state)
' this saves the note, since _state contains:
' Property Cities As ICollection(Of City)
' and the city object passed into this method belongs to that collection...
RaisePropertyChanged(Function() city.Notes)
End If
End Sub
In C# I solved this with:
Dispatcher.CurrentDispatcher.Invoke(
new Action<Note>(item => city.Notes.Add(item)),
DispatcherPriority.Background,
note);

wpf binding to string

I have a Movie class with a Dim _characters = New ObservableCollection(of String)
Characters is the associated property to get and set
How can i get characters to show up in the listBox using Binding?
So far i have the following, this isn't working as i don't know what to put instead of ToString.
<ListBox Name="cList" ItemsSource="{Binding Characters}">
<ItemsControl >
<ItemsControl.ItemTemplate >
<DataTemplate >
<TextBox Text="{Binding ToString}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
I want them to be editable, hence a textbox.
i tried to bind Characters to TextBox directly, even that didn't work.
Edit :
in the code i have parentGrid1.DataContext = me.movies where
parent grid holds movies.
For those who are experiencing the exception
... binding requires path or xpath ...
You can bind the object directly this way:
<Label Content="{Binding .}" />
Change your TextBox binding to the following. I think it should work:
<TextBox Text="{Binding}"/>
This loads the item itself instead of a property or method output. Since the item is a string it should bind to the strings value.
You cannot perform two-way binding to ObservableCollection<string>. In order to make the strings editable you have to create a class with a string get/set property as the following class Foo:
public class Foo
{
string _text;
public Foo(string text)
{
_text = text;
}
public string Text
{
get { return _text; }
set { _text = value; }
}
}
Your Characters should then be of type ObservableCollection<Foo> and your XAML should be changed so that the textboxes are binding to Foo.Text:
<ListBox ItemsSource="{Binding Characters}" >
<ListBox.ItemTemplate >
<DataTemplate >
<TextBox Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Just remove the ToString portion of the code.
Currently you are telling the program that you want to bind to an object called ToString
I take it that Characters is a public property. Debug and be sure that get is being called for Characters. If you have a the datacontext of the page/window to Movies then you need ItemsSource on the ListBox to be {Binding Path=Characters}

wpf two way binding not working

i have
<Grid Name="thisPage">
<TextBlock Name="tbtb" />
<ScrollViewer Name="sv4" Visibility="Hidden">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
in the MainWindow.vb, i have
movieArray as ObservableCollection(of Movie)
For i As Integer = 0 To 5
Me.movieArray.Add(New Movie(i))
Next
Me.sv4.DataContext = Me.movieArray
Me.listBox5.DataContext = Me.movieArray
Private Sub TextBox_TextChanged(sender As System.Object, e As System.Windows.Controls.TextChangedEventArgs)
Me.tbtb.Text = ""
For Each m As Movie In movieArray
Me.tbtb.Text += p.Title.ToString + " ^ "
Next
End Sub
Class Movie
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Property Title As Integer
Get
Return Me._title
End Get
Set(value As Integer)
Me._title = value
If Not (value = _title) Then
Me._title= value
NotifyPropertyChanged("Title")
End If
End Set
End Property
for the next page i have,
<Grid Name="nextPage" Visibility="Hidden" >
<ListBox Name="listBox5" >
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
</Grid >
To change pages i just toggle the visibility of thisPage and nextPage using back, next buttons.
IM not sure what im doing wrong as:-
listbox5 shows only the original values, not anything changed by
textboxes.
tbtb, however is able to update its values
I think the problem might be your 'Title' property setter.
I'm a C# guy, not a VB expert... but it would appear that NotifyPropertyChanged will never get called.
value = _title will always be true because you just set Me._title = value in the previous line of code. Thus you will never execute any of the code in your if statement.
Why are you using Textchanged evetn in two way binding you dont need kind of stuff. two way binding is directly bind values from your view to property and from property to view
so don't use textchanged event and try again. this will work.

Resources