Binding to a WPF custom control dependency property - wpf

I am having a hard time binding a property of a wpf custom control.
Here is an excerp of my xaml custom datagrid cell whose datacontext is the list(of date):
<DataGridTemplateColumn Header="Start" MinWidth="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<timePicker:CustomTimePicker selectedTime="{Binding Path=startDate}" MinWidth="100" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
As you can see, I try to bind the startDate Property of my List(Of Date) to the selectedTime dependency property in my custom wpf control TimePicker.
Here is the definition of the customtimepicker class with the selectedDate dependency-property:
Public Class CustomTimePicker
Private _hours As String
Private _minutes As String
Private _hoursChoices As List(Of String)
Private _minutesChoices As List(Of String)
Public Shared selectedTimeProperty As DependencyProperty = DependencyProperty.Register("selectedTime", GetType(Date), GetType(CustomTimePicker), New FrameworkPropertyMetadata(DateTime.MinValue, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, New PropertyChangedCallback(AddressOf CustomTimePicker.OnSelectedTimeChanged)))
Public Property selectedTime() As Date
Get
Return DirectCast(GetValue(selectedTimeProperty), Date)
End Get
Set(value As Date)
SetValue(selectedTimeProperty, value)
End Set
End Property
Public Property hours As String
Get
Return _hours
End Get
Set(value As String)
_hours = value
End Set
End Property
Public Property minutes As String
Get
Return _minutes
End Get
Set(value As String)
_minutes = value
End Set
End Property
Public Property hoursChoices As List(Of String)
Get
Return _hoursChoices
End Get
Set(value As List(Of String))
_hoursChoices = value
End Set
End Property
Public Property minutesChoices As List(Of String)
Get
Return _minutesChoices
End Get
Set(value As List(Of String))
_minutesChoices = value
End Set
End Property
Protected Shared Sub OnSelectedTimeChanged(ByVal obj As DependencyObject, ByVal args As DependencyPropertyChangedEventArgs)
Console.WriteLine("#######")
Console.WriteLine(CType(obj, CustomTimePicker).selectedTime.ToString)
End Sub
Protected Shared Sub OnCoerce()
End Sub
Protected Sub OnSelectedHourChanged(obj As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles hourComboBox.SelectionChanged
Dim minute As Integer = 0
Dim result As Date = New Date(2000, 1, 1, hourComboBox.SelectedIndex, selectedTime.Minute, 0)
selectedTime = result
Console.WriteLine("---")
Console.WriteLine(result.ToString)
Console.WriteLine(selectedTime.ToString)
End Sub
Protected Sub OnSelectedMinuteChanged(obj As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles minutesComboBox.SelectionChanged
Dim minute As Integer = 0
Console.WriteLine("bbbbbb")
Console.WriteLine(selectedTime.ToString)
Select Case minutesComboBox.SelectedIndex
Case 0
minute = 0
Case 1
minute = 15
Case 2
minute = 30
Case 3
minute = 45
End Select
Dim result As Date = New Date(2000, 1, 1, selectedTime.Hour, minute, 0)
selectedTime = result
End Sub
Protected Overrides Sub OnInitialized(e As System.EventArgs)
MyBase.OnInitialized(e)
_hoursChoices = New List(Of String)
_minutesChoices = New List(Of String)
Console.WriteLine("aaaaaaa")
Console.WriteLine(selectedTime.ToString)
For i As Integer = 0 To 23
_hoursChoices.Add(i.ToString)
Next
_minutesChoices.Add("00")
_minutesChoices.Add("15")
_minutesChoices.Add("30")
_minutesChoices.Add("45")
hourComboBox.ItemsSource = hoursChoices
hourComboBox.DisplayMemberPath = hours
hourComboBox.SelectedValuePath = hours
hourComboBox.SelectedValue = hours
minutesComboBox.ItemsSource = minutesChoices
minutesComboBox.DisplayMemberPath = minutes
minutesComboBox.SelectedValuePath = minutes
minutesComboBox.SelectedValue = minutes
Select Case selectedTime.Minute
Case 0 To 14
minutesComboBox.SelectedIndex = 0
Case 15 To 29
minutesComboBox.SelectedIndex = 1
Case 30 To 44
minutesComboBox.SelectedIndex = 2
Case 45 To 59
minutesComboBox.SelectedIndex = 3
End Select
hourComboBox.SelectedIndex = selectedTime.Hour
End Sub
End Class
If I change the selectedTime property for example by
selectedTime = New Date(2000,1,1,23,59,0)
the value does not change in the datagrid. It is the same problem the other way around. I do not get the startTime values to the wpf control.
It really seems like the binding is not working, even though the application compiles and runs error free.
Can somebody help me?
Kind regards

Related

VBNet: List of items inside a class, I cant change the value for a single item

My Problem:
I have a Class and a list of other class inside:
Public Class Signal_Type_Read
Private c_signal_count As Integer = 0 ' counter for read signals
Private _items As List(Of Signal_Item)
Private item As New Signal_Item
Sub add_sig()
c_signal_count += 1
items.Add(item)
End Sub
Public Property items() As List(Of Signal_Item)
Get
Return _items
End Get
Set(value As List(Of Signal_Item))
_items = value
End Set
End Property
Function item_counter() As Integer
item_counter = c_signal_count
End Function
Public Sub New()
_items = New List(Of Signal_Item)
End Sub
End Class
Public Class Signal_Item
Private _original_name As String
Public Property Original_name() As String
Get
Return _original_name
End Get
Set(value As String)
_original_name = value
End Set
End Property
'Many other properties
End Class
My problem is when I use in a loop
Public Shared ReadSignals As New Signal_Type_Read
//Part of a Loop to read cells values and store in the variable
ReadSignals.add_sig()
Dim c_index As Integer = ReadSignals.item_counter - 1
ReadSignals.items.item(c_index).Original_name = c_row.Cells(e_Signame).Value
It always changes the "Original_name" Property in all items of my Variable. Where is my error? I want only that oe item is changed.
I found the cause of the problem... I need to create a new instane of item in my ADD_sig() sub
Sub add_sig()
Dim s_item As New Signal_Item
c_signal_count += 1
items.Add(s_item)
End Sub

Create table dynamically in WPF (VB.Net)

I want to create an editable table and it size is defined by user (size can be 10*10 or 20*20 or 30*30).
--
I found this topic (here) but it's running in WinForms, and the DataGridView is not supported by WPF.
I tried with a DataGrid, but the following row doesn't working :
Me.DataGridTableau.ItemsSource = dt
--
I tried with a RadGridView (Telerik) but rows are only updatable by ItemsSource property, and like I don't know how many columns will be, I can't create an object which represents the table (x properties for x columns).
Can anybody help me?
You can set the ItemsSource of a DataGrid to any IEnumerable, including a DataView of a DataTable:
Me.DataGridTableau.ItemsSource = dt.DefaultView
If anybody need it, I found a solution using a RadGridView (Telerik) :
Create this class :
Imports System.Dynamic
Imports System.Collections.Generic
Imports System.ComponentModel
Public Class MyDataRow
Inherits DynamicObject
Implements INotifyPropertyChanged
ReadOnly data As IDictionary(Of String, Object)
Public Sub New()
data = New Dictionary(Of String, Object)()
End Sub
Public Sub New(ByVal source As IDictionary(Of String, Object))
data = source
End Sub
Public Overrides Function GetDynamicMemberNames() As IEnumerable(Of String)
Return data.Keys
End Function
Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder, ByRef result As Object) As Boolean
result = Me(binder.Name)
Return True
End Function
Public Overrides Function TrySetMember(ByVal binder As SetMemberBinder, ByVal value As Object) As Boolean
Me(binder.Name) = value
Return True
End Function
Default Public Property Item(ByVal columnName As String) As Object
Get
If data.ContainsKey(columnName) Then
Return data(columnName)
End If
Return Nothing
End Get
Set(ByVal value As Object)
If Not data.ContainsKey(columnName) Then
data.Add(columnName, value)
OnPropertyChanged(columnName)
Else
If data(columnName) <> value Then
data(columnName) = value
OnPropertyChanged(columnName)
End If
End If
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Sub OnPropertyChanged(name As String)
Try
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
Catch
If Debugger.IsAttached Then Debugger.Break()
Throw ' rethrow exception
End Try
End Sub
Protected Sub OnPropertyChanged(event_args As PropertyChangedEventArgs)
RaiseEvent PropertyChanged(Me, event_args)
End Sub
End Class
In your VM, you need a public property :
Private _tableau As ObservableCollection(Of MyDataRow)
Public Property Tableau() As ObservableCollection(Of MyDataRow)
Get
Return _tableau
End Get
Set(ByVal value As ObservableCollection(Of MyDataRow))
_tableau = value
OnPropertyChanged("Tableau")
End Set
End Property
You need to create a method to load your table :
Private Function LoadTableau() As ObservableCollection(Of MyDataRow)
Dim taille As Integer = Me.GetTailleTableau()
If taille = 0 Then Return Nothing
Dim data As New ObservableCollection(Of MyDataRow)()
For i As Integer = 0 To (taille - 1)
Dim row = New MyDataRow()
For j As Integer = 0 To (taille - 1)
'row(String.Format("Column{0}", j)) = String.Format("Cell {0} {1}", i, j)
row(j) = ""
Next
data.Add(row)
Next
Return data
End Function
You need to load your table :
Me.Tableau = Me.LoadTableau()
And you need to bind your table :
<telerik:RadGridView x:Name="RadGridViewTableau" ItemsSource="{Binding Tableau}" >
I hope this help :)

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

