WPF Radgridview with ObservableCollection and INotifyPropertyChanged - wpf

Despite the many online tutorials, I still do not quite understand how this is supposed to work. I have a radgridview where the user can enter an address per row. I want them to enter the zip code first. The city and state will be supplied from a database matching the zip code. So the radgridview must be updated as soon as the zip code is entered.
It appears that using INotifyProperty is the way to go. I can get the collection to update but cannot get the datagrid to update.
View Model:
Public Class ShipTo
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
Public Property shipAddressType As String = String.Empty
Public Property orderID As String = String.Empty
Public Property shipName As String = String.Empty
Public Property shipAddr1 As String = String.Empty
Public Property shipAddr2 As String = String.Empty
Private _shipCity As String
Public Property shipCity As String
Get
Return _shipCity
End Get
Set(value As String)
_shipCity = value
NotifyPropertyChanged("shipZip")
End Set
End Property
Private _shipState As String
Public Property shipState As String
Get
Return _shipState
End Get
Set(value As String)
_shipState = value
NotifyPropertyChanged("shipZip")
End Set
End Property
Public Property shipZip As String = String.Empty
Code to find City and State:
Dim shipCS As New ShipTo
shipCS.shipCity = GetCityAndState.CityLookup(CStr(CType(dgShippingAddresses.SelectedItem, ShipTo).shipZip))
shipCS.shipState = GetCityAndState.StateLookup(CStr(CType(dgShippingAddresses.SelectedItem, ShipTo).shipZip))
XAML:
Any help appreciated. Thanks.

I finally got this to work well. For anyone else who is struggling here is my solution:
Private _shipSelected As Boolean
Public Property shipSelected As Boolean
Get
Return _shipSelected
End Get
Set(value As Boolean)
_shipSelected = value
NotifyPropertyChanged("shipSelected")
End Set
End Property
Public Property shipAddressType As String = String.Empty
Public Property orderID As String = String.Empty
Public Property shipName As String = String.Empty
Public Property shipAddr1 As String = String.Empty
Public Property shipAddr2 As String = String.Empty
Private _shipCity As String
Public Property shipCity As String
Get
Return _shipCity
End Get
Set(value As String)
_shipCity = value
NotifyPropertyChanged("shipCity")
End Set
End Property
Private _shipState As String
Public Property shipState As String
Get
Return _shipState
End Get
Set(value As String)
_shipState = value
NotifyPropertyChanged("shipState")
End Set
End Property
Private _shipZip As String
Public Property shipZip As String
Get
Return _shipZip
End Get
Set(value As String)
_shipZip = value
shipCity = GetCityAndState.CityLookup(_shipZip)
shipState = GetCityAndState.StateLookup(_shipZip)
NotifyPropertyChanged("shipZip")
End Set
End Property
Now when the zip is entered in the datagrid, the city and state automatically update.

Related

Why is my data binding not working? (WPF/vb.net)

