Data Binding Question

G

Guest

Think it is great the way that you can set up a datsource, value member, and
display member for a combobox, and map a 'code' to a 'description' and back
again by binding the combobox to a datasource that contains the code. Now
suppose that you want it to be read-only? That is, you have the 'code' in
your DB, want the associated 'description' to display, but not let the user
change it on this particular form. There is no read-only property for a
combobox like with a textbox, and there are no datasource, display member,
value member properties for a textbox or label controls. Is there some easy
way to accomplish this? The only way I can think to do this is to use the
binding class 'format' event to lookup the description myself. Any other
ideas?
 
G

Guest

The user can still change the selected index. And while I could change the
'Data Source Update Mode' (under advanced binding) to Never, this would be
confusing. Really want to show it in a label or readonly text box so the
user is aware that he/she can not change it (here).
 
C

Cor Ligthert[MVP]

Terry,

Why are you then binding it, just fill the itemarray with the fields you
want and you are ready.

This needs really only the most simple for each loop there is.

Cor
 
L

Linda Liu[MSFT]

Hi Terry,

If we could custom draw the input box part of a ComboBox as well as its
items in the drop down list, a possible workaround may be custom drawing
the ComboBox to get what you want.

Unfortunately, we can not custom draw the input box part of a ComboBox.

IMO, the solution to use the Binding class's Format event to look up the
description youself you have mentioned in your first reply is good. In
addition, I think it would be better to create a custom control to
encapsulate the above logic. Then you could use this custom control
wherever you want.

The following is a sample of this custom control. Note that you shoud set
the DataSource, ValueMember and DisplayMember properties of the derived
Label before bind the Text property of the control to a data source.

Public Class MyLabel
Inherits Label

Private _datasource As IList
Private _valuemember As String
Private _displaymember As String

Public Property DataSource() As IList
Get
Return _datasource
End Get
Set(ByVal value As IList)
_datasource = value
End Set
End Property

Public Property ValueMember() As String
Get
Return _valuemember
End Get
Set(ByVal value As String)
_valuemember = value
End Set
End Property

Public Property DisplayMember() As String
Get
Return _displaymember
End Get
Set(ByVal value As String)
_displaymember = value
End Set
End Property

Sub New()
AddHandler Me.DataBindings.CollectionChanged, AddressOf
DataBindings_CollectionChanged
End Sub

Private Sub DataBindings_CollectionChanged(ByVal sender As Object,
ByVal e As System.ComponentModel.CollectionChangeEventArgs)
Dim b As Binding = CType(e.Element, Binding)
If (Not (b Is Nothing)) Then
If (b.PropertyName = "Text") Then
If (e.Action = CollectionChangeAction.Add) Then
AddHandler b.Format, AddressOf b_Format
b.ReadValue()
ElseIf (e.Action = CollectionChangeAction.Remove) Then
RemoveHandler b.Format, AddressOf b_Format
End If
End If
End If
End Sub

Private Sub b_Format(ByVal sender As Object, ByVal e As
ConvertEventArgs)
If (Not (_datasource Is Nothing) And _datasource.Count > 0) Then
Dim pdc As PropertyDescriptorCollection =
TypeDescriptor.GetProperties(_datasource(0))
For i As Integer = 0 To _datasource.Count - 1
If (pdc(_valuemember).GetValue(_datasource(i)).ToString() =
e.Value.ToString()) Then
e.Value = pdc(_displaymember).GetValue(_datasource(i))
Exit For
End If
Next
Else
Throw New Exception("Please set the DataSource, DisplayMember
and ValueMember properties first")
End If
End Sub

End Class

Sincerely,
Linda Liu
Microsoft Online Community Support

==================================================
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for non-urgent issues
where an initial response from the community or a Microsoft Support
Engineer within 1 business day is acceptable. Please note that each follow
up response may take approximately 2 business days as the support
professional working with you may need further investigation to reach the
most efficient resolution. The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================

This posting is provided "AS IS" with no warranties, and confers no rights.
 
C

Cor Ligthert[MVP]

Linda,

That was my first thought too, but is this not a little bit long solution
while you have direct the result by filling the itemarray?

:)