Using Classes with nested arrays

I'd like to use classes instead of structures in my VBA program, but could not figure it out. Below is an example of what I'm doing, and would be grateful for any advice. Maybe classes are not good for this type of thing, because it did not seem very intuitive to me, I don't know.
Option Explicit
Public Type xYear
month(1 To 12) As Double ' Index is the month
End Type
Public Type Company
Name As String
City As String
Sales(2010 To 2020) As xYear ' Index is the year
End Type
Public SuperData(1 To 50) As Company ' An array of companies with monthly sales
Sub Test_Table()
Dim Company1_Name As String
Dim Company1_City As String
Dim Company1_2011_Sales(1 To 12) As Double
Dim Company1_2012_Sales(1 To 12) As Double
Dim Toledo_Sales_Jul_2012 As Double
' Test Data
Company1_Name = "ABC"
Company1_City = "Toledo"
Company1_2011_Sales(7) = 1000
Company1_2012_Sales(7) = 2000
' Copy test data into Structure
SuperData(1).Name = Company1_Name
SuperData(1).City = Company1_City
SuperData(1).Sales(2011).month(7) = Company1_2011_Sales(1) ' Jul 2011 sales
SuperData(1).Sales(2012).month(7) = Company1_2012_Sales(7) ' Jul 2012 sales
' Query the structure
Toledo_Sales_Jul_2012 = City_Sales("Toledo", 7, 2012)
End Sub
Public Function City_Sales(ByRef City As String, ByRef m As Double, ByRef y As Double) As Double
Dim c As Double
For c = LBound(SuperData) To UBound(SuperData)
If City = SuperData(c).City Then
City_Sales = City_Sales + SuperData(c).Sales(y).month(m)
End If
Next
End Function
I would do this with four classes: CCompany and CSale and collection classes for both.
CCompany:
Private mlCompanyID As Long
Private msCompanyName As String
Private msCity As String
Private mclsSales As CSales
Private mlParentPtr As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(dest As Any, Source As Any, ByVal bytes As Long)
Public Property Set Sales(ByVal clsSales As CSales): Set mclsSales = clsSales: End Property
Public Property Get Sales() As CSales: Set Sales = mclsSales: End Property
Public Property Let CompanyID(ByVal lCompanyID As Long): mlCompanyID = lCompanyID: End Property
Public Property Get CompanyID() As Long: CompanyID = mlCompanyID: End Property
Public Property Let CompanyName(ByVal sCompanyName As String): msCompanyName = sCompanyName: End Property
Public Property Get CompanyName() As String: CompanyName = msCompanyName: End Property
Public Property Let City(ByVal sCity As String): msCity = sCity: End Property
Public Property Get City() As String: City = msCity: End Property
Public Property Get Parent() As CCompanies: Set Parent = ObjFromPtr(mlParentPtr): End Property
Public Property Set Parent(obj As CCompanies): mlParentPtr = ObjPtr(obj): End Property
Private Function ObjFromPtr(ByVal pObj As Long) As Object
Dim obj As Object
CopyMemory obj, pObj, 4
Set ObjFromPtr = obj
' manually destroy the temporary object variable
' (if you omit this step you'll get a GPF!)
CopyMemory obj, 0&, 4
End Function
Private Sub Class_Initialize()
Set mclsSales = New CSales
End Sub
Private Sub Class_Terminate()
Set mclsSales = Nothing
End Sub
CCompanies:
Private mcolCompanies As Collection
Private Sub Class_Initialize()
Set mcolCompanies = New Collection
End Sub
Private Sub Class_Terminate()
Set mcolCompanies = Nothing
End Sub
Public Property Get NewEnum() As IUnknown
Set NewEnum = mcolCompanies.[_NewEnum]
End Property
Public Sub Add(clsCompany As CCompany)
If clsCompany.CompanyID = 0 Then
clsCompany.CompanyID = Me.Count + 1
End If
Set clsCompany.Parent = Me
mcolCompanies.Add clsCompany, CStr(clsCompany.CompanyID)
End Sub
Public Property Get Company(vItem As Variant) As CCompany
Set Company = mcolCompanies.Item(vItem)
End Property
Public Property Get Count() As Long
Count = mcolCompanies.Count
End Property
CSale:
Private mlSaleID As Long
Private mdAmount As Double
Private mlYear As Long
Private mlMonth As Long
Private mlParentPtr As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
(dest As Any, Source As Any, ByVal bytes As Long)
Public Property Let SaleID(ByVal lSaleID As Long): mlSaleID = lSaleID: End Property
Public Property Get SaleID() As Long: SaleID = mlSaleID: End Property
Public Property Let Amount(ByVal dAmount As Double): mdAmount = dAmount: End Property
Public Property Get Amount() As Double: Amount = mdAmount: End Property
Public Property Let Year(ByVal lYear As Long): mlYear = lYear: End Property
Public Property Get Year() As Long: Year = mlYear: End Property
Public Property Let Month(ByVal lMonth As Long): mlMonth = lMonth: End Property
Public Property Get Month() As Long: Month = mlMonth: End Property
Public Property Get Parent() As CSales: Set Parent = ObjFromPtr(mlParentPtr): End Property
Public Property Set Parent(obj As CSales): mlParentPtr = ObjPtr(obj): End Property
Private Function ObjFromPtr(ByVal pObj As Long) As Object
Dim obj As Object
CopyMemory obj, pObj, 4
Set ObjFromPtr = obj
' manually destroy the temporary object variable
' (if you omit this step you'll get a GPF!)
CopyMemory obj, 0&, 4
End Function
CSales:
Private mcolSales As Collection
Private Sub Class_Initialize()
Set mcolSales = New Collection
End Sub
Private Sub Class_Terminate()
Set mcolSales = Nothing
End Sub
Public Property Get NewEnum() As IUnknown
Set NewEnum = mcolSales.[_NewEnum]
End Property
Public Sub Add(clsSale As CSale)
If clsSale.SaleID = 0 Then
clsSale.SaleID = Me.Count + 1
End If
Set clsSale.Parent = Me
mcolSales.Add clsSale, CStr(clsSale.SaleID)
End Sub
Public Property Get Sale(vItem As Variant) As CSale
Set Sale = mcolSales.Item(vItem)
End Property
Public Property Get Count() As Long
Count = mcolSales.Count
End Property
Public Sub AddSale(ByVal dAmount As Double, ByVal lYear As Long, ByVal lMonth As Long)
Dim clsSale As CSale
Set clsSale = New CSale
With clsSale
.Amount = dAmount
.Year = lYear
.Month = lMonth
End With
Me.Add clsSale
End Sub
Then in a standard module.
Sub Test_Class()
Dim clsCompanies As CCompanies
Dim clsCompany As CCompany
Dim clsSale As CSale
Set clsCompanies = New CCompanies
Set clsCompany = New CCompany
clsCompany.CompanyName = "ABC"
clsCompany.City = "Toledo"
'Verbose way to add a sale
Set clsSale = New CSale
clsSale.Amount = 1000
clsSale.Year = 2011
clsSale.Month = 7
clsCompany.Sales.Add clsSale
'Quickway to add a sale
clsCompany.Sales.AddSale 2000, 2012, 7
clsCompanies.Add clsCompany
For Each clsCompany In clsCompanies
For Each clsSale In clsCompany.Sales
Debug.Print clsCompany.CompanyName, clsCompany.City, clsSale.Amount, clsSale.Year, clsSale.Month
Next clsSale
Next clsCompany
End Sub
This uses some undocumented features, such as to be able to use For Each on a custom class. Here are a couple of references for you.
http://dailydoseofexcel.com/archives/2010/07/09/creating-a-parent-class/
http://www.cpearson.com/excel/classes.aspx

