I have WPF Form where I've created a listview from a childdataview. The listview, amongst other controls, will have an image.
So the datarows from the childdataview have the "Image Name", the location is actually inside a seperate zip file.
I've accomplished the display of the pictures just fine in winforms:
If img.newFileFlag = vbTrue Then
photoDisp.Image = Image.FromFile(img.newFileLocation)
photoDisp.SizeMode = Windows.Forms.PictureBoxSizeMode.StretchImage
Else
Dim bp_Name As String = loc
Dim strm As FileStream = New FileStream(bp_Name, FileMode.Open)
Dim archive As ZipArchive = New ZipArchive(strm, ZipArchiveMode.Update)
Dim imageStrm As Stream
For Each entry As ZipArchiveEntry In archive.Entries
If entry.Name.Contains(img.xFileName) Then
imageStrm = entry.Open()
photoDisp.Image = Image.FromStream(imageStrm)
photoDisp.SizeMode = Windows.Forms.PictureBoxSizeMode.StretchImage
Exit For
End If
Next
strm.Close()
End If
Write wrong or in between, it works. So i've taken on the task of converting over to WPF and trying to do a bit MVVM as well.
So if i have a listview:
<ListView Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Left" Width="300" Background="Red" ItemsSource="{Binding Path=ConnectedChildView}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="/"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
What's the correct way to get that Image loaded correctly?
In my Viewmodel I can get to the zip file:
Public Property BPDProp As BredpackageData
Get
Return BPD
End Get
Set(value As BredpackageData)
BPD = value
End Set
End Property
And in the BredpackageData Class:
Public Property BredPackageLocationProperty() As String
Get
Return BredPackageLocation
End Get
Set(ByVal value As String)
BredPackageLocation = value
End Set
End Property
So i have all the data necessary to use a similar function as my winforms, and I could probably just sneak it into the code behind and make it work, but that feels wrong.
Is there a better, more correct way?
I'd like to post more about what I've tried, but I haven't really tried anything. I can't get anywhere.
Thanks
Related
Is it possible to display something in my treeview at design time and what would be the best way to achieve it?
Runtime works perfectly well and does display the data I want. However, I would like to have a more convenient display when designing than an empty area where the treeview is.
Thank you.
The treeview looks like this:
<TreeView x:Name="tvConfig" Width="400" Height="300" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type demo:TvItemsSource}" ItemsSource="{Binding Path=Items}">
<StackPanel Orientation="Horizontal">
<Image></Image>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And make uses of this class:
Public Class TvItemsSource
Public Property Items() As New List(Of TvItemsSource)
Public Property Name() As String
Public Sub New(Name As String)
Me.Name = Name
End Sub
End Class
I generated a small runtime data using the following piece of code:
Private _source As TvItemsSource
Private Sub LoadData()
_source = New TvItemsSource("Root")
_source.Items.Add(New TvItemsSource("Item 1"))
_source.Items.Add(New TvItemsSource("Item 2 "))
Dim ParentItem1 = New TvItemsSource("Parent 1")
ParentItem1.Items.Add(New TvItemsSource("Enfant 1"))
Dim ParentItem2 = New TvItemsSource("Parent 2")
ParentItem2.Items.Add(New TvItemsSource("Enfant 2"))
ParentItem1.Items.Add(ParentItem2)
_source.Items.Add(ParentItem1)
tvConfig.ItemsSource = _source.Items
End sub
edit: In the xaml of the treeview, I added the xmlns namespace of the application to declare the data type as :
xmlns:demo="clr-namespace:demo"
I just found the ideal solution for my project by using this statement:
Dim IsDesignMode As Boolean = DesignerProperties.GetIsInDesignMode(New DependencyObject())
The IsDesignMode variable does return true and anything therefore a listbox items source binded on a "Content" property would display the "Design Mode" items in the designer and the "Production" items when ran.
If IsDesignMode Then
For I = 1 To 10
Content.Add("DesignMode " & I)
Next
Else
Content.Add("Production 1")
Content.Add("Production 2")
Content.Add("Production 3")
End If
There are other way to do it.
Relevant:
SO: What approaches are available to dummy design-time data in WPF
Also:
Simulating data in design mode in Microsoft Blend
I am currently working on a multilingual app where the interface text can be swapped at runtime based on the language that is selected by the user. I am using DynamicResources defined in a ResourceDictionary and swapping the dictionary file when the language is changed. This works great for everything except for the DataGrid's Column Header property. I know that the DataGrid column is not part of the Visual Tree and have used proxies for binding to properties in my VM in the past however, in this case there is no binding to the VM. How can I go about updating the column headers when the ResourceDictionary is swapped?
My method for swapping dictionaries is below. This resides in the Application.xaml.vb and is called upon app startup passing a string saved in MySettings.Default. This is also called using a messenger from a property in a VM that is bound to a ComboBoxSelectedIndex.
Private Sub SetLanguage(ByVal language As String)
Dim dic As ResourceDictionary = Nothing
Dim langFile As String = Environment.CurrentDirectory & "\Languages\" & language & ".xaml"
If File.Exists(langFile) Then
Using fs As FileStream = New FileStream(langFile, FileMode.Open)
dic = CType(XamlReader.Load(fs), ResourceDictionary)
If LanguageCount > 0 Then
Resources.MergedDictionaries.RemoveAt(Resources.MergedDictionaries.Count - 1)
End If
Resources.MergedDictionaries.Add(dic)
End Using
End If
LanguageCount += 1
End Sub
The related DataGrid xaml
<DataGridTextColumn Header="{DynamicResource G_Spec}" ... />
The ResourceDictionary entry
<system:String x:Key="G_Spec">Spec:</system:String>
This is an extremely simplified example, basically you can use HeaderTemplate for the column:
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{DynamicResource MyColumnHeaderText}" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
</DataGridTemplateColumn>
I'm creating a WPF data access layer that probably doesn't require full MVVM at this stage (but I might implement it).
I've successfully created a ComboBox that data binds to a foreign key value of a related table using a CollectionViewSource as the data source (See my XAML below, the combo box works fine but the TextBlock doesn't).
I only want to display the ComboBox as the cell editing template and use a TextBlock for displaying the data when it is not being edited. I can get the TextBlock to almost work (it displays data from the table related in the Foreign Key) but I can't find the equivalent property for "SelectedValuePath" so the TextBlock always displays the first value from the related table, rather than the value that corresponds to the ID in the Foreign Key field.
Is there a way (there must be) to get an equivalent behaviour from the TextBlock as I have in the ComboBox? Is there an equivalent property for SelectedValuePath?
The answer to this question would be hugely useful as there are some other fields I want to display in my data grid without providing the user any ability to edit but I still want to display a field from a related table rather than the Foreign Key ID.
Thanks so much in advance for your help and have a great day!
<CollectionViewSource x:Key="QGradeLookup"/>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Source={StaticResource QGradeLookup}, Path=QGrade}"
>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
IsEditable="False"
ItemsSource="{Binding Source={StaticResource QGradeLookup}}"
DisplayMemberPath="QGrade"
SelectedValuePath="ID"
SelectedValue="{Binding Path=OfficeQualityGradeID}"
IsSynchronizedWithCurrentItem="False"
>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
As requested here's the code for the item's source (thanks NIT):
Dim QGradeLookup As CollectionViewSource
Dim QGradeList = From q In OMRInterfaceEntities.OfficeQualityGrades
Dim QGsource = CType(Me.FindResource("QGradeLookup"), CollectionViewSource)
QGsource.Source = QGradeList.ToList()
I used Beth Massi's post as a template for the above - Beth Massi's Template
And here's the codebehind
Public Class WinPropertyDataEntry
Dim QGradeLookup As CollectionViewSource
Private Function GetOMRMarketsQuery(OMRInterfaceEntities As OMRInterfaceCustomCode.OMRInterfaceEntities) As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket)
Dim OMRMarketsQuery As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket) = OMRInterfaceEntities.OMRMarkets
'To explicitly load data, you may need to add Include methods like below:
'OMRMarketsQuery = OMRMarketsQuery.Include("OMRMarkets.OMRMarketType").
'For more information, please see http://go.microsoft.com/fwlink/?LinkId=157380
'Update the query to include Properties data in OMRMarkets. You can modify this code as needed.
OMRMarketsQuery = OMRMarketsQuery.Include("Properties")
'Update the query to include OMRBuildingSurveys data in OMRMarkets. You can modify this code as needed.
OMRMarketsQuery = OMRMarketsQuery.Include("Properties.OMRBuildingSurveys").Where("it.ID = 12")
'Returns an ObjectQuery.
Return OMRMarketsQuery
End Function
Private Sub Window_Loaded_1(sender As Object, e As RoutedEventArgs) Handles MyBase.Loaded
Dim OMRInterfaceEntities As OMR.OMRInterfaceCustomCode.OMRInterfaceEntities = New OMR.OMRInterfaceCustomCode.OMRInterfaceEntities()
'Load data into OMRMarkets. You can modify this code as needed.
Dim OMRMarketsViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("OMRMarketsViewSource"), System.Windows.Data.CollectionViewSource)
Dim OMRMarketsQuery As System.Data.Objects.ObjectQuery(Of OMR.OMRInterfaceCustomCode.OMRMarket) = Me.GetOMRMarketsQuery(OMRInterfaceEntities)
OMRMarketsViewSource.Source = OMRMarketsQuery.Execute(System.Data.Objects.MergeOption.AppendOnly)
Dim QGradeList = From q In OMRInterfaceEntities.OfficeQualityGrades
Dim QGsource = CType(Me.FindResource("QGradeLookup"), CollectionViewSource)
QGsource.Source = QGradeList.ToList()
End Sub
The requirement you put here can be handled in the following manner. First of all binding the TextBlock here to Collection source and giving Path is not going to give you anything.
Each row in your DataGrid represents object of type OMRBuildingSurvey which has property OfficeQualityGradeID which I assume is of type string or int. Now what you want here is whenever the ID for OfficeQualityGrade is changed, the non-editable template i.e TextBlock should display the Grade name for the selected OfficeQualityGrade.
Problem here is you are not capturing the selected OfficeGrade here. So what you need to do is to first bind SelectedItem of the Combobox to the property of type "OfficeQualityGrade" (lets say SelectedOfficeGrade). The property should raise the property change notifications. And then you can bind your textblock to this property as Text = {Binding SelectedOfficeGrade.Grade}. you will have to define this property in your entity and implement INotifyPropertychanged.
Hope this will help.
Thanks
Okay, thanks so much Nit for your help. Your solution would absolutely work in a different scenario but each building survey has it's own unique Quality Grade. The answer is that I was referencing the object incorrectly. I was using the name of the column (OfficeQualityGradeID) Rather than the name of the object OfficeQualityGrade.
The following code provides a text block for the display of Quality Grades and a Combo Box for editing Quality Grades:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding OfficeQualityGrade.QGrade}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
x:Name="QGradeSelector"
IsEditable="False"
ItemsSource="{Binding Source={StaticResource QGradeLookup}}"
DisplayMemberPath="QGrade"
SelectedValuePath="ID"
SelectedValue="{Binding Path=OfficeQualityGradeID}"
IsSynchronizedWithCurrentItem="False"
>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
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.
I have a datagrid (A C1 datagrid, in this case) bound to a property in my View Model. The XAML for the datagrid looks like this:
<c1:C1DataGrid
AutoGenerateColumns="False"
IsReadOnly="False"
Margin="5" Width="auto"
MinWidth="250"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Name="dgNotifAssign"
CanUserAddRows="True"
ItemsSource="{Binding Path=notifCodeSubs.notification_configuration}"
>
<c1:C1DataGrid.Columns>
<c1:DataGridTextColumn
Binding="{Binding Path=user_id}"
Header="Recipient"
VerticalAlignment="Stretch"
SortMemberPath="user_id"
>
The property that it is bound to, in my viewmodel, looks like this:
Public Property notifCodeSubs As dsPeruseFM
Get
If _notifCode Is Nothing Then
_notifCode = New dsPeruseFM
End If
Return _notifCode
End Get
Set(ByVal value As dsPeruseFM)
MsgBox("If you can see this, success!")
End Set
End Property
In the codebehind I create an instance of the viewmodel and set the datacontext of the xaml to that instance, rather simple...
Dim vm As New ctrlAlertNotifyVM
As well as:
ctrlAlertNotifyXML.DataContext = vm
The above configuration compiles and reads data just fine. The grid is populated with all the correct data, etc. The problem comes when I try to add Mode=twoway to the ItemsSource on the datagrid. At that point VS2010 spits out the following error:
A TwoWay or OneWayToSource binding cannot work on the read-only property 'notification_configuration' of type 'PeruseFM.dsPeruseFM'.
I'm quite sure that all of my properties are read/write. And while the set command for this is nothing more than a message box at this point, it doesn't seem like I can even access that.
So the question is... has anybody ever encountered this issue before?
Update, response to question "What does notification_configuration look like?" from sixlettervariables:
Public Function codeChanged(Optional ByVal x As String = "")
If _notifCode Is Nothing Then
_notifCode = New dsPeruseFM
End If
taNotifSubs.fillNotifSubs(notifCode:=x, dataTable:=_notifCode.notification_configuration)
Return _notifCode
End Function
You've shown us that notifCodeSubs is read/write, however, that is not the actual property you've bound to.
From this viewpoint, the error message is fairly self-explanatory:
...read-only property 'notification_configuration'...
Therefore you cannot apply TwoWay binding to that property as an ItemsSource.