Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations bkrike on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

oops help: generic databinding.format for numbers 1

Status
Not open for further replies.

misterstick

Programmer
Apr 7, 2000
633
GB
i'm fairly new to c#, so please forgive me if i don't explain this clearly.

i have a form which displays data from an oracle database.

each column of the table is displayed in its own textbox object.

i'd like numbers to be displayed with an appropriate format; eg a currency value to be to two decimal places, or "#,##0.00".

i have bound the databinding("text").format event to a custom function, which formats the text in the way i want.

however, my first guess has been to create one function for each format, viz FormatNumber0DP, FormatNumber1DP, etc.

this works, but isn't very concise.

my question is, how can i pass the required format to the custom function, so that i can use just the one for all my textboxes, no matter what format they need to be in.

thanks,


mr s. <;)

 
thank you for the article link.

i think i must be missing something.

the decimaltocurrency delegate formats a decimal value as currency.

what i'm asking about is how to use just the one delegate function to format any datatype using a supplied format string.

i would like somehow to pass the format to the function as a parameter, or to retrieve it from the calling object, using something like either a "Format" or a "DecimalPlaces" property .

any ideas?

mr s. <;)

 
I think the trick part of that is that you would need reflection to determine what you are dealing with Decimal, int, Datetime, etc

here's what you could do. Totally unproven
1) Create this class:
Public Class CustomBinding
Inherits System.Windows.Forms.Binding

Private mFormatString As String

Sub New(ByVal propertName As String, ByVal dataSource As Object, ByVal dataMember As String, ByVal formatString As String)
MyBase.New(propertName, dataSource, dataMember)

mFormatString = formatString
End Sub

Public ReadOnly Property FormatString()
Get
Return mFormatString
End Get
End Property

End Class


2) Here are the functions that wraps the databinding. I had them in my BaseForm

Protected Sub BindFieldCustom(ByVal control As Control, ByVal PropertyName As String, _
ByVal DataSource As Object, ByVal DataMember As String, ByVal formatString As String)

Dim bd As CustomBinding
Dim i As Integer
For i = control.DataBindings.Count - 1 To 0 Step -1
bd = control.DataBindings.Item(i)
If bd.PropertyName = PropertyName Then
control.DataBindings.Remove(bd)
End If
Next

bd = New CustomBinding(PropertyName, DataSource, DataMember, formatString)

control.DataBindings.Add(bd)

End Sub
Protected Sub BindFieldCustom(ByVal control As Control, ByVal PropertyName As String, _
ByVal DataSource As Object, ByVal DataMember As String, ByVal formatString As String, _
ByVal bdFormatDelate As ConvertEventHandler, _
ByVal bdParseDelate As ConvertEventHandler)

Dim bd As CustomBinding
Dim i As Integer
For i = control.DataBindings.Count - 1 To 0 Step -1
bd = control.DataBindings.Item(i)
If bd.PropertyName = PropertyName Then
control.DataBindings.Remove(bd)
End If
Next

bd = New CustomBinding(PropertyName, DataSource, DataMember, formatString)

AddHandler bd.Format, bdFormatDelate
AddHandler bd.Parse, bdParseDelate

control.DataBindings.Add(bd)

End Sub

Protected Sub BindField(ByVal control As Control, ByVal PropertyName As String, _
ByVal DataSource As Object, ByVal DataMember As String)
Dim bd As Binding
Dim i As Integer
For i = control.DataBindings.Count - 1 To 0 Step -1
bd = control.DataBindings.Item(i)
If bd.PropertyName = PropertyName Then
control.DataBindings.Remove(bd)
End If
Next

control.DataBindings.Add(PropertyName, DataSource, DataMember)

End Sub

Protected Sub BindField(ByVal control As Control, ByVal PropertyName As String, _
ByVal DataSource As Object, ByVal DataMember As String, _
ByVal bdFormatDelate As ConvertEventHandler, _
ByVal bdParseDelate As ConvertEventHandler)

