How to set DataTable Value to null in WPF [duplicate] - c#

We have a column bound to a sql integer value that is nullable. When the user tries to "clear" the cell in the DataGrid, we get the validation error
"...could not be converted".
How can I make it empty and bind a "null" value to the underlying column?
I have googled this problem for 2 days now and found anything. I tried to edit my comment to you HCL, but it would never save.
I am familiar with the IValueConverters. I wrote one to handle our percentages (so the user can enter "50 %" and it gets converted to ".5"). I am trying to do something very similar, but when the value comes back as a Null (I wrote a dummy IValueConterter to debug it), I just want it to be saved to the database as a Null. Maybe I just need to set it to DBNullValue? It just seems like a lot of work for something that I think might have a built in property. I tried using the TargetNullValue property on the column (DataGridBoundColumn) and setting that to DBNull.Value, but it didn't change the value (based on my dummy IValueConverter it is still coming in as a regular (string) NULL.
I simply want to be able to save a null value to the database for this (integer type) column.
** Latest addition **
Good point HCL, I forget that the whole world isn't ADO sometimes.
I am binding to a SQL table. The column in question is an int that allows NULLs.
The columns are created using AutoGeneratingColumn, so it basically just hooks them up "automagically" (certain styles such as Right Justify are applied in this method, but not for this column). It takes this approach, because the app is pretty much an "Access" replacement. Our CIO mandated we remove Access from the users, so this was the solution (create our own MS Access). Anyway, since it can open any table (and create tables, columns, etc) it just "AutoGenerates" the columns based on the table that was opened. Although, since there are certain columns that the app inherently knows about such as Discount_Pct, when it encounters one of those columns, it does do some "special stuff" (like assign the IValueConverter I described above). Although, like I said... for this particular column, there isn't anything "special" done to it. It is just a regular SQL integer (nullable) that is "AutoGenerated".

I just found the property I was looking for!!
TargetNullValue
This post explains it: HERE
The problem is, these columns are generated using "AutoGenerateColumns" (because the datasource...a sql table... is "dynamic"). If I could figure out how to get the table column (primitive) type, I already know how to see if it is Nullable, then I can set this value for that column. (I still don't understand why the AutoGenerateColumns doesn't handle this automatically!!)

Extending off of Shayne's self-answer, I discovered that the TargetNullValue property is available on DataGridBoundColumn objects. You could use the additional logic described from this comment to make this conditional. Unfortunately, that post does not explain how to relate its logic to a DataGrid, so here is my solution:
<DataGrid AutoGenerateColumns="True" AutoGeneratingColumn="m_grid_AutoGeneratingColumn"/>
And...
public void m_grid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
// Make all columns nullable
((DataGridBoundColumn)e.Column).Binding.TargetNullValue = string.Empty;
}
I was so frustrated with this problem. I hope this helps someone.

I decided to go the IValueConverter route for this field too.
Here is what I wrote (and it works, I still feel like there must be an easier way! lol):
[ValueConversion(typeof(Int32), typeof(String))]
public class IntDBNullConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return "";
else if (DBNull.Value.Equals(value))
return "";
else
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string str = value as string;
if (String.IsNullOrEmpty(str))
return DBNull.Value; // returns DBNull.Value
Int32 result = 0;
Int32.TryParse(str, out result);
return result;
}
}

Related

Hide Columns in DataGridView by field reference not column string name