I want to bind each property of my class "Artikelstammdaten" to a textbox, but my textbox stays empty. This is what the class looks like:
Public Class Artikelstammdaten
Private _Artikel As String
Private _BezeichnungDE As String
Private _BezeichnungEN As String
Private _Einheit As String
Private _MatGrp As String
Private _Kostenart As Integer
Private _Vertriebstext_DE As String
Private _Vertriebstext_EN As String
Private _Stuecklistennummer As String
Private _Status As String
Private _Klasse As String
Private _Mantelflaeche As Double
Private _Gewicht As Double
Private _KlasseID As String
Private _Stueckliste As IList(Of Stueckliste)
Private _Arbeitsgaenge As IList(Of Arbeitsgaenge)
Public Property Artikel As String
Get
Return _Artikel
End Get
Set
_Artikel = Value
End Set
End Property
Public Property BezeichnungDE As String
Get
Return _BezeichnungDE
End Get
Set
_BezeichnungDE = Value
End Set
End Property
Public Property BezeichnungEN As String
Get
Return _BezeichnungEN
End Get
Set
_BezeichnungEN = Value
End Set
End Property
Public Property Einheit As String
Get
Return _Einheit
End Get
Set
_Einheit = Value
End Set
End Property
Public Property MatGrp As String
Get
Return _MatGrp
End Get
Set
_MatGrp = Value
End Set
End Property
Public Property Kostenart As Integer
Get
Return _Kostenart
End Get
Set
_Kostenart = Value
End Set
End Property
Public Property Vertriebstext_DE As String
Get
Return _Vertriebstext_DE
End Get
Set
_Vertriebstext_DE = Value
End Set
End Property
Public Property Vertriebstext_EN As String
Get
Return _Vertriebstext_EN
End Get
Set
_Vertriebstext_EN = Value
End Set
End Property
Public Property Stuecklistennummer As String
Get
Return _Stuecklistennummer
End Get
Set
_Stuecklistennummer = Value
End Set
End Property
Public Property Status As String
Get
Return _Status
End Get
Set
_Status = Value
End Set
End Property
Public Property Klasse As String
Get
Return _Klasse
End Get
Set
_Klasse = Value
End Set
End Property
Public Property Mantelflaeche As Double
Get
Return _Mantelflaeche
End Get
Set
_Mantelflaeche = Value
End Set
End Property
Public Property Gewicht As Double
Get
Return _Gewicht
End Get
Set
_Gewicht = Value
End Set
End Property
Public Property KlasseID As String
Get
Return _KlasseID
End Get
Set
_KlasseID = Value
End Set
End Property
Public Property Stueckliste As IList(Of Stueckliste)
Get
Return _Stueckliste
End Get
Set
_Stueckliste = Value
End Set
End Property
Public Property Arbeitsgaenge As IList(Of Arbeitsgaenge)
Get
Return _Arbeitsgaenge
End Get
Set
_Arbeitsgaenge = Value
End Set
End Property
End Class
I'm using a ViewModel where i implement the INotifyChanged Interface:
Public Class ArtikelstammdatenViewModel
Implements INotifyPropertyChanged
Private _ArtikelstammdatenList As List(Of Artikelstammdaten)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Property ArtikelstammdatenList As List(Of Artikelstammdaten)
Get
Return _ArtikelstammdatenList
End Get
Set
_ArtikelstammdatenList = Value
NotifyPropertyChanged("ArtikelstammdatenList")
End Set
End Property
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
End Class
I'm initializing an object in the MainWindow and fill it with data, which should bind to "EditArtikelstammdaten.xaml"
Dim asd As New Artikelstammdaten
With asd
.Artikel = "VAUBEF0010"
.BezeichnungDE = "Sammelbandantrieb"
.BezeichnungEN = "Collection Belt Drive N50"
.Einheit = "STK"
.MatGrp = "VAU"
.Kostenart = 1500
.Vertriebstext_DE = "Antrieb Umlenkungen"
.Vertriebstext_EN = "Drive, Deflections"
.Stuecklistennummer = "VAUBEF0010"
.Status = "F"
.Klasse = "VTPIMV"
.Mantelflaeche = 1.3
.Gewicht = 120
.KlasseID = "1.2.6.5"
End With
I used "Artikelstammdaten" as DataContext and finally bind each Property to the text Properites.
<Window.DataContext>
<local:Artikelstammdaten></local:Artikelstammdaten>
</Window.DataContext>
<TextBox x:Name="txtItem"
Text="{Binding Artikel}"
Grid.Column="2"
Grid.Row="1"
Margin="0, 5, 0, 8"
FontSize="13"
></TextBox>
What am i missing or doing wrong? Thank you for your time!

Linking IDataErrorInfo to an existing data model class in a seperate class

I have an existing data model class that cannot be changed, in this example it is named customer. I now need to add data handling using IDataErrorInfo in a different class but cannot seem to work out how to join the 2 classes. What am I doing wrong?
Public Class Customer
Implements INotifyPropertyChanged
Private _firstName As String
Public Property FirstName As String
Get
Return _firstName
End Get
Set(value As String)
_firstName = value
OnPropertyChanged("FirstName")
End Set
End Property
My Error handler looks like this:
Public Class CustomerValidation
Inherits Customer
Implements INotifyPropertyChanged, IDataErrorInfo
Default Public ReadOnly Property Item(columnName As String) As String Implements IDataErrorInfo.Item
Get
Dim errorMessage As String = String.Empty
Select Case columnName
Case "FirstName"
If Customer IsNot Nothing AndAlso String.IsNullOrEmpty(Customer.FirstName) Then
errorMessage = "First Name Must Be At Least 1 Character"
End If
End Select
Return errorMessage
End Get
End Property
Public ReadOnly Property [Error] As String Implements IDataErrorInfo.Error
Get
Return String.Empty
End Get
End Property
I then declare and bind to it like this:
Private _customer As CustomerValidation = New CustomerValidation
Public Sub New()
InitializeComponent()
_customer.FirstName = "Steve"
Me.DataContext = _customer