Dim bd As Binding
Dim i As Integer
For i = control.DataBindings.Count - 1 To 0 Step -1
bd = control.DataBindings.Item(i)
If bd.PropertyName = PropertyName Then
control.DataBindings.Remove(bd)
End If
Next

bd = New Binding(PropertyName, DataSource, DataMember)

AddHandler bd.Format, bdFormatDelate
AddHandler bd.Parse, bdParseDelate

control.DataBindings.Add(bd)

End Sub



3) Here is the formatting and Parsing
Protected Sub FormatText(ByVal sender As Object, ByVal cevent As ConvertEventArgs)

Try
'THIS IS UNFINISHED
'YOU WILL NEED A CASE STATEMENT TO DETERMINE WHAT TYPE OF OBJECT YOU HAVE
'Here is an example data format
cevent.Value = CDate(cevent.Value).ToString(CType(sender, CustomBinding).FormatString())
Catch
cevent.Value = Date.Parse("1/1/1900").ToShortDateString
End Try
' DisplayBrokenRules()
End Sub

Protected Sub ParseText(ByVal sender As Object, ByVal cevent As ConvertEventArgs)
Try
'THIS IS UNFINISHED
'AGAIN You need a case statement to handle different types
'This only handles Dates
If CStr(cevent.Value) = "" Then
cevent.Value = "1/1/1900"
Else
cevent.Value = CDate(cevent.Value)
End If
Catch
cevent.Value = sd.DBValue
End Try

End Sub

4) Example Call
BindFieldCustom(txtOrderDate, "Text", mOrderData, "OrderDate", _
"d", _
New ConvertEventHandler(AddressOf FormatText), _
New ConvertEventHandler(AddressOf ParseText))
 
An elegant solution is to write and extender for the TextBox control using the System.ComponentModel.IExtenderProvider.
Let refer this class by CurrencyFormatSelector.
You add it to the Toolbox ,you will drag this control on your form and for every TextBox you will add on the form you can choose at design time the currency format.
The responsibility of the currency format will be encapsulated in the extender and a TextBox will be formated according the format associated to that textbox.
Here is an example that you can develop . If you find it hard then see a second solution.

Code:
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;


namespace CurrencyFormatSelector
{
	
	public class TextExtendedProperties
	{
		
		public string m_CyFormat;
	}
	/// <summary>
	/// Summary description for CurrencyFormatSelector.
	/// </summary>
	[ProvideProperty("CurrencyFormat",typeof(System.Windows.Forms.Control))]
	public class CurrencyFormatSelector : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider
	{
		/// <summary>
		/// Required designer variable.
		/// </summary>
		public Hashtable ht = new Hashtable();
		private System.ComponentModel.Container components = null;
		
		private Hashtable htTextProps = new Hashtable(); // to hold each property we want to validate
		private bool m_isEnabled = true; // enable or disable the CurrencyFormatSelector
		public CurrencyFormatSelector(System.ComponentModel.IContainer container)
		{
			
			///
			/// Required for Windows.Forms Class Composition Designer support
			///
			container.Add(this);
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		
		
		}

		public CurrencyFormatSelector()
		{
			///
			/// Required for Windows.Forms Class Composition Designer support
			///
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}


		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
		public bool CanExtend( object extendee)
		{
			if (extendee.GetType()== typeof(System.Windows.Forms.TextBox))
			{
				AddPropertyValue(extendee); 
				return true;
			}
			else 
				return false;
		}
		[Description("Enable currency format validation on all TextBox control?")]
		[Category("Currency Format Selector")]
		[DefaultValue(false)]
		public bool Enabled
		{
			get {return m_isEnabled;}
			set{m_isEnabled=value;}
		}
		
		
		