How to retrieve column items from listview in code

I am new to wpf and am going for an MCTS exam. I have searched for 2 days now on how to retrieve row column items in code. I have been able to insert data into the listview by creating a structure and adding row items via code.
Public Structure SimpleData
Public Property Txt1 As String
Get
Return mTxt1
End Get
Set(value As String)
mTxt1 = value
End Set
End Property
Private mTxt1 As String
Public Property Txt2 As String
Get
Return mTxt2
End Get
Set(value As String)
mTxt2 = value
End Set
End Property
Private mTxt2 As String
Public Property Txt3 As String
Get
Return mTxt3
End Get
Set(value As String)
mTxt3 = value
End Set
End Property
Private mTxt3 As String
End Structure
Public Structure MyData
Public Property Desc() As String
Get
Return m_Desc
End Get
Set(value As String)
m_Desc = Value
End Set
End Property
Private m_Desc As String
Public Property Progress() As Integer
Get
Return m_Progress
End Get
Set(value As Integer)
m_Progress = Value
End Set
End Property
Private m_Progress As Integer
Public Property ProgressText() As String
Get
Return m_ProgressText
End Get
Set(value As String)
m_ProgressText = Value
End Set
End Property
Private m_ProgressText As String
Public Property Pic() As String
Get
Return m_Pic
End Get
Set(value As String)
m_Pic = Value
End Set
End Property
Private m_Pic As String
End Structure
Private Sub Button2_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button2.Click
Dim sd As New SimpleData
sd.Txt1 = "Today is"
sd.Txt2 = "a good day"
sd.Txt3 = "O YES!"
listView1.Items.Add(sd)
End Sub
I want to be able to retrieve row(0).Item(0).ToString, which is how to retrieve it in win forms. Expecting a response. Thanks in advance
Dim constr As String = "Put your connection string here"
Dim ds As New DataSet
Dim con As New SqlClient.SqlConnection(constr)
con.Open()
Dim sqladap As New SqlClient.SqlDataAdapter("select * from tbl_Employee", con)
sqladap.Fill(ds)
For i As Integer = 0 To ds.Tables(0).Columns.Count - 1
ListView1.Columns.Add(ds.Tables(0).Columns(i).ToString())
Next
For i As Integer = 0 To ds.Tables(0).Rows.Count - 1
Dim listRow As New ListViewItem
listRow.Text = ds.Tables(0).Rows(i)(0).ToString()
For j As Integer = 1 To ds.Tables(0).Columns.Count - 1
listRow.SubItems.Add(ds.Tables(0).Rows(i)(j).ToString())
Next
ListView1.Items.Add(listRow)
Next
Read data from Listview :
Dim name, room, subject, date, period As String
If listviewName.SelectedItems.Count > 0 then
For i As Integer = 0 To listviewName.SelectedItems.Count - 1
'*********** transfer selected data on declare String variable ************'
name= listviewName.SelectedItems(i).SubItems(0).Text
room = listviewName.SelectedItems(i).SubItems(1).Text
subject = listviewName.SelectedItems(i).SubItems(2).Text
date= listviewName.SelectedItems(i).SubItems(3).Text
period= listviewName.SelectedItems(i).SubItems(4).Text
'*********** delete **************'
cmd1.Connection = MYSQLCON
MYSQLCON.Open()
cmd1.CommandText = "DELETE FROM tablename WHERE columnname = '" & name & "'"
reader = cmd1.ExecuteReader
MYSQLCON.Close()
Next
End If
I have found the answer by casting the listview item to the created structure SimpleData then looping through it
Dim getitems = CType(listView1.SelectedItem, SimpleData)
For Each mem In getitems.Txt1
MsgBox(mem.ToString)
Next

Resources