Databinding breaks after reloading DBcontext

I have the following ViewModel, bound to a DataGrid in my view using ItemsSource="{Binding Path=ItemsViewSource.View}":
Public Class DataTableViewModel
Inherits ViewModelBase
Private _dataContext As DbContext
Private _items As IList
Private _itemsType As Type
Private _contextType As Type
Private _itemsViewSource As CollectionViewSource
Private _itemsView As ListCollectionView
'Commands
Private _refreshTableCommand As ICommand
Public ReadOnly Property RefreshTableCommand As ICommand
Get
If _refreshTableCommand Is Nothing Then
_refreshTableCommand = New RelayCommand(AddressOf ExecuteRefreshTable, AddressOf CanExecuteRefreshTable)
End If
Return _refreshTableCommand
End Get
End Property
Private Function CanExecuteRefreshTable() As Boolean
'This command can always run.
Return True
End Function
Private Sub ExecuteRefreshTable()
Me.RefreshTable()
End Sub
Public Property Items As IList
Get
Return Me._items
End Get
Set(value As IList)
_items = value
MyBase.OnPropertyChanged("Items")
End Set
End Property
Public Property ItemsViewSource As CollectionViewSource
Get
Return Me._itemsViewSource
End Get
Set(value As CollectionViewSource)
Me._itemsViewSource = value
MyBase.OnPropertyChanged("ItemsViewSource")
End Set
End Property
Public Property ItemsView As ListCollectionView
Get
Return Me._itemsView
End Get
Set(value As ListCollectionView)
Me._itemsView = value
MyBase.OnPropertyChanged("ItemsView")
End Set
End Property
Public ReadOnly Property DataContext As DbContext
Get
Return Me._dataContext
End Get
End Property
Public ReadOnly Overrides Property IsDirty As Boolean
Get
Return Me._dataContext.ChangeTracker.HasChanges
End Get
End Property
Private Sub RefreshTable()
NewDataContext()
End Sub
Private Sub NewDataContext()
If Me._dataContext IsNot Nothing Then
Me._dataContext.Dispose()
End If
Me._dataContext = Activator.CreateInstance(_contextType)
Me._dataContext.Set(_itemsType).Load
Me.Items = _dataContext.Set(_itemsType).Local
End Sub
Public Sub New(displayName As String, contextType As Type, recordType As Type)
MyBase.DisplayName = displayName
Me._itemsType = recordType
Me._contextType = contextType
NewDataContext()
Me._itemsViewSource = New CollectionViewSource
Me.ItemsViewSource.Source = Me.Items
Me.ItemsView = CType(Me.ItemsViewSource.View, ListCollectionView)
Me.ItemsView.Filter = New Predicate(Of Object)(AddressOf FilterByString)
End Sub
End Class
The code functions perfectly until the RefreshTable() method is called at which point the databinding seems to break. Any new data from the database is loaded into the ViewModel and is available in the Items list, however the new data is not shown in the datagrid and any changes made by the user in the DataGrid are not reflected by the DBContext.ChangeTracker.
What am I missing?
Edit:
Upon further testing, the problem seems to be somehow related to the CollectionViewSource; if I bind directly to the Items property, updated records are properly shown in the DataGrid.
Apparently I've been staring at the screen too long.
The CollectionViewSource needs to be rebuilt as well and the DataGrid notified via OnPropertyChanged("ItemsViewSource").
Fix:
Private Sub RefreshTable()
NewDataContext()
Me._itemsViewSource = New CollectionViewSource
Me.ItemsViewSource.Source = Me.Items
Me.ItemsView = CType(Me.ItemsViewSource.View, ListCollectionView)
Me.ItemsView.Filter = New Predicate(Of Object)(AddressOf FilterByString)
'Let WPF know the data changed in the ViewModel
OnPropertyChanged("ItemsViewSource")
End Sub

Error while checking row, after Filtering observational collection

