WPF binding with observerableCollection doesnt work - wpf

I use for my WPF project a binding of a table (db) to a listview. But if i start my project, the listview is empty. Iam using linq to get the data of my entity-framework and address has definitly the right string in it.
Is my binding is wrong and how to fix it?
xaml
<ListView ItemsSource="{Binding Items}" x:Name="lstvw_Overview" >
<ListView.View>
<GridView>
<GridViewColumn Header="Adresse"
DisplayMemberBinding="{Binding structureAddress}"/>
</GridView>
</ListView.View>
</ListView>
code
Iam pretty sure that my table is filled
Public Sub New()
Initialize()
End Sub
Dim address As String
Public Structure Uebersicht
Dim structureAddress As String
Shared _items As ObservableCollection(Of Uebersicht) = New ObservableCollection(Of Uebersicht)
Public Property Items As ObservableCollection(Of Uebersicht)
Get
Return _items
End Get
Set(value As ObservableCollection(Of Uebersicht))
_items = value
End Set
End Property
End Structure
Sub Initialize()
InitializeComponent()
DataContext = Me
fillListView()
End Sub
Sub fillListView()
Using container As New infrastrukturDB_TESTEntities1
Dim mailAddressList = From tbl_unzustellbarAdressen In container.tbl_unzustellbarAdressen
For Each mail In mailAddressList
address = mail.unzustellbarMail.ToString()
Try
Uebersicht._items.Add(New Uebersicht With {.structureAddress = address})
Catch ex As Exception
MessageBox.Show("error")
End Try
Next
End Using
End Sub

For WPF bindings, always provide a Property definition, fields are not supported by the Binding class. So you need to turn structureAddress into a property in order to make it work.
Since you set your Window (?) class instance as its own DataContext and try to bind ItemsSource="{Binding Items}", your window class needs to contain a Property named Items with some collection type (eg ObservableCollection). So move your collection from within Structure Uebersicht to the outer Window class and don't use Shared on the backing field.
Note you don't really need the property setter on Items since you initialize _items once and then you only ever modify the contained items but not the collection reference itself.

Related

Cascading Comboboxes WPF mvvm and EF

I'm trying to create cascading comboboxes on my view. I have a table, imported from sql with ADO.NET Entity Data Model:
Partial Public Class tbl_Activities
Public Property MRU As String
Public Property Market As String
Public Property Tower As String
Public Property FirstActivity As String
Public Property SecondActivity As String
First combobox should contain FirstActivity items and the second one SecondActivity items related to the first one.
I'm using method to populate the first one:
Public Sub FillFirstActivity()
Using context As New TimerConn
Dim result = (From act In context.tbl_Activities Where (act.MRU = MRU() And act.Market = MAR() And act.Tower = TOW()) Order By act.FirstActivity).Distinct().ToList()
For Each act In result
_First.Add(act)
Next
End Using
End Sub
Properties:
Public Property First() As ObservableCollection(Of tbl_Activities)
Get
Return _First
End Get
Set(value As ObservableCollection(Of tbl_Activities))
_First = value
End Set
End Property
Public Property Second() As ObservableCollection(Of tbl_Activities)
Get
Return _Second
End Get
Set(value As ObservableCollection(Of tbl_Activities))
_Second = value
End Set
End Property
Public Property SelectedFirst() As tbl_Activities
Get
Return _selectedFirst
End Get
Set(value As tbl_Activities)
_selectedFirst = value
Me.Second.Clear()
Me.Second.Add(_selectedFirst)
End Set
End Property
XAML:
<ComboBox ItemsSource="{Binding EmployeeViewM.First}" SelectedIndex="{Binding EmployeeViewM.SelectedFirst}" DisplayMemberPath="FirstActivity"/>
<ComboBox ItemsSource="{Binding EmployeeViewM.Second}" DisplayMemberPath="SecondActivity"/>
My problem
I'm not able to populate it correctly. In the first combobox I have doubles (Test1, Test2, Test2), and in the second combobox I get only item related to the item chosen from the first combobox:
First one should be distinct and the second one with all items for the current activity.
First Combobox = 'Test1', 'Test2'
Second Combobox = If 'Test1' then 'Test1Add', If 'Test2' then 'Test2Add', 'Test2Add1'
I don't know what I'm doing wrong here. Maybe I should split it into Two tables with PK - FK and then try? If so, how can I bind it to the comboboxes?
My Table view:
Thank you very much for any suggestion.

