This question is a follow-up to one I asked and got answered here: How to display XPS document using a selected combobox item
I've created a WPF app using VB 2010. I set the comboboxitems via the XAML. However, I can't seem to figure out how to set the value of each item to a file path.
The objective is for a user to be able to select an item from a drop-down list, then that selection opens an XPS file in the DocumentViewer. The code below was provided to me by COMPETENT_TECH (thanks) to read and display the value of the selected comboboxitem in the DocumentViewer.
The path to the files I want opened is C:\folder\file.xps
Private Sub Button4_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button4.Click
Try
Dim sFileName As String
sFileName = DirectCast(ComboBox1.SelectedValue, String)
If Not String.IsNullOrEmpty(sFileName) Then
Dim theDocument As New System.Windows.Xps.Packaging.XpsDocument(sFileName, System.IO.FileAccess.Read)
DocumentViewer1.Document = theDocument.GetFixedDocumentSequence()
End If
Catch ex As Exception
MessageBox.Show("ERROR: " & ex.Message)
End Try
End Sub
Thanks in advance for your assistance.
Update
Here's the XAML I'm using:
<ComboBox Width="Auto" IsReadOnly="True" IsEditable="True" Name="ComboBox1" Height="Auto" Margin="0" Padding="1" Grid.Column="2">
<ComboBoxItem>123456</ComboBoxItem>
<ComboBoxItem>123457</ComboBoxItem>
<ComboBoxItem>123458</ComboBoxItem>
</ComboBox>
Depending on precisely how the xaml to load the combobox is specified, you will probably want to change this line:
Dim theDocument As New System.Windows.Xps.Packaging.XpsDocument(sFileName, System.IO.FileAccess.Read)
to:
Dim theDocument As New System.Windows.Xps.Packaging.XpsDocument(System.IO.Path.Combine("C:\folder", sFileName & ".xps"), System.IO.FileAccess.Read)
All that we do in the new code is combine the directory where the files are stored with the file name retrieved from the combobox.
Update
The correct way to retrieve the value from the combobox is:
If ComboBox1.SelectedValue IsNot Nothing Then
sFileName = DirectCast(ComboBox1.SelectedValue, ComboBoxItem).Content.ToString()
End If
I highly recommend you use Data Binding with that ComboBox.
create a class something like following:
Class XPSDocumentInfo
{
Public Property Title As String
Public Property FileName As String
}
Create an ObservableCollection(Of XPSDocumentInfo) and bind it to ComboBox's ItemsSource.
Use DisplayMemberPath="Title" attribute on ComboBox so that it will use Title property to show text in dropdown but since you bound collection of type XPSDocumentInfo, SelectedItem property of that ComboBox with return an object of type XPSDocumentInfo.
For example,
sFileName = DirectCast(ComboBox1.SelectedValue, String)
will be changed to
sFileName = DirectCast(ComboBox1.SelectedItem, XPSDocumentInfo).FileName
Related
I'm new to LINQ to SQL and WPF and i'm using several online guides and YouTube Videos to pick this up (along with this application).
The problem:
On loading the window (and on other key data changes that refresh the Windows DataContext), I need to update what selected item the ComboBox is showing to the item with a matching ID from the Window DataContext binding (c_Instance.FK_RiskIndicator_ID)
I populate the ComboBox from a view which returns the display field and the ID (All ID's are GUIDs).
When the selection is changed, i call a Stored Procedure to update the DB with the ID of the selected item.
Private db As New AppDatabase_DBDataContext
Private CRCTypeView As BindingListCollectionView
Private InstanceDetailTypeView As BindingListCollectionView
Property guInstance_ID As Guid
Private Sub Refresh_PCA_Instance()
Dim c_Instance As IEnumerable(Of t_Instance)
c_Instance = (From Inst In db.t_Instances Where Inst.ID = guInstance_ID Select Inst)
If c_Instance.Count = 1 Then
Me.DataContext = c_Instance
Me.InstanceDetailTypeView = CType(CollectionViewSource.GetDefaultView(Me.DataContext), BindingListCollectionView)
ElseIf c_Instance.Count > 1 Then
Me.DataContext = c_Instance.First
Me.InstanceDetailTypeView = CType(CollectionViewSource.GetDefaultView(Me.DataContext), BindingListCollectionView)
End If
End Sub
Private Sub LoadCRCType()
Dim CRCTypeList As IEnumerable(Of vw_RiskIndicator)
CRCTypeList = (From RiskIndicator In db.vw_RiskIndicators
Order By RiskIndicator.IndexNum
Select RiskIndicator)
PCA_CRC_ComboBox.DataContext = CRCTypeList
Me.CRCTypeView = CType(CollectionViewSource.GetDefaultView(PCA_CRC_ComboBox.DataContext), BindingListCollectionView)
End Sub
Private Sub PCA_CRC_ComboBox_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles PCA_CRC_ComboBox.SelectionChanged
If PCA_CRC_ComboBox.SelectedItem IsNot Nothing Then
db.sp_Instance_RiskIndicator_UPSERT(guInstance_ID, PCA_CRC_ComboBox.SelectedValue)
Refresh_PCA_Instance() 'Refreshes the Window DataContext
End If
End Sub
XAML for the ComboBox:
<ComboBox x:Name="PCA_CRC_ComboBox" Grid.Row="1" Grid.Column="2"
ItemsSource="{Binding}"
SelectedValuePath="ID"
DisplayMemberPath="RiskIndicator"
PreviewKeyDown="RiskIndicator_ComboBox_PreviewKeyDown"/>
I had assumed i could do this with binding the SelectedItem, but i can't figure it out. I had assumed it would be something like:
<ComboBox x:Name="PCA_CRC_ComboBox" Grid.Row="1" Grid.Column="2"
SelectedItem="{Binding Path=FK_RiskIndicator_ID}" <!--THIS LINE HERE-->
ItemsSource="{Binding}"
SelectedValuePath="ID"
DisplayMemberPath="RiskIndicator"
PreviewKeyDown="RiskIndicator_ComboBox_PreviewKeyDown"/>
Any help with this would be much appreciated.
EDIT: Grouped the code together. I assume this is what was meant. Personally i think this layout if less explanatory.
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've got a big issue trying to modify the ItemSource for a ComboBox. When I try to debug the app by putting a breakpoint on MessageBox.Show(...) in the Sub cboTest_SelectionChanged (see below), I get this error:
An Unhandled exception of type 'System.InvalidCastException' occured in Data.exe
Additional Information:
Unable to cast object of type 'System.Data.DataRowView' to type 'System.Windows.Controls.ComboBoxItem'
How can I fix this?
Here's what I do. I create an ACCESS 2007 Database named SNIGDoFFE.accdb, my solution is named Data, I added the Database through the Add New Source Configuration wizard and I drag and drop the Field "Libelle" from the Table Ethnie to the the ComboBox, so it modifies my files like this:
In MainWindow.xaml.vb
Private Sub Window_Loaded(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
Dim SNIGDoFFEDataSet As Data.SNIGDoFFEDataSet = CType(Me.FindResource("SNIGDoFFEDataSet"), Data.SNIGDoFFEDataSet)
'Load data into the table Ethnie. You can modify this code as needed.
Dim SNIGDoFFEDataSetEthnieTableAdapter As Data.SNIGDoFFEDataSetTableAdapters.EthnieTableAdapter = New Data.SNIGDoFFEDataSetTableAdapter.EthnieTableAdapter()
SNIGDoFFEDataSetEthnieTableAdapter.Fill(SNIGDoFFEDataSet.Ethnie)
Dim EthnieViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("EthnieViewSource"), System.Windows.Data.CollectionViewSource)
'EthnieViewSource.View.MoveCurrentToFirst
'I comment this line to leave the combobox empty
End Sub
The code for cboTest_SelectionChanged is:
Private Sub cboTesT_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles cboTesT.SelectionChanged
MessageBox.Show(CType(cboTesT.SelectedItem, ComboBoxItem).Content)
End Sub
In XAML:
<ComboBox x:Name="cboTest" width="120" Margin"184,10,183,134" SelectionChanged="cboTest_SelectionChanged" DisplayMemberPath="Libelle" ItemSource="{Binding Source={StaticResource EthnieViewSource}}" />
In my Resources:
<Window.Resources>
<local:SNIGDoFFEDataSet x:Key"SNIGDoFFEDataSet" />
<CollectionViewSource x:Key="EthnieViewSource" Source="{Binding Ethnie, Source={StaticResources SNIGDoFFEDataSet}}" />
</Window.Resources>
The Solution is built correctly, the main Window loaded correctly, but when I select an item in the combobox, the program crashes. I try to remove all binding and create Item with and everything work, but by binding the source from my database, it crashes again.
The program crashes because you allow the exception to propagate out of your code. To stop the program from crashing, place everything in your SelectedChange event handler in a try catch block.
This should become a standard for all 'windows entry points' into your code. Whether it is an event or a command, if you didn't call it from your code, use a try catch block to stop exceptions from propagating out of your code.
Regarding the error, you are binding data to your ComboBox. When you interrogate the SelectedItem, it will be of the type of the data (in your case, of type System.Data.DataRowView), not a ComboBoxItem.
If you need the containing ComboBoxItem of the selected item, use the ItemContainerGenerator.ContainerFromItem() to find it, for example
Private Sub cboTesT_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles cboTesT.SelectionChanged
Try
Dim combo As ComboBox = CType(sender, ComboBox)
Dim item As ComboBoxItem = combo.ItemContainerGenerator.ContainerFromItem(combo.SelectedItem)
MessageBox.Show(item.Content)
Catch ex As Exception
' Log or display exception.
End Try
End Sub
If you are just after the data, CType the SelectedItem to your datatype (System.Data.DataRowView) and work with it directly.
I hope this helps.
Sorry Mark, to be so long
and again I'm sorry a lot
on msdn forum, they told me that as the binding source is a Table in a database I must use DataRowView instead of ComboBoxItem,
Dim drv As System.Data.DataRowView = CType(cboTest.SelectedItem, DataRowView)
MessageBox.show(drv("Libelle").ToString())
Don't hesitate to help me another time, please.
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));
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.