I am writing a program where data is being displayed using a DataGridView and I was hoping there was a way to access the visible property of the columns without specifying the index location, or Column Name string value.
public class test {
public static string value1 { get; set; }
public static string value2 { get; set; }
}
I am using the LINQ to SQL datacontext to query information to be displayed into my DataGridView.
As it currently is, I can only seem to find a way to change the Columns visible property as so (assuming DataGridView is instantiated as dgvDATA)
dgvDATA.columns["value1"].visible = false;
Is there any way to do something similar to the following? I assume if there is it would be through databindings, but I tried that and couldn't figure it out.
dgvDATA.column.value1.visible = false;
I found this article stating that the DataGridView does not have this kind of ability built in but there was a workaround where you could add a database field representing if you want it visible or not.
HOWEVER the article was written in 2011 so I find it hard to believe that something like this was never implemented.
https://dotnetbuildingblocks.wordpress.com/2011/07/30/binding-datagrid-column-visibility-to-a-datacontext/
Please let me know if this is possible! Thank you
DataGridView doesn't support what you are asking for.
Anyway, if your only goal is to avoid using hardcoded field names, then using nameof() makes sense.
In your case it will be
dgvDATA.Columns[nameof(test.value1)].Visible = false;
That way you will have no issues refactoring your code later.
Apparently you want some kind of special DataGridViewColumn, where columns can be identified using some identification that is not available in a standard DataGridViewcolumn. For example you want to identify them by the PropertyInfo of the property that is shown in the column, or maybe the database column name of the database column whose values you show in this column.
In object oriented programming, if you want to create a special kind of DataGridViewColumn you should write a derived class:
class MySpecialDataGridViewColumn : DataGridViewColumn
{
public string DatabaseColumnName {get; set;}
}
Normally this would be enough: as long as you make sure that you only add MySpecialDataGridViewColumn objects to your DataGridView. When you fetch a column, typecast it to MySpecialDataGridViewColumn.
var theColumnThatDisplaysFirstName = myDataGridView.Columns // get all columns
.Cast<MySpecidalDataGridViewColumn>() // cast to your type
.Where(column => column.DatabaseCollumnName == "FirstName")
.SingleOrDefault(); // keep the ones with "FirstName"
Be aware that others will still be able to Add other kind of columns to your DataGridView. If you are afraid of this, make sure that you keep your members private and add functionality to Add / Fetch / Remove MySpecialDataGridViewColumns.
If your DataGridView is to be used by many, consider creating a UserControl that contains a private DataGridView, with functionalities to Add / Retrieve / Remove MySpecialDataGridViewColumn objects to the user control. This way others can't misuse your `DataGridView by adding other types of columns
Of course, if you want to allow others to add their own kind of Columns, you could always use OfType<MySpecialDataGridViewColumn> instead of a Cast. This way you ignore the other type of added columns, of which you are certain that they don't display your database columns

Accessing Binding from Type Converter?