How to show list values in a datagrid?

This might be a newbie question.
I want to show some information in a DataGrid. This does not have to be two-way binding, I only need to show the DataGrid as an output. The information is stored in a List that consists of List(Of String) items. The List(Of String) item being an entire row, where each String item within that list would go into its own column in the DataGrid.
So what would be the easiest way to do this?
I tried to do something like this:
Created the DataGrid:
<DataGrid x:Name="MyDataGrid" AutoGenerateColumns="True" ItemsSource="{Binding}" Margin="24,17,144,37">
</DataGrid>
Created the "row" entry by adding some data to the first list:
Dim MyEntryList As New List(Of String)
MyEntryList.Add("SomeName")
MyEntryList.Add("SomeInfo")
MyEntryList.Add("SomeStuff")
Added the created "row" list to the encompassing list:
Dim MyDataList As New List(Of List(Of String))
MyDataList.Add(MyEntryList)
And finally tried to set the source of my DataGrid:
Me.MyDataGrid.ItemsSource = MyDataList
Instead of those strings, the DataGrid shows some numbers describing Capacity and Count:
How can I get it to show the contents of the lists?
You should try to build some objects or Datatable to populate the Datagrid. Refer the code below
<DataGrid x:Name="MyDataGrid" AutoGenerateColumns="True" >
</DataGrid>
Imports System.Collections.ObjectModel
Class MainWindow
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs)
Dim lst As New ObservableCollection(Of SomeObject)
For index = 1 To 10
Dim obj As New SomeObject
obj.SomeName = "test" + index.ToString
obj.SomeInfo = "info" + index.ToString
lst.Add(obj)
Next
MyDataGrid.ItemsSource = lst
End Sub
End Class
Class SomeObject
Private name As String
Public Property SomeName() As String
Get
Return name
End Get
Set(ByVal value As String)
name = value
End Set
End Property
Private info As String
Public Property SomeInfo() As String
Get
Return info
End Get
Set(ByVal value As String)
info = value
End Set
End Property
End Class

Adding Items to a DataGrid (MVVM)