I'm having trouble with editing an observable collection... I bind columns in codebehind like so...
Dim dgCheckBoxColumn As New DataGridCheckBoxColumn()
Dim column_username As New DataGridTextColumn()
Dim textColumn2 As New DataGridTextColumn()
Dim textColumn3 As New DataGridTextColumn()
dgCheckBoxColumn.Header = "Selected"
dgCheckBoxColumn.Binding = New Binding("Selected")
dgvResults.Columns.Add(dgCheckBoxColumn)
column_username.Header = "User Name"
column_username.Binding = New Binding("accountName")
dgvResults.Columns.Add(column_username)
textColumn2.Header = "First Name"
textColumn2.Binding = New System.Windows.Data.Binding("firstName")
dgvResults.Columns.Add(textColumn2)
textColumn3.Header = "Last Name"
textColumn3.Binding = New System.Windows.Data.Binding("lastName")
dgvResults.Columns.Add(textColumn3)
Then I create my observablecollection...
Dim oc_userlist As New ObservableCollection(Of user)
Imports System.ComponentModel
Public Class user
Implements INotifyPropertyChanged
Private m_accountname As String
Private m_firstname As String
Private m_lastname As String
Private _Selected As Boolean
Public Property Selected() As Boolean
Get
Return _Selected
End Get
Set(value As Boolean)
_Selected = value
NotifyPropertyChanged("IsChecked")
End Set
End Property
Public Property accountName() As String
Get
Return m_accountname
End Get
Set(value As String)
m_accountname = value
End Set
End Property
Public Property firstName() As String
Get
Return m_firstname
End Get
Set(value As String)
m_firstname = value
End Set
End Property
Public Property lastName() As String
Get
Return m_lastname
End Get
Set(value As String)
m_lastname = value
End Set
End Property
#Region "INotifyPropertyChanged Members"
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
#End Region
#Region "Private Helpers"
Private Sub NotifyPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
#End Region
End Class
Then I add new user to collection, like so...
oc_userlist.Add(New user With { _
.Selected = False, _
.accountName = "awiles", _
.firstName = "Anthony", _
.lastName = "Wiles"
})
I'm trying to filter using a textbox, with the textchanged event, and it's working wonderfully...
Private Sub TextBox_TextChanged(sender As Object, e As TextChangedEventArgs)
Dim result = oc_userlist.Where(Function(w) w.accountName.Contains(txtFilter.Text.ToString) _
Or w.firstName.Contains(txtFilter.Text.ToString) _
Or w.lastName.Contains(txtFilter.Text.ToString))
dgvResults.ItemsSource = result
End Sub
The problem I'm having is once this is filtered, using the textbox, then I try to select something, it tells me
'EditItem' is not allowed for this view.
I'm having issues trying to get around this issue, can anybody point me in the correct direction?
The reason could be the result of your Linq query (with the Where) is not a List(Of T) but an IEnumerable.
Try adding a call to ToList()
Dim result = oc_userlist.Where(Function(w) w.accountName.Contains(txtFilter.Text.ToString) _
Or w.firstName.Contains(txtFilter.Text.ToString) _
Or w.lastName.Contains(txtFilter.Text.ToString)) _
.ToList()
Then tell if it works

Combobox and IDataErrorInfo

This is my problem.
When I select an item from the combobox, (combobox is populated) I have always red outline.
Why ?
<ComboBox x:Name="Cmb_USER"
TabIndex="5"
ItemsSource="{Binding User_USER,
ValidatesOnExceptions=True,
ValidatesOnDataErrors=True,NotifyOnValidationError=True}"
IsEditable="True"
DisplayMemberPath ="DescUser"
Text="{Binding Path=USER}" Margin="114,105,98,179"/>
Public Property User_USER As ObservableCollection(Of Model_User)
Private p_USER As String
Public Property USER() As String
Get
Return p_USER
End Get
Set(ByVal value As String)
p_USER = value
OnPropertyChanged("USER")
End Set
End Property
#Region "IDataErrorInfo Members"
Public ReadOnly Property [Error]() As String Implements System.ComponentModel.IDataErrorInfo.Error
Get
Return Nothing
End Get
End Property
Default Public ReadOnly Property Item(ByVal Name As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
Get
Dim result As String = Nothing
Select Case Name
Case "User_USER"
If USER = "" Then
result = "ERROR"
End If
End Select
Return result
End Get
End Property
#End Region
Solved ..
This is the solution
Public Property USER() As String
Get
Return p_USER
End Get
Set(ByVal value As String)
p_USER = value
OnPropertyChanged("USER")
OnPropertyChanged("User_USER")
End Set
End Property

Resources