Binding multiple progress bars - wpf

Referring to screenshot below, I have the following view and viewmodel setup to represent progress bar download. When I add more than 1 downloads to this collection of downloads, it seems that download 1 will overwrite download 2's Progress() public property.
Example below is download 1 is downloading 1MB it will finish first and compared with download 2 downloading 5MB.
When download 1 finish, download 2 (hello2 example below) progress bar just stop there but the bytes are still being downloaded at background until 5MB is reached.
How can I change the code to ensure download 1 does not interfere with download 2's progress bar UI. I've tried modifying Progress() to private but then both progress bar UI is not shown
View
<ItemsControl Name="MyItemsControl" ItemsSource="{Binding GameDownloads}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid DockPanel.Dock="Right">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding GameName}" />
<ProgressBar Grid.Row="0" Grid.Column="1" Minimum="0" Maximum="100" Value="{Binding Progress, Mode=OneWay}" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding StatusText}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
DownloadAppViewModel
Public Class DownloadAppViewModel
Inherits ViewModelBase
Private ReadOnly WC As WebClient
Public Sub New(ByVal Name As String, ByVal URL As String, ByVal FileName As String)
_gameName = Name
WC = New WebClient
AddHandler WC.DownloadFileCompleted, AddressOf DownloadCompleted
AddHandler WC.DownloadProgressChanged, AddressOf DownloadProgressChanged
WC.DownloadFileAsync(New Uri(URL), FileName)
End Sub
Private Sub DownloadCompleted(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
End Sub
Private Sub DownloadProgressChanged(ByVal sender As [Object], ByVal e As DownloadProgressChangedEventArgs)
If _totalSize = 0 Then
_totalSize = e.TotalBytesToReceive
End If
DownloadedSize = e.BytesReceived
End Sub
Private _gameName As String
Public Property GameName() As String
Get
Return _gameName
End Get
Set(ByVal value As String)
_gameName = value
Me.OnPropertyChanged("GameName")
End Set
End Property
Public ReadOnly Property StatusText() As String
Get
If _downloadedSize < _totalSize Then
Return String.Format("Downloading {0} MB of {1} MB", _downloadedSize, _totalSize)
End If
Return "Download completed"
End Get
End Property
Private _totalSize As Long
Private Property TotalSize() As Long
Get
Return _totalSize
End Get
Set(ByVal value As Long)
_totalSize = value
OnPropertyChanged("TotalSize")
OnPropertyChanged("Progress")
OnPropertyChanged("StatusText")
End Set
End Property
Private _downloadedSize As Long
Private Property DownloadedSize() As Long
Get
Return _downloadedSize
End Get
Set(ByVal value As Long)
_downloadedSize = value
OnPropertyChanged("DownloadedSize")
OnPropertyChanged("Progress")
OnPropertyChanged("StatusText")
End Set
End Property
Public ReadOnly Property Progress() As Double
Get
If _totalSize <> 0 Then
Return 100.0 * _downloadedSize / _totalSize
Else
Return 0.0
End If
End Get
End Property
End Class

Related

WPF NumericUpDown usercontrol in VB.net

I am creating my own NumericUpDown control in VB. Here is my XAML:
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
x:Class="NumericUpDown"
mc:Ignorable="d"
d:DesignHeight="30" d:DesignWidth="100">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<TextBox x:Name="txtNum" Grid.Column="0" x:FieldModifier="private" TextChanged="txtNum_TextChanged"/>
<Button x:Name="cmdDown" Grid.Column="1" x:FieldModifier="private" Content="˅" Width="20" Click="cmdDown_Click" />
<Button x:Name="cmdUp" Grid.Column="2" x:FieldModifier="private" Content="˄" Width="20" Click="cmdUp_Click" />
</Grid>
</UserControl>
And here is the VB code behind it:
Class NumericUpDown
Dim _Minimum As Double = 0
Dim _Maximum As Double = 100
Private Sub NumericUpDown()
InitializeComponent()
txtNum.Text = Numeric
End Sub
Public Property Maximum As Double
Get
Return _Maximum
End Get
Set(value As Double)
_Maximum = value
End Set
End Property
Public Property Minimum As Double
Get
Return _Minimum
End Get
Set(value As Double)
_Minimum = value
End Set
End Property
Public Shared ReadOnly NumericProperty As DependencyProperty = DependencyProperty.Register("Numeric", GetType(String), GetType(NumericUpDown), _
New PropertyMetadata(""))
Public Property Numeric As String
Get
Return CType(GetValue(NumericProperty), String)
End Get
Set(value As String)
SetValue(NumericProperty, value)
End Set
End Property
Private Sub cmdUp_Click(sender As Object, e As RoutedEventArgs)
Dim NumValue As Double
NumValue = Val(txtNum.Text)
NumValue += 1
If NumValue > Maximum Then NumValue = Maximum
txtNum.Text = NumValue.ToString
End Sub
Private Sub cmdDown_Click(sender As Object, e As RoutedEventArgs)
Dim NumValue As Double
NumValue = Val(txtNum.Text)
NumValue -= 1
If NumValue < Minimum Then NumValue = Minimum
txtNum.Text = NumValue.ToString
End Sub
Private Sub txtNum_TextChanged(sender As Object, e As TextChangedEventArgs)
Numeric = txtNum.Text
End Sub
End Class
I use it in my page like this:
Put this on page definition:
xmlns:local="clr-namespace:Demo"
And put this in the content section:
<local:NumericUpDown Numeric="{Binding Path=score, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
Of course I have already set DataContext on the container and all other databound controls work as they should. But the textbox in my custom control turned out empty! It doesn't end here. When I type something in the textbox and when I give it some value using decrease and increase button, the value is transferred to my DataTable; which means this usercontrol does work to some extend. Where did I do wrong? Why won't the Textbox content be initialized with the starting value?
After a little more testing. It seems that my usercontrol doesn't work in 'Two-Way'. It doesn't receive data from DataTable, it only propagates value to it. How do I fix it?
The issue is that you are not binding the Text property of txtNum to your Numeric property.
<TextBox x:Name="txtNum" Grid.Column="0" x:FieldModifier="private"
Text="{Binding Path=Numeric, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"/>
Then you can remove txtNum_TextChanged, and modify your up/down click handlers to just update the Numeric property, e.g.:
Private Sub cmdUp_Click(sender As Object, e As RoutedEventArgs)
If Me.Numeric < Me.Maximum Then
Me.Numeric += 1
Else
Me.Numeric = Me.Maximum
End If
End Sub
Private Sub cmdDown_Click(sender As Object, e As RoutedEventArgs)
If Me.Numeric > Me.Minimum Then
Me.Numeric -= 1
Else
Me.Numeric = Me.Minimum
End If
End Sub
Note that there are still lots of issues - a user can enter a value outside of the allowed range, or non-numeric data (which will break things!), etc. For this specific problem, you could check out the Extended EPF Toolkit, which has various up/down controls.

How to bind object to UserControl?

I have a CustomerOrder class which has 2 properties namely CustomerName and NumberOfOrders. I have a Usercontrol to show these two properties, namely ucCustomerOrder.
Following the code corresponding
Customer Class
Public Class CustomerOrder
Private m_CustomerName As String = String.Empty
Private m_NumberOfOrders As Decimal = 0
Public Property CustomerName As String
Get
Return m_CustomerName
End Get
Set(ByVal value As String)
m_CustomerName = value
End Set
End Property
Public Property NumberOfOrders As Decimal
Get
Return m_NumberOfOrders
End Get
Set(ByVal value As Decimal)
m_NumberOfOrders = value
End Set
End Property
End Class
UserControl XAML
<UserControl x:Class="ucCustomerOrder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="99" d:DesignWidth="300">
<Grid>
<Label Name="lblCustomerName" Content="{Binding Path=CustomerName}" Height="28" HorizontalAlignment="Left" Margin="23,12,0,0" VerticalAlignment="Top" Width="237" />
<Label Name="lblNoOfOrders" Content="{Binding Path=NumberOfOrders}" Height="28" HorizontalAlignment="Left" Margin="23,46,0,0" VerticalAlignment="Top" Width="237" />
</Grid>
</UserControl>
Code behind the User control is as follows.
Public Class ucCustomerOrder
Private m_CustomerOrder As CustomerOrder
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
m_CustomerOrder = New CustomerOrder
Me.DataContext = m_CustomerOrder
End Sub
Public Property CustomerOrder As CustomerOrder
Get
Return m_CustomerOrder
End Get
Set(ByVal value As CustomerOrder)
m_CustomerOrder = value
Me.DataContext = m_CustomerOrder
End Set
End Property
End Class
In my main window I have a combo box control and ucCustomerOrder (User Control) and also I have list of Customer’s orders in Dictionary object. Like as follows
XAML For main windows looks like
<Window x:Class="DictionaryTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DictionaryTest" Height="182" Width="300" xmlns:my="clr-namespace:TestUserControls">
<Grid>
<ComboBox Height="23" Width="254" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="12,12,0,0" Name="cboCustomerOrders" ItemsSource="{Binding}" DisplayMemberPath="Key" SelectedValuePath="Value"/>
<my:ucCustomerOrder HorizontalAlignment="Left" VerticalAlignment="Top" Margin="12,56,0,0" x:Name="UcCustomerOrder1"/>
</Grid>
</Window>
Code Behind for my main window as follows.
Public Class DictionaryTest
Private Function GetCustomers() As Dictionary(Of String, CustomerOrder)
Dim customerList As New Dictionary(Of String, CustomerOrder)
Dim customer1 As New CustomerOrder
With customer1
.CustomerName = "XXXX"
.NumberOfOrders = 100
End With
Dim customer2 As New CustomerOrder
With customer2
.CustomerName = "ZZZZ"
.NumberOfOrders = 150
End With
customerList.Add("X-Customer", customer1)
customerList.Add("Z-Customer", customer2)
Return customerList
End Function
Private Sub DictionaryTest_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Try
cboCustomerOrders.ItemsSource = GetCustomers()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
End Class
How can I set my selected CustomerOrder object to the user control using Binding?

WPF Parent Element Lost Focus

I am developing a WPF application. In one of my pages I have a DockPanel with 3 ComboBoxes within it. These 3 ComboBoxes are dependent on each other. So, I have a binding group on the DockPanel and a validation rule for that binding group.
I would like to validate these 3 ComboBoxes when the DockPanel loses focus; however, the DockPanel's LostFocus event is fired when the user clicks on one of the child ComboBoxes within it!
I would have thought that if the child control has focus, the parent control would also have focus...however this does not seem to be the case.
I am looking for a solution to perform validation on the binding group when the DockPanel loses focus...where the focus is lost to a control that is not one of its children.
Edit:
So, I created as simple an application as I could to demonstrate the problem.
I have a "FertilizerCombination" class that contains 3 integers that make up the Fertilizer. These must be unique within the application. Currently the only combination that is not available is 10-10-10. This is a hard coded value in the class.
Public Class FertilizerCombination
Private _nitrogen As Integer
Private _phosphorous As Integer
Private _potassium As Integer
<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Nitrogen As Integer
Get
Return _nitrogen
End Get
Set(value As Integer)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, NitrogenValidationContext)
_nitrogen = value
End Set
End Property
<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Phosphorous As Integer
Get
Return _phosphorous
End Get
Set(value As Integer)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, PhosphorousValidationContext)
_phosphorous = value
End Set
End Property
<System.ComponentModel.DataAnnotations.Range(1, 20)> _
Public Property Potassium As Integer
Get
Return _potassium
End Get
Set(value As Integer)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, PotassiumValidationContext)
_potassium = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer)
Me.Nitrogen = nitrogen
Me.Phosphorous = phosphorous
Me.Potassium = potassium
End Sub
Public Shared Function FertilizerCombinationAvailable(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer) As Boolean
'Checking against combinations already used'
If nitrogen = "10" And phosphorous = "10" And potassium = "10" Then
'Combination has already been used'
Return False
End If
'Combination was not used yet'
Return True
End Function
Public Sub SetFertilizerCombination(ByVal nitrogen As Integer, ByVal phosphorous As Integer, ByVal potassium As Integer)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(nitrogen, NitrogenValidationContext)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(phosphorous, PhosphorousValidationContext)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(potassium, PotassiumValidationContext)
If FertilizerCombination.FertilizerCombinationAvailable(nitrogen, phosphorous, potassium) = False Then
Throw New ArgumentException("This fertilizer combination has already been used")
End If
Me.Nitrogen = nitrogen
Me.Phosphorous = phosphorous
Me.Potassium = potassium
End Sub
Private NitrogenValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Nitrogen"}
Private PhosphorousValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Phosphorous"}
Private PotassiumValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Potassium"}
End Class
I have a created a Validation Rule for the Fertilizer Combination Class:
Public Class FertilizerCombinationValidationRule
Inherits ValidationRule
Public Overrides Function Validate(ByVal value As Object, ByVal cultureInfo As System.Globalization.CultureInfo) As System.Windows.Controls.ValidationResult
Dim bg As BindingGroup = TryCast(value, BindingGroup)
If bg IsNot Nothing AndAlso bg.Items.Count > 0 Then
'The BindingGroup Items property contains the original object(s)'
Dim fertilizerCombo As FertilizerCombination = TryCast(bg.Items(0), FertilizerCombination)
'Using the BindingGroups GetValue method to retrieve the user provided values'
Dim proposedNitrogen As Integer
Dim proposedPhosphorous As Integer
Dim proposedPotassium As Integer
Try
proposedNitrogen = bg.GetValue(fertilizerCombo, "Nitrogen")
proposedPhosphorous = bg.GetValue(fertilizerCombo, "Phosphorous")
proposedPotassium = bg.GetValue(fertilizerCombo, "Potassium")
If FertilizerCombination.FertilizerCombinationAvailable(proposedNitrogen, proposedPhosphorous, proposedPotassium) = False Then
Return New ValidationResult(False, "This fertializer combination has already been used")
End If
Catch noValue As System.Windows.Data.ValueUnavailableException
'a binding was not properly bound yet'
End Try
End If
Return New ValidationResult(True, Nothing)
End Function
End Class
I have a "PlantSolution" class that has a FertilizerCombination property. I also have a VM for that class so that it's easy for binding in the XAML:
Public Class PlantSolutionVM
Public Property PlantSolution As PlantSolution
Public Sub New()
PlantSolution = New PlantSolution("Produce Blooms", "Using this fertilizer will help your plant produce flowers!", 10, 10, 20)
End Sub
End Class
Public Class PlantSolution
Private _name As String
Private _description As String
Private _fertilizer As FertilizerCombination
<System.ComponentModel.DataAnnotations.Required()>
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, NameValidationContext)
_name = value
End Set
End Property
<System.ComponentModel.DataAnnotations.Required()>
Public Property Description As String
Get
Return _description
End Get
Set(value As String)
System.ComponentModel.DataAnnotations.Validator.ValidateProperty(value, DescriptionValidationContext)
_description = value
End Set
End Property
Public ReadOnly Property Fertilizer As FertilizerCombination
Get
Return _fertilizer
End Get
End Property
Public Sub New(name As String, description As String, nitrogen As Integer, phosphorous As Integer, potassium As Integer)
_fertilizer = New FertilizerCombination(nitrogen, phosphorous, potassium)
Me.Name = name
Me.Description = description
End Sub
Private NameValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Name"}
Private DescriptionValidationContext = New System.ComponentModel.DataAnnotations.ValidationContext(Me, Nothing, Nothing) With {.MemberName = "Description"}
End Class
Now here is my XAML for a window called "FunWithFocus":
<Grid>
<Grid.Resources>
<x:Array x:Key="CombinationOptions" Type="sys:Int32" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:Int32>0</sys:Int32>
<sys:Int32>1</sys:Int32>
<sys:Int32>2</sys:Int32>
<sys:Int32>3</sys:Int32>
<sys:Int32>4</sys:Int32>
<sys:Int32>5</sys:Int32>
<sys:Int32>6</sys:Int32>
<sys:Int32>7</sys:Int32>
<sys:Int32>8</sys:Int32>
<sys:Int32>9</sys:Int32>
<sys:Int32>10</sys:Int32>
<sys:Int32>11</sys:Int32>
<sys:Int32>12</sys:Int32>
<sys:Int32>13</sys:Int32>
<sys:Int32>14</sys:Int32>
<sys:Int32>15</sys:Int32>
<sys:Int32>16</sys:Int32>
<sys:Int32>17</sys:Int32>
<sys:Int32>18</sys:Int32>
<sys:Int32>19</sys:Int32>
<sys:Int32>20</sys:Int32>
</x:Array>
<local:PlantSolutionVM x:Key="PlantSolutionVM" />
<Style TargetType="DockPanel">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid DataContext="{Binding Source={StaticResource PlantSolutionVM}, Path=PlantSolution}" Margin="50">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Solution Name:" Grid.Row="0" Grid.Column="0" Margin="5"/>
<TextBox x:Name="ItemName" Text="{Binding Name, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Margin="5"/>
<TextBlock Text="Description of Problem:" Grid.Row="1" Grid.Column="0" Margin="5"/>
<TextBox x:Name="ItemDescription" Text="{Binding Description, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Grid.Row="1" Grid.Column="1" Margin="5"/>
<TextBlock Text="Recommended Fertilizer:" Grid.Row="2" Grid.Column="0" Margin="5"/>
<DockPanel x:Name="FertilizerCombinationContainer" DataContext="{Binding Fertilizer}" Grid.Row="2" Grid.Column="1" Margin="5" HorizontalAlignment="Left" VerticalAlignment="Top">
<DockPanel.BindingGroup>
<BindingGroup NotifyOnValidationError="True">
<BindingGroup.ValidationRules>
<local:FertilizerCombinationValidationRule />
</BindingGroup.ValidationRules>
</BindingGroup>
</DockPanel.BindingGroup>
<ComboBox x:Name="NitrogenValue" HorizontalAlignment="Left" VerticalAlignment="Top"
ItemsSource="{StaticResource CombinationOptions}"
SelectedItem="{Binding Nitrogen}"/>
<ComboBox x:Name="PhosphorousValue" HorizontalAlignment="Left" VerticalAlignment="Top"
ItemsSource="{StaticResource CombinationOptions}"
SelectedItem="{Binding Phosphorous}"/>
<ComboBox x:Name="PotatssiumValue" HorizontalAlignment="Left" VerticalAlignment="Top"
ItemsSource="{StaticResource CombinationOptions}"
SelectedItem="{Binding Potassium}"/>
</DockPanel>
<Button x:Name="SaveIt" Content="Save" Grid.Row="3" Grid.Column="1"/>
</Grid>
</Grid>
And here is the Code Behind for the page:
Class FunWithFocus
Private Sub FertilizerCombinationContainer_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles FertilizerCombinationContainer.Loaded
'FertilizerCombinationContainer.BindingGroup.CancelEdit()'
'FertilizerCombinationContainer.BindingGroup.BeginEdit()'
End Sub
Private Sub FertilizerCombinationContainer_LostFocus(sender As Object, e As System.Windows.RoutedEventArgs) Handles FertilizerCombinationContainer.LostFocus
'This will get fired if the user drops down one of the comboboxes'
End Sub
'This is how I am currently handling the lost focus...but it doesn't fire if the user clicks somewhere that doesn't take keyboard focus'
'Private Sub FertilizerCombinationContainer_IsKeyboardFocusWithinChanged(sender As Object, e As System.Windows.DependencyPropertyChangedEventArgs) Handles FertilizerCombinationContainer.IsKeyboardFocusWithinChanged'
' If FertilizerCombinationContainer.IsKeyboardFocusWithin = False Then'
' FertilizerCombinationContainer.BindingGroup.ValidateWithoutUpdate()'
' If Validation.GetErrors(FertilizerCombinationContainer).Count = 0 Then'
' FertilizerCombinationContainer.BindingGroup.CommitEdit()'
' Dim bg As BindingGroup = FertilizerCombinationContainer.BindingGroup'
' Dim fertilizerCombo As FertilizerCombination = bg.Items(0)'
' Dim proposedNitrogen As Integer'
' Dim proposedPhosphorous As Integer'
' Dim proposedPotassium As Integer'
' Try'
' proposedNitrogen = bg.GetValue(fertilizerCombo, "Nitrogen")'
' proposedPhosphorous = bg.GetValue(fertilizerCombo, "Phosphorous")'
' proposedPotassium = bg.GetValue(fertilizerCombo, "Potassium")'
' ''there was a change: set it'
' fertilizerCombo.SetFertilizerCombination(proposedNitrogen, proposedPhosphorous, proposedPotassium)'
' Catch noValue As System.Windows.Data.ValueUnavailableException'
' ''a binding was not properly bound yet'
' End Try'
' End If'
' End If'
'End Sub'
End Class
If you put a break point on the method that handles FertilizerCombinationContainer's LostFocus event, you will notice that it is fired when you select one of the ComboBoxes even though it is a child of the FertilizerContainer element.
Thank you,
-Frinny