Goal
To add a list of a custom class object (DamagedItems) to a DataGrid using the Model, View, ViewModel (MVVM) way of doing things.
I want the user to be able to create entries of damaged parts (deemed improper during inspection of a machine).
What I have done
I have created:
A window: wDamagedItems.xaml in which it's DataContext is set to DamagedItemViewModel
A Model: DamagedItemModel.vb which implements INotifyPropertyChanged
A ViewModel: DamagedItemViewModel.vb where I set properties of classes such as my DamagedItemModel
An ObservableCollection: DamagedItemList.vb which inherits an ObservableCollection(Of DamagedItemModel)
Since my DataContext is set to the DamagedItemViewModel, here is how I setup the properties:
Public Class DamagedItemViewModel
Private _DamagedItem As DamagedItemModel
Private _Add As ICommand
Private _DamagedItems As DamagedItemList
Public Property DamagedItem As DamagedItemModel
Get
Return _DamagedItem
End Get
Set(value As DamagedItemModel)
_DamagedItem = value
End Set
End Property
Public Property DamagedItems As DamagedItemList
Get
Return _DamagedItems
End Get
Set(value As DamagedItemList)
_DamagedItems = value
End Set
End Property
Public Property Add As ICommand
Get
Return _Add
End Get
Set(value As ICommand)
_Add = value
End Set
End Property
Public Sub New()
DamagedItem = New DamagedItemModel("", "", "")
DamagedItems = New DamagedItemList
Add = New DamagedItemAddEntryCommand(Me)
End Sub
Public Function CanUpdate() As Boolean
If DamagedItem.Description = "" Then Return False
If DamagedItem.Initiales = "" Then Return False
Return True
End Function
Public Sub AddEntry()
DamagedItems.Add(DamagedItem) 'Items get added to the datagrid
DamagedItem = New DamagedItemModel 'Does not seem to clear textboxes
End Sub
End Class
Here is how my XAML is set up:
<DataGrid ItemsSource="{Binding Path=DamagedItems}" AutoGenerateColumns="True" HorizontalAlignment="Stretch" Margin="12,90,12,0" Name="DataGrid1" VerticalAlignment="Top" Height="229" / >
<TextBox Text="{Binding DamagedItem.Description, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,24,0,0" VerticalAlignment="Top" Width="249" />
<TextBox Text="{Binding DamagedItem.Initiales, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="88,58,0,0" VerticalAlignment="Top" Width="249" />
As you can see, my textboxes are bound to my Model (which is contained in my ViewModel, which is bound to that Window's DataContext). Whenever I click on my "Add" button, whatever is in the textbox gets added to the DataGrid, but the content in the text boxes stay there.
This step is fine, I write in what I want to add and click on "Add"
After clicking on "Add" i get the following results in the DataGrid, which is fine. The issue is my text boxes are still filled with data yet the Model was cleared (see code after DamagedItemViewModel AddEntry method).
Now when I try to add the following text:
Description: "Part is bent"
Initiales: "A.C"
I get the following result:
The first letter typed in the description gets inputted in the first entry of the DataGrid, then it erases the text in the description textbox. Only then can I keep typing what I want. The same thing occurs for the initiales text box.
Any ideas? If you wish to see more of my code, suggest which portion I should add.
Thank you in advance!
Yup, I remember running into this one. You have to implement iNotifyPropertyCHnaged. This is how the viewmodel class "notifies" the user interface that there has been a change to the underlying property of a binding:
look here:
http://msdn.microsoft.com/en-us/library/ms743695.aspx
You will have to implement this for every property you want reflected back to the view. SO what I do is have a base viewmodel class (ViewModelBase which exposes method RasiePropertyChanged) which implements iNotifyPropertyChanged and then my viewmodles inherit from it. Then I notify the property changed in the property set of the property:
ie:
Public Property Selection As job
Get
Return Me._Selection
End Get
Set(ByVal value As job)
If _Selection Is value Then
Return
End If
_PreviousJob = _Selection
_Selection = value
RaisePropertyChanged(SelectionPropertyName)
End Set
End Property
This seems frustrating at first but is needed to keep the decoupling that MVVM supports. Its easy to implement.

Can't remove Listbox items from listbox

I have a listbox in a Silverlight application.
The listbox:
<ListBox Grid.Row="1" Grid.ColumnSpan="2" Name="lbHazards" Margin="5"
MinHeight="75" ItemsSource="{Binding Path=HazListByTaskIDCollection}"
DisplayMemberPath="sHaz_Name"
IsEnabled="{Binding Path=IsEnabled}" />
In the view model I have
Private _HazListByTaskIDCollection As ObservableCollection(Of vw_HazList_By_TaskID)
Public Property HazListByTaskIDCollection() As ObservableCollection(Of vw_HazList_By_TaskID)
Get
Return _HazListByTaskIDCollection
End Get
Set(ByVal value As ObservableCollection(Of vw_HazList_By_TaskID))
_HazListByTaskIDCollection = value
'Used to notify CommonBase class that a property change has occured
RaisePropertyChanged("HazListByTaskIDCollection")
End Set
End Property
Then I have a sub in the viewmodel:
Public Sub FillHazList(ByVal iHazID As Integer, ByVal sHaz_Name As String)
Try
Dim yy = New vw_HazList_By_TaskID
yy.iHazID = iHazID
yy.sHaz_Name = sHaz_Name
HazListByTaskIDCollection.Add(yy)
Catch ex As Exception
DisplayError("Error Happened", ex)
End Try
End Sub
And that works perfectly when this Sub is called the item is added to the listbox.
But I also need to be able to remove the items from the listbox
So I thought it would be easy enough so I created another sub
Public Sub RemoveHazListItem(ByVal iHazID As Integer, ByVal sHaz_Name As String)
Try
Dim yyy = New vw_HazList_By_TaskID
yyy.iHazID = iHazID
yyy.sHaz_Name = sHaz_Name
HazListByTaskIDCollection.Remove(yyy)
HazListByTaskIDCollection.Clear()
Catch ex As Exception
DisplayError("Error Happened", ex)
End Try
End Sub
This runs with no errors but it does NOT remove the item from the listbox.
What am I doing wrong?
Your RemoveHazListItem method appears to be creating a new vw_HazList_By_TaskID object, putting a couple of values in it, and attempting to remove this newly-created object from your collection. It seems you're not getting the behaviour you are expecting because you're attempting to remove from a collection an item that was never added to it.
As far as I can see, there are two solutions to your problem:
Override Equals in your vw_HazList_By_TaskID class. Doing this should allow new objects to be considered equal to existing objects in the collection, and hence you should be able to remove objects from the collection by passing to Remove an object that is equal to the one you want to remove.
Look through the collection for a vw_HazList_By_TaskID object with matching iHazID and sHaz_Name properties, and remove that object from the collection instead.
Incidentally, the Remove method of the ObservableCollection(Of T) class returns a Boolean value indicating whether it was able to remove a value from the list. In the event of the item to remove not being found, it returns False as opposed to throwing an exception.
Try like this:
HazListByTaskIDCollection.RemoveAt(HazListByTaskIDCollection.IndexOf(yyy));

ListBox ItemTemplateSelector doesn't work

I am attempting to use an ItemTemplateSelector on a WPF ListBox and have looked at several examples online. Seemed simple enough but I cannot get it to work. I am hoping that someone can tell me where I've gone wrong:
Fist, I have an DataTemplateSelector class defined as follows:
Public Class DocketDataTemplateSelector
Inherits DataTemplateSelector
Public Overrides Function SelectTemplate(ByVal item As Object, ByVal container As DependencyObject) As DataTemplate
Return DataDocketHeaderTemplate
End Function
Private _DataDocketHeaderTemplate As DataTemplate
Public Property DataDocketHeaderTemplate() As DataTemplate
Get
Return _DataDocketHeaderTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketHeaderTemplate = value
End Set
End Property
Private _DataDocketDataTemplate As DataTemplate
Public Property DataDocketDataTemplate() As DataTemplate
Get
Return _DataDocketDataTemplate
End Get
Set(ByVal value As DataTemplate)
_DataDocketDataTemplate = value
End Set
End Property
End Class
Very simple - just returns the DataDocketHeaderTemplate datatemplate for the time being until I can get it to work.
I then have my user control with the following as its resource definition:
<UserControl.Resources>
<DataTemplate x:Key="docketHeaderTemplate">
<TextBlock Text="Header Row Test" Background="Yellow"/>
</DataTemplate>
<DataTemplate x:Key="docketDataTemplate">
<TextBlock Text="Data Row Test" Background="Green"/>
</DataTemplate>
<local:DocketDataTemplateSelector DataDocketHeaderTemplate="{StaticResource docketHeaderTemplate}" DataDocketDataTemplate="{StaticResource docketDataTemplate}" x:Key="myDataTemplateSelector"/>
</UserControl.Resources>
The ListBox in the user control is simply defined like this:
<ListBox ItemsSource="{Binding TestData}" ItemTemplateSelector="{StaticResource myDataTemplateSelector}"/>
Then finally, my TestData list is defined in my bound viewmodel like so:
Private _listTestData As ObservableCollection(Of String) = Nothing
Public Property TestData As ObservableCollection(Of String)
Get
If _listTestData Is Nothing Then
_listTestData = New ObservableCollection(Of String)
_listTestData.Add("Row 1")
_listTestData.Add("Row 2")
_listTestData.Add("Row 3")
End If
Return _listTestData
End Get
Set(ByVal value As ObservableCollection(Of String))
_listTestData = value
NotifyPropertyChanged("TestData")
End Set
End Property
Now, I expect I would see a list of 3 rows in my listbox all saying 'Header Row Test' (since my datatemplateselector is always returning DataDocketHeaderTemplate). But instead I see my core data of
Row 1
Row 2
Row 3
This seems to indicate that my overriding datatemplateselector is not being hit (indeed if I set a breakpoint in DocketDataTemplateSelector, at no time do I see it being hit). Where am I going wrong with this?
Thanks
Sorry i can not post this as comment, i haven't got enough score.
I just tried your example code (my first VB project) and guess what, it works as expected: three times "Header Row Test" on yellow background. I've put the ListBox in a Grid in the UserControl, then put the UserControl in a Grid in a Window, then set the DataContext of the UserControl to a ViewModel object with your TestData property.
Something must be wrong that is not demonstrated by your example code, maybe you can provide more info.

Resources