		// CurrencyFormat (set &get)
		[Description("Predefined Currency Formats ...")]
		[Category("Currency Format Selector")]
		[DefaultValue("None")]
		public string GetCurrencyFormat( System.Windows.Forms.Control ctrl)
		{
			if (htTextProps.Contains(ctrl))
			{
				return ((TextExtendedProperties)htTextProps[ctrl]).m_CyFormat;
			}		
			else
				return "None";
		}
		public void SetCurrencyFormat( System.Windows.Forms.Control ctrl, string val)
		{
			AddPropertyValue(ctrl).m_CyFormat = val;
		}
		
         
		public TextExtendedProperties AddPropertyValue(object ctrl)
		{
			if (htTextProps.Contains(ctrl))
			{
				return (TextExtendedProperties)(htTextProps[ctrl]);
			}
			else
			{
				TextExtendedProperties texp = new TextExtendedProperties();
				htTextProps.Add(ctrl,texp);
				((System.Windows.Forms.Control)ctrl).Validating+=new System.ComponentModel.CancelEventHandler(TextBoxCyFormatHandler);
				return texp;
			}
		}
		private void TextBoxCyFormatHandler(object sender, System.ComponentModel.CancelEventArgs e)
		{
			if (m_isEnabled == false)
				return;
			System.Windows.Forms.Control ctrl = (System.Windows.Forms.Control)sender;
			ValidateProps(ctrl);
			
		}
		private void ValidateProps (System.Windows.Forms.Control ctrl)
		{
			// extract the class from the hash table and evaluate the properties
			TextExtendedProperties props = (TextExtendedProperties)htTextProps[ctrl];
			if (!props.m_CyFormat.Equals("None"))
			{
				// Format here the text of the TextBox depending on the
				// props.m_CyFormat
				ctrl.Text= ...;
			}
			
			
			
		}

	}
}
Another solution is to inherit MyTextBox: System.Windows.Forms.TextBox and add there:
public string m_CyFormat;
Next add the MyTextBox controls on your form instead of TextBox and do the followings:
Code:
Binding bnd = new Binding ("Text", m_DataToBound, "FirstName");
bnd.Format+=new ConvertEventHandler(bnd_Format);
textBox1.DataBindings.Add(bnd);

public void bnd_Format (object sender, ConvertEventArgs args)
{
	//for example, sender.m_CyFormat is like "{0:$#,##0.00;($#,##0.00)}"
	string fmt = ((MyTextBox)sender).m_CyFormat; 
	string newTextFormatted = string.Format(fmt,args.Value);
	args.Value=newTextFormatted;
}
Use the same bnd_Format() for all textboxes.
obislavu
 
wow, pretty thorough examples. thanks very much.

it will take me some time to read through them and even longer to understand them, so no stars for a while yet.

what i've gleaned so far is that the sender parameter is the object that fired the event (well, duh).
cast sender to textbox and you can access its properties.
i should derive a class from textbox that has a CustomFormat property; i'll be able to use it in the ide in exactly the same way as a textbox.
pretty damn elegant and just what i was wanting.

rider 1: in the help i've seen qualifiers in square brackets used to communicate with the ide. is there anything i should be aware of before i plough into creating a custom control?

rider 2: is there any difference between using: [tt]string.Format("{0}", format)[/tt] and [tt]object.tostring(format)[/tt]?

again, many thanks.


mr s. <;)

 
ok, so rider 2 should read:

rider 2: is there any difference between using: [tt]string.format("{0:format}", object.value)[/tt] and [tt]object.value.tostring(format)[/tt]?

oops.


mr s. <;)

 
ok, i've read the examples.

suggested solutions:

[ol][li]derive a class from databinding that includes a format, and the functions to do the formatting.[/li]
[li]extend the textbox object using iextenderprovider so that it includes internal formatting.[/li]
[li]derive a class from textbox that includes a format which can be passed to the format and parse events.
[/li][/ol]

the first two are a little complex for me.

in the first case i don't see how the ide will cope with the new databinding object.

the second case seems a little drastic: won't all textboxes have the extra overheads?

i've gone with the third option, it's the one that seems most .nettish and easiest to understand.

i'll ask the extra questions in the forum if i can't work them out for myself.

many thanks again,


mr s. <;)

 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top