Cor
 
G

Guest

Hi Linda,
I was able to accomplish what I wanted by using the Format event. As a
learnig exercise for myself, I am going to try the custom control aproach
that you have suggested here. I have never done this before and have a few
general questions for you. I realize I could just add the code you have
given me here as a new class in the project and then reference it through
code in my form. What approach do I take to make it available to other
projects? Should I build a seperate project for it? If so, is it a class
library or a windows control project? How would I get it available in the
toolbox so that I can drop and position it on my form? Maybe you could point
me to something I could read.
Thanks for all your help!
 
L

Linda Liu[MSFT]

Hi Terry,

Thank you for your feedback!

You could create a Windows Control Library project for the custom control
and build the control library project, which generates an assembly(a .dll
file). Then you can add a reference to the assembly in other WinForm
application projects to use the custom control.

To add the custom control into the Toolbox, right click on the Toolbox and
choose 'Choose Items..'. In the 'Choose Toolbox Items' dialog, switch to
the '.NET Framework Components' tab and click the Browse button. Navigate
to the assembly containing the custom control and press Open button. The
custom control should appear in the listbox with the checkbox before it
selected. Press OK to close the dialog.

For more information on authoring a Windows Forms control, you may refer to
the following MSDN document:
'Developing Windows Forms Controls at Design Time'
http://msdn2.microsoft.com/en-us/library/w29y3h59.aspx

Hope this helps.
If you have any question, please feel free to let me know.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

Hi Linda,
I finally had a few moments to give this 'Custom Control' approach a try
and am having a few issues.
1) When I try to set the DataSource property in the properties page, I get a
"Object Collection Editor" dialog that wants me to add system.object's to a
collection. Not what I had in mind. So I decided to set the datasource in
code to the bindingsource I am using for this purpose. Which led me too....
2)I get a null reference exception in the line....
If (pdc(_valuemember).GetValue(_datasource(i)).ToString() =
e.Value.ToString()) Then
_datsource is a bindingsource and has 7 elements. pdc has 2, yet if I type
?pdc.count in the immediate window it says a null pointer was returned, but
when I hover the mouse over it it says its count is 2. I think (guess) the
problem is with 'GetValue' which is marked as 'MustOverride'. What am I
missing?

I tried changing the type of _datasource from Ilist to BindingSource. That
allowed me to set the DataSource in the properties window, but I still got
the same null exception on the same line.

Thanks again for all your help.
 
L

Linda Liu[MSFT]

Hi Terry,

About your first question, you could add some attributes on the DataSource,
DisplayMember and ValueMember properties of the custom control to make the
design time behavior of these three properties like that of the DataSource,
DisplayMember and ValueMember properties of a ComboBox.

The following is a sample:

Imports System.Drawing.Design

Public Class MyLabel
Inherits Label
<DefaultValue(CType(Nothing, String)),
AttributeProvider(GetType(IListSource))> _
Public Property DataSource() As IList
...
End Property

<Editor("System.Windows.Forms.Design.DataMemberFieldEditor,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a", _
GetType(UITypeEditor)), DefaultValue("")> _
Public Property ValueMember() As String
...
End Property

<DefaultValue(""), _
TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter,
System.Design, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"),
Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
GetType(UITypeEditor))> _
Public Property DisplayMember() As String
...
End Property
...
End Class

Build the project and open the form containing the custom control in the
designer. Select the custom control on the form and go to the DataSource
entry in the Properties window, a drop down arrow should appear on the
right hand. If you click it, a drop down window should open with all data
sources available in the project listed in it. Then go to the DisplayMember
or ValueMember entry in the Properties window and click the drop down arrow
on the right, and you should see a drop down window opening with all
properites available in the selected data source.

About your second question, are all the objects in the data source of the
same type? If the problem is still not solved, please send me a simple
project that could just reproduce the problem. To get my actual email
address, remove 'online' from my displayed email address.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

