I am trying to build a small application with a combobox and the issue is that the items are not getting updated in it properly, sometimes 2 items sometime 4 items are only getting visible however the items count is getting updated properly. Following is the xaml code:
<ComboBox Name="NumbersCombo" Width="118">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Following is the code for itemssource on page load, where con.Numbers is dictionary of string and Numbers class, hence Values are sent for attachment:
NumbersCombo.ItemsSource = con.Numbers.Values
Following is the code for adding new items to combobox:
Dim temp As New BSPLib.ContactLib.ContactCon(con.prime.Conid, False)
con.Numbers.Add(temp.ConRowID, temp)
NumbersCombo.ItemsSource = con.Numbers.Values
TestLabel1.Content = NumbersCombo.Items.Count
Following is the code for the class:
Public Class ContactCon
Property ConId As String
Property ConRowID As String
Property Title As String
Property Mob1 As String
Property Mob2 As String
Property Land1 As String
Property Land2 As String
Property Email1 As String
Property Email2 As String
Property Fax1 As String
Property Fax2 As String
Property Primary As Boolean
Public Sub New()
End Sub
Public Sub New(ByVal contactID As String, ByVal primarynumbers As Boolean)
ConId = contactID
ConRowID = contactID & "-" & Now.ToString
If primarynumbers = True Then
Title = "Primary Details"
Else
Title = "Additional Contact Numbers"
End If
Mob1 = ""
Mob2 = ""
Land1 = ""
Land2 = ""
Email1 = ""
Email2 = ""
Fax1 = ""
Fax2 = ""
Primary = primarynumbers
End Sub
End Class
Public Class Contact
Public prime As ContactPrime
Public addrs As Dictionary(Of String, ContactAddress)
Public Numbers As Dictionary(Of String, ContactCon)
Public Sub New()
Numbers = New Dictionary(Of String, ContactCon)
'assigning initial ids and values
Dim t As New ContactCon(prime.Conid, vbYes)
Numbers.Add(t.ConRowID, t) 'Primary Contact Number
End Sub
In simple, the gui of combo box is not showing the items but the items count is correct, could you please tell where I have gone wrong. Thank you.
If you want the combo box to be updated automatically when a particular ContactCon object or the collection of ContactCon objects changes then the ContactCon class needs to implement INotifyPropertyChanged and you need to set the ItemSource to a collection class that implements INotifyCollectionChanged (This means that you only have to set the ItemSource once). You can find out how to implement an observable dictionary in the answers section of the following question.
Making these changes will also fix the synchronisation problems you are seeing.
I agree with Dave +1
If you need dictionary for lookup speed or uniqueness on key then you need a dictionary that implements CollectionChanged.
The other option is to just use ObservableCollections direclty and use LINQ for lookup. If you have even 10000 LINQ is still pretty fast for lookup. Since you are not using virtualization on the combobox I suspect the list is not large.
Related
For some reason I'm adding each item in the findall function to the observable collection, and I am getting back "groupname" in my listbox.. instead of the actual name of the group.
Dim ctx As New PrincipalContext(ContextType.Domain)
Dim qbeGroup As New GroupPrincipal(ctx)
Dim srch As New PrincipalSearcher(qbeGroup)
For Each item In srch.FindAll()
GroupsList.Add(New groups With { _
.name = item.Name
})
Next
Below is my simple observablecollection
Public Property name() As String
Get
Return m_name
End Get
Set(value As String)
m_name = value
End Set
End Property
Private m_name As String
Is there something else I need to be doing to return the actual string?
Edit: Xaml
<ListBox x:Name="lbAvGroups_Copy" HorizontalAlignment="Left" Height="470" VerticalAlignment="Top" Width="355" Margin="392,35,0,0"/>
I do the binding in code-behind, so this is what my binding looks like.
Public View As ICollectionView
Public GroupsList As new ObservableCollection(Of groups)
View = CollectionViewSource.GetDefaultView(GroupsList)
lbAvGroups.ItemsSource = View
When you bind a ListBox to a list of objects it will, by default, display the value returned by calling ToString on the objects. You can either define the ItemTemplate for the ListBox, or for simple text display you can set the DisplayMemberPath to the name of the property you want to show. So, in your case you can add
lbAvGroups.DisplayMemberPath = "name"
when binding the list box to display the name property of each item.
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.
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
I'm trying to create a UserControl which consists of a DataGrid and a couple of buttons. The buttons will handle adding/deleting rows (needs to be buttons). The DataGrid is bound to a custom observable collection. The collections properties will vary (so I'm auto-generating the columns).
How can I add a new row? Normally I'd just modify the observable collection. I've tried adding a new row directly to the control:
dgMain.Items.Add(New DataGridRow())
but I get an error which doesn't mean much to me:
Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead.
Here's the current code behind:
Public Class DataGrid
Sub New()
InitializeComponent()
End Sub
#Region "Dependency Properties"
Public Shared MyItemsSourceProperty As DependencyProperty = DependencyProperty.Register("MyItemsSource", GetType(IEnumerable), GetType(DataGrid))
Public Property MyItemsSource() As IEnumerable
Get
Return DirectCast(GetValue(MyItemsSourceProperty), IEnumerable)
End Get
Set(value As IEnumerable)
SetValue(MyItemsSourceProperty, value)
End Set
End Property
#End Region
#Region "Buttons"
Private Sub btnAdd_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnAdd.Click
dgMain.Items.Add(New DataGridRow())
End Sub
#End Region
End Class
So does anyone know how I can add a new row?
Thanks for any help.
EDIT: This is how the data is created:
Dim np As New ObPerson
np.Add(New Person With {.FirstName = "Jane", .LastName = "Mitel", .Age = 18})
np.Add(New Person With {.FirstName = "Joe", .LastName = "Bloggs", .Age = 92})
UserControlInstance.MyItemsSource = np
Public Class ObPerson
Inherits ObservableCollection(Of Person)
End Class
EDIT2: VB Version of the accepted answer:
Public Shared Sub AddNewElement(l As IList)
If l Is Nothing OrElse l.Count = 0 Then
Throw New ArgumentNullException()
End If
Dim obj As Object = Activator.CreateInstance(l(0).[GetType]())
l.Add(obj)
End Sub
Usage: AddNewElement(MyItemsSource)
You need to use the collection that's bound - not the 'Items' property on the grid. ItemsSource will point to your collection that is bound:
SomeGrid.ItemsSource = SomeCollection;
SomeCollection.Add(new ItemOfTheRightType());
or
(SomeGrid.ItemsSource as SomeCollection).Add(new ItemOfTheRightType());
The error says that you can't use Grid.Items if you are binding using Grid.ItemsSource
Edit:
If you don't know the item type at runtime (maybe because this is a 3rd party using the control etc and you want a generic add method) you need to call the .Add method on the underlying interface. Most list types inherit from IList in the .NET framework
I'm no VB expert, I much prefer c# so I'll give you the c#. You need to check for the underlying type first:
in c#
if(grid.ItemsSource is IList)
{
(grid.ItemsSource as IList).Add(new childType()); <-- other issue here..
}
The problem you have though is that if you are adding a new item to the collection and you don't know the list type, IList requires an instance of the object to add to the list
solution is to use reflection:
Dynamically creating a new instance of IList's type
An interesting late answer is:
var collectionType = targetList.GetType().GetProperty("Item").PropertyType;
var constructor = collectionType.GetConstructor(Type.EmptyTypes);
var newInstance = constructor.Invoke(null);
Which might work
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));