I've spent a fair bit of time trying a number of different ways to solve an issue I'm having, to no avail, so I'm hoping someone here can help.
I have a Text Box element with Two-Way binding, which utilises a Type Converter to convert the value from a string to a custom Data type, say, MyCustomType. This is working fine, however due to a change in my project's requirements, I now need to perform extra processing prior to the conversion taking place.
In order to perform this extra processing, however, I need to be able to access the "source" text box, or the binding context. Neither of which I have been able to access.
Is there any way to access the source text box, from a Type Converter's ConvertFrom() method?
I have tried to use the ITypeDescriptorContext parameter passed (by WPF) to the ConvertFrom() method, however most of the properties therein are null.
i.e.
public class MyCustomTypeConverter : TypeConverter
{
...
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
// Context is
return new MyCustomType(value);
}
...
}
I have also tried using a MultiValueConverter, and avoiding the Type converter entirely, however this led to a LOT of extra code, and didn't really help. I would prefer to avoid going down this route, as a Type Converter is much more elegant.
Any advice/assistance would be greatly appreciated! :)
EDIT: I ended up changing the way that validation is performed (using INotifyDataError instead of validating on exceptions), and ended up re-writing the ConvertFrom() method in my Type Converter, such that I wouldn't need to access the TypeDescriptor's context anymore.
I wouldn't recommend using the context from the ConvertFrom() method, as it (being a private property) isn't guaranteed that the property will exist in the future (though I haven't read anything to support this, it is best to assume that private properties can be removed/renamed without notification from the MS development team), and it isn't set when setting a property's value programmatically, like so:
TypeConverter converter = TypeDescriptor.GetConverter(typeof(MyCustomType));
converter.ConvertFrom(mySourceValue);
If you're reading this and really need to access the context parameter, you can do so using my method below, at your own risk.
I was able to solve this by interrogating the ValueConverterContext class, and accessing the private _targetElement field, like this:
var sourceTextBox = context.GetType().GetField("_targetElement", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(context)
Thanks for your help nonetheless. :)
edit: To access the Bindings for this TextBox, you can simply cast sourceTextBox as TextBox and then:
var BindingExpression = sourceTextBox.GetBindingExpression(TextBox.TextProperty);

Comparing TimeStamp value in C#

I created a new column in an existing database, to allow me a RowVersion for concurrency operations:
alter table MyTable add ColumnName rowversion
In my class I added the following property
public byte[] ColumnName { get; set; }
In the one method that uses this class i get the error when using this property
Cannot implicitly convert type 'System.Data.Linq.Binary' to 'byte[]'
I overcome this by adding ToArray (myObj.ColumnName.ToArray()) to the property.
In my ASP .Net page I add a hidden control to a Repeater and assign the value as
RowHiddenField.Value = Convert.ToBase64String(myObj.ColumnName);
Now i am trying to compare this column with the object passed in
public bool RowModified(MyObject myObj)
objFound = GetAllObjects.First(o=> o.ID = myObj.ID);
If (objFound.ColumnName == myObj.ColumnName)
but the values are never the same?
After reading a few links in converting TimeStamp, i've attempted them but either they didnt work (could be i was confused and did something wrong) or it didnt apply to my scenario.
Appreciate any help on this.
I don't know anything about the SQL rowversion format. Seems like the links offered by mss in the comments will help you if you care about that.
But if all you care about is equality, then it should be sufficient to just compare the two byte[] objects. The problem with your code is that arrays don't override Equals() or provide an == overload. So using == just compares the two object references, which are always the same.
See this previously-asked question for details of how to correctly (and easily) compare two arrays:
Easiest way to compare arrays in C#

Datagridview combobox business object updating reference

I recently asked this question on here and got the answer. However I'm now trying to apply the same logic on a DataGridView which is bound to a BindingList< T > of Curriculum objects. The Curriculum class has a property of type Year. I'm trying to use a ComboBoxColumn to update the reference the curriculum object has of years.
The comboboxcolumn is bound to a BindingList< T > of years, it errors if I set either the display member or the value member so I left them null. Doing this the datagridview successfully loads and displays the data correctly (I overrode the ToString method on the year class). However, if I choose another year object from the combobox, as soon as it end edits it throws and exception saying it can't convert string to type year.
It looks like I need a TypeConverter to do it, but the problem is the combobox is displaying a descriptive value, which I can't guarantee will be unique to that year object - so I have no way of getting a year object from a given string.
Has anyone got any experience in situations like these, it must be a pretty common thing to want to do but google has failed me on this occasion.
Marlon
Same problem as here. Seems that object binding in a combobox column doesn't work properly and you have to specify a ValueMember.
For the particular project I am working on, I came to the conculsion that it was not worth implementing a custom type descriptor, so instead, I am using a fairly horrible hack:
In the entity that I am binding to, I have the following:
class TestEntity
{
public TestEntity BindingHack_ValueMember
{
get
{
return this;
}
}
public string BindingHack_DisplayMember
{
get
{
return this.ToString();
}
}
}
And the databinding for the column looks like this:
column.DataPropertyName = "Foo";
column.DisplayMember = "BindingHack_DisplayMember";
column.ValueMember = "BindingHack_ValueMember";
A little ugly, perhaps, but it works ...

How to define a default value for a field of a FileHelpers element class

I'm trying to load a CSV file (delims are ';' and quotes are '"').
I have successfully created everything (the wizard tool is awesome), but there's one thing that I can't find a solution for.
Basically, I have an integer (System.Int32) column. In theory most of the records will have a positive integer value in that column. Sometimes, however, I might encounter a value "N/A" in that column. What I want to achieve, is have FileHelpers assign a default value (-1 works just fine), when it encounters "N/A" in that column.
Does anyone know if this is possible?
PS: I might need to do the same for a System.DateTime field (it might also have "N/A" sometimes).
The best way that comes to mind to do what you want would be to use the FieldConverter attribute on those fields and create custom converter classes that assign the default value you want the field to have.
You need to create a class that inherits ConverterBase, and then provide implementations for the two virtual methods, StringToField() and FieldToString().
In the FieldToString() method, you'll check to see if the string is equal to "N/A". If it is, return the default value you want.
You will need two different classes, one that can handle Int32 and one that can handle DateTime.
The FileHelpers documentation has an example of how to do this. Link
Like Brandon said u need to use this converter
// In your class
[FieldConverter(typeof(NoValueConverter))]
public int Number;
// Te Converter
public class NoValueConverter : ConverterBase
{
public override object StringToField(string sourceString)
{
if (sourceString.Trim().ToUpper() == "N/A")
return -1; // or int.MinValue;
else
return Integer.Parse(sourceString);
}
public override string FieldToString(object fieldValue)
{
return fieldValue.ToString();
}
}
We are glad that u liked the wizard :) (we are working a cool autodetection feature to make it more easy to create the mapping class)

Categories