Hi Linda,
First, let me thank you for the ‘Attribute’ stuff – worked like a charm!
When I get a chance I will look at it some more to figure out what it does!
Now for the second problem, I have figured out what was going on, but I’m
not sure of the best way to fix the problem. Let me try to explain. I am
using custom classes, and the main one is called Subscription. There are
several ‘codes’ for things like status, tax exemption code, cancel code etc.
Some of these can be ‘empty’, Null. My ‘lookup’ tables (code/description)
have entries like (Null/<None>) or (Null/No Exemption) etc. This all works
fine when binding to a normal combobox, but with the ‘boundlabel’ you helped
me create, I get the Null reference exception I mentioned earlier on the line:
If (pdc(_valuemember).GetValue(_datasource(i)).ToString() =
e.Value.ToString) Then
Due to e.value being ‘Nothing’. So let me take you through the steps as the
record comes from the DB. IN this case we are dealing with the CancelCode.
The Subscription record is retrieved; the class instantiated and the
properties are being set:
….
..CancelCode = dr.Item(CN_CancelCode).ToString

The property looks like…

Private _CancelCode As String
Public Property CancelCode() As String
Get
Return _CancelCode
End Get
Set(ByVal value As String)
If (_CancelCode <> value) Then
Me.DataStateChanged(EntityStateEnum.Modified)
_CancelCode = value
End If
End Set
End Property

_CancelCode is Nothing, Value is “â€, and _CancelCode<>Value is False! ie.
Nothing = Ҡand so CancelCode remains Nothing.

So later we get the exception when the routine tries to look up the code.
The left side (pdc(_valuemember…..) evaluates to “†(DBNull tostring), while
the right side is Nothing (tostring) which throws the exception. Seems like
in some places, Nothing and Ҡare the same and in others they are not!

So I changed the declaration of the backing variable to
_CancelCode as String = “â€
this fixed the problem (here) but when I now bind the field to a real
combobox, the Ҡdoes not match the lookup table! I finally worked around
the problem by adding
if e.value = Nothing then
e.value = “â€
end if
as the first thing in the parsing routine. This works but seems like a
hack. The combobox is able to match a ‘Nothing’ coming from the property and
a DBNull coming from the DataTable. Have any ideas?
 
L

Linda Liu[MSFT]

Hi Terry,

Thank you for your reply!

I understand your second question now.

Firstly, the reason why " Nothing = "" " returns true is that string
comparisons treat Nothing as "" (an empty string).

Secondly, it seems that the internal implementation of the ComboBox regards
Nothing equal to DBNull.Value.

So we can modify the code in the b_Format method in the derived Label class
as follows to get the same behavior of the ComboBox:

Public Class MyLabel
Inherits Label
...
Private Sub b_Format(ByVal sender As Object, ByVal e As ConvertEventArgs)
If (Not (_datasource Is Nothing) And _datasource.Count > 0) Then
Dim pdc As PropertyDescriptorCollection =
TypeDescriptor.GetProperties(_datasource(0))
For i As Integer = 0 To _datasource.Count - 1
Dim valueinDS As Object =
pdc(_valuemember).GetValue(_datasource(i))
If ((valueinDS Is Nothing Or valueinDS Is DBNull.Value) And
(e.Value Is Nothing Or e.Value Is DBNull.Value)) Then
e.Value = pdc(_displaymember).GetValue(_datasource(i))
Exit For
ElseIf (valueinDS IsNot Nothing And valueinDS IsNot
DBNull.Value And e.Value IsNot Nothing And e.Value IsNot DBNull.Value) Then
If (valueinDS.ToString() = e.Value.ToString()) Then
e.Value =
pdc(_displaymember).GetValue(_datasource(i))
Exit For
End If
End If
Next
Else
Throw New Exception("Please set the DataSource, DisplayMember
and ValueMember properties first")
End If
End Sub

End Class

Please try it in your project to see if it solves the problem and let me
know the result.

Sincerely,
Linda Liu
Microsoft Online Community Support
 
G

Guest

Hi Linda,
Thanks! Works like a charm! I did make one small change, which I will
share...
If (Not (_datasource Is Nothing) AndAlso _datasource.Count > 0) Then
| so the second half
wont be evaluated if nothing

If you are in the US - Happy Thanksgiving! If not, and in any case - Have a
Great Day and thanks again for all your help!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top