ListView do not get updated even with INotifyPropertyChanged interface

I have a UI element which is a listview that contains in it another listview (one vertical and one horizontal).
The Itemsource of the the First listview is an ObservableCollection of Objects, which in turn contains ObservableCollection of a child object. Both have INotifyPropertyChanged interface implemented and call the PropetyChanged Event when usefull.
If I added new child object, I see my second listview update, but when I change a value, I do not see anything change. Is there anything I must do to refresh my UI element in this case ?
Here is my listview
<ListView x:Name="LstInfoEtape" Margin="0,0,0,0" Style="{StaticResource BaseListView}" Grid.Row="2" Grid.Column="1" >
<ListView.ItemTemplate>
<DataTemplate>
<ListView x:Name="LstEtape" ItemsSource="{Binding LstEtapes}" Background="#00000000" SelectionChanged="ListView_SelectionChanged" ScrollViewer.ScrollChanged="ListView_ScrollChanged">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Border Width="82" CornerRadius="4" BorderBrush="{Binding ColorSelected}" BorderThickness="3,3,3,3" Visibility="{Binding EtapeVisible}" Background="{Binding ColorBackground}" MouseDown="Border_MouseDown">
<Grid Width="80" >
<Grid.RowDefinitions>
<RowDefinition Height="19"/>
</Grid.RowDefinitions>
<TextBlock Margin="2,0,2,0" x:Name="TxtDateRappel" TextWrapping="Wrap" Grid.Row="0" Text="{Binding DateRappelTerminaison,StringFormat=yyyy-MM-dd,NotifyOnSourceUpdated=True}" Visibility="{Binding EtapeVisible}" FontSize="12" Foreground="{Binding ColorForeground}" />
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The classes I use are those
Public Class SuiviOperationLite
Implements INotifyPropertyChanged
Private _strNom As String
Private _oLstProcessusLite As ObservableCollection(Of ContratProcessusLite) ' Not implemented yet
Private _oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite)
Public Sub New(ByVal strNom As String,
ByVal oLstProcessusLite As ObservableCollection(Of ContratProcessusLite),
ByVal oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite))
Constructor(strNom, oLstProcessusLite, oLstEtapeLite)
End Sub
Private Sub Constructor(ByVal strNom As String,
ByVal oLstProcessusLite As ObservableCollection(Of ContratProcessusLite),
ByVal oLstEtapeLite As ObservableCollection(Of ContratProcessusEtapeLite))
_strNom = strNom
_oLstProcessusLite = oLstProcessusLite
_oLstEtapeLite = oLstEtapeLite
End Sub
Public Property Nom() As String
Get
Return _strNom
End Get
Set(ByVal value As String)
_strNom = value
End Set
End Property
Public Property LstContratProcessus() As ObservableCollection(Of ContratProcessusLite)
Get
Return _oLstProcessusLite
End Get
Set(ByVal value As ObservableCollection(Of ContratProcessusLite))
_oLstProcessusLite = value
End Set
End Property
Public Property LstEtapes() As ObservableCollection(Of ContratProcessusEtapeLite)
Get
Return _oLstEtapeLite
End Get
Set(ByVal value As ObservableCollection(Of ContratProcessusEtapeLite))
_oLstEtapeLite = value
End Set
End Property
Protected Friend Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
End Sub
Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class
Public Class ContratProcessusEtapeLite
Implements INotifyPropertyChanged
Private _lngId As Long
Private _lngIdSequenceTravail As Long
Private _boolSelected As Boolean
Private _strETapeVisible As String
Private _strColorBackGround As String
Private _strColorForeGround As String
Private _dateRappelTerminaison As Date
Private _oParent As DAL.SuiviOperationLite
Public Sub New(ByVal oParent As DAL.SuiviOperationLite,
ByVal lngId As Long,
ByVal lngIdSequenceTravail As Long,
ByVal boolSelected As Boolean,
ByVal strEtapeVisible As String,
ByVal strColorBackGround As String,
ByVal strColorForeGround As String,
ByVal dateRappelTerminaison As Date)
Constructor(oParent,
lngId, _
lngIdSequenceTravail, _
boolSelected, _
strEtapeVisible, _
strColorBackGround, _
strColorForeGround, _
dateRappelTerminaison)
End Sub
Private Sub Constructor(ByVal oParent As DAL.SuiviOperationLite,
ByVal lngId As Long,
ByVal lngIdSequenceTravail As Long,
ByVal boolSelected As Boolean,
ByVal strEtapeVisible As String,
ByVal strColorBackGround As String,
ByVal strColorForeGround As String,
ByVal dateRappelTerminaison As Date)
_oParent = oParent
_lngId = lngId
_lngIdSequenceTravail = lngIdSequenceTravail
_boolSelected = boolSelected
_strETapeVisible = strEtapeVisible
_strColorBackGround = strColorBackGround
_strColorForeGround = strColorForeGround
_dateRappelTerminaison = dateRappelTerminaison
End Sub
Public Property Parent() As DAL.SuiviOperationLite
Get
Return _oParent
End Get
Set(ByVal value As DAL.SuiviOperationLite)
_oParent = value
End Set
End Property
Public Property ID() As Long
Get
Return _lngId
End Get
Set(ByVal value As Long)
_lngId = value
NotifyPropertyChanged("ID")
End Set
End Property
Public Property IdClientContratSequenceTravail() As Long
Get
Return _lngIdSequenceTravail
End Get
Set(ByVal value As Long)
_lngIdSequenceTravail = value
NotifyPropertyChanged("IdSequence")
End Set
End Property
Public Property Selected() As Boolean
Get
Return _boolSelected
End Get
Set(ByVal value As Boolean)
_boolSelected = value
NotifyPropertyChanged("Selected")
End Set
End Property
Public ReadOnly Property ColorSelected As String
Get
If _boolSelected Then
Return "#FF4394DF"
Else
Return "#FF000000"
End If
End Get
End Property
Public Property EtapeVisible() As String
Get
Return _strETapeVisible
End Get
Set(ByVal value As String)
_strETapeVisible = value
NotifyPropertyChanged("Visible")
End Set
End Property
Public Property ColorBackground() As String
Get
Return _strColorBackGround
End Get
Set(ByVal value As String)
_strColorBackGround = value
NotifyPropertyChanged("Background")
End Set
End Property
Public Property ColorForeground() As String
Get
Return _strColorForeGround
End Get
Set(ByVal value As String)
_strColorForeGround = value
NotifyPropertyChanged("Foreground")
End Set
End Property
Public Property DateRappelTerminaison() As Date
Get
Return _dateRappelTerminaison
End Get
Set(ByVal value As Date)
_dateRappelTerminaison = value
NotifyPropertyChanged("DateRappel")
End Set
End Property
Private Sub NotifyPropertyChanged(ByVal info As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
_oParent.NotifyPropertyChanged("Child")
End Sub
Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class
And here are the functions (There are more processing to get the collection of items, but I doubt it's really relevant :
Private Sub LoadList(colSuivi as ObservableCollection(Of SuiviProductionLite)
LstInfoEtape.ItemsSource = colSuivi
End Sub
Private Sub Border_MouseDown(sender As System.Object, e As System.Windows.Input.MouseButtonEventArgs)
Dim oBorder As Border
Dim oEtapeLite As DAL.ContratProcessusEtapeLite
Select Case e.Source.GetType.ToString
Case "System.Windows.Controls.TextBlock"
Dim oTxt As TextBlock = e.Source
Dim oGrid As Grid = oTxt.Parent
_oCurBorderDet = oGrid.Parent
Case "System.Windows.Controls.Border"
Dim oBrd As Border = e.Source
_oCurBorderDet = e.Source
End Select
If Not _oCurBorderDet.DataContext.GetType().FullName.Equals("MS.Internal.NamedObject") Then
oEtapeLite = _oCurBorderDet.DataContext
oEtapeLite.Selected = True
End If
End Sub
When I do some tracing, I know that both PropertyChanged Events are call wheter I click on my UI. If I do a manual "Items.Refresh", I can also see the UI being changed, but I want to remove that manual refresh as it refresh everything, not only the item just modified, which, when facing a lot of data, takes much more time.
I see one problem
Public Property IdClientContratSequenceTravail() As Long
Get
Return _lngIdSequenceTravail
End Get
Set(ByVal value As Long)
_lngIdSequenceTravail = value
NotifyPropertyChanged("IdSequence")
End Set
End Property
Wrong name in Notify

Treeview With One To Many Linq to SQL tables

I have a couple tables dealing with cars:
<Table(Name:="tblManufacturer")> Public Class dbManufacturers
Private _ManufacturerID As Integer
<Column(Storage:="_ManufacturerID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ManufacturerID")> _
Public Property ManufacturerID() As Integer
Get
Return Me._ManufacturerID
End Get
Set(value As Integer)
Me._ManufacturerID = value
End Set
End Property
Private _ManufacturerName As String
<Column(Storage:="_ManufacturerName", DbType:="Varchar(50)", Name:="ManufacturerName")> _
Public Property ManufacturerName() As String
Get
Return Me._ManufacturerName
End Get
Set(value As String)
Me._ManufacturerName = value
End Set
End Property
Private _Models As EntitySet(Of dbModels) = New EntitySet(Of dbModels)
<Association(Storage:="_Models", DeleteRule:="CASCADE", OtherKey:="ManufacturerID")> _
Public Property Models As EntitySet(Of dbModels)
Get
Return _Models
End Get
Set(value As EntitySet(Of dbModels))
_Models.Assign(value)
End Set
End Property
End Class
<Table(Name:="tblModels")> Public Class dbModels
Private _ModelID As Integer
<Column(Storage:="_ModelID", DbType:="int IDENTITY NOT NULL", IsPrimaryKey:=True, IsDbGenerated:=True, Name:="ModelID")> _
Public Property ModelID() As Integer
Get
Return Me._ModelID
End Get
Set(value As Integer)
Me._ModelID = value
End Set
End Property
Private _ManufacturerID As Integer
<Column(Storage:="_ManufacturerID", DbType:="int", Name:="ManufacturerID")> _
Public Property ManufacturerID() As Integer
Get
Return Me._ManufacturerID
End Get
Set(value As Integer)
Me._ManufacturerID = value
End Set
End Property
Private _ModelName As String
<Column(Storage:="_ModelName", DbType:="Varchar(200)", Name:="ModelName")> _
Public Property ModelName() As String
Get
Return Me._ModelName
End Get
Set(value As String)
Me._ModelName = value
End Set
End Property
End Class
The XAML:
<TreeView Name="TreeView1" Margin="3" ItemsSource="{Binding ElementName=ManufacturerInfo, Path=tblManufacturers}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate>
<TextBlock Text="{Binding Path=ManufacturerName}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source=Models, Path=ModelName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The code behind:
Public Class ManufacturerInfo
Private LinqDB As New DataContext(My.Settings.dbConnection)
Property tblManufacturers As Table(Of dbManufacturers) = LinqDB.GetTable(Of dbManufacturers)()
Public Sub New()
InitializeComponent()
End Sub
Private Sub ManufacturerInfo_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
Me.DataContext = Me
End Sub
End Class
So I get the list of Manufacturers, but not the nested models. Is there a way to make this work, or do I have to change how I'm doing my LINQ query?
I'm having trouble finding similar treeview examples.
Figured it out.
Still have a bit to learn about the subtle art of data binding.
<TreeView Name="TreeView1" Margin="3" ItemsSource="{Binding ElementName=ManufacturerInfo, Path=tblManufacturers}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Models}">
<TextBlock Text="{Binding Path=ManufacturerName}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ModelName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Resources