Winforms DataBinding works one way only - c#

I do DataBinding in the following way
private List<MyEditor> Editors { get; set; }
private Dictionary<int,object> dictionary
private void SetEditors()
{
Editors.Clear();
foreach (var element in dictionary)
{
var editor = MyFactory.GetEditor();
editor.DataBindings.DefaultDataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
editor.DataBindings.Add("Value", element, "Value");
//some code
Editors.Add(editor);
}
//some more code
}
Then in GUI I do some changes to Value of Editors[0], after that in another piece of code I try to get value from dictionary element and find out that it hasn't changed, even though I use Editors[0].DataBindings["Value"].WriteValue() to ensure data is written to dictionary.
In debugger I can see the following picture:
Editors[0].DataBindings["Value"] {System.Windows.Forms.Binding} System.Windows.Forms.Binding
....
DataSource {[0, oldValue]} object {System.Collections.Generic.KeyValuePair<int,object>}
....
while
Editors[0].Value "newValue" object {string}
What could it be? Will be grateful for any ideas.

I'm not certain I'm following correctly, but I believe the answer is that when you enumerate a Dictionary, the KeyValuePair (KVP) objects you get are created at the time of the enumeration. Enumerate it again and you will get a different set of objects. This is because a KVP is a value type.
To alter something referred to by a KVP, you have to return to the original Dictionary object. A data binding does not do that.

Related

Update ObservableCollection element not working

I have one ObservableCollection in my ViewModel with INotifyPropertyChanged, say A. Now I am going to loop through A to get some elements updated.
public ObservableCollection<ClassA> A
{
get
{
if (this.a== null)
{
this.a= new ObservableCollection<ClassA>();
}
return this.a;
}
set
{
this.a= value;
this.OnPropertyChanged("A");// implement PropertyChangedEvent
}
}
In the loop I update the values.
foreach (var item in MyViewModel.A)
{
if(condition)
MyViewModel.A.Type= "CASH";
else
MyViewModel.A.Type= "CHECK";
}
But I see the setter part is not reached. so the collection is not updated.
It looks like you're trying to update the elements in the ObservableCollection<ClassA> and not setting the collection to a new value. If you want a property change to occur when calling MyViewModel.A.Type = "CASH" then ClassA will need to implement INotifyPropertyChanged.
(EDIT: For others seeing this, check this question/answer - I'm not able to mark this as a possible duplicate. You need to monitor for property changes of elements in the collection and then trigger the property change on your ObservableCollection manually. The container does not do this for you.)
I use my own method. By using generic list to retrieve the all items from Observable Collection. Then convert the generic list to the Observable Collection.
It is not the best way but so far it works out.

C# Iterating over a dictionary passed as a generic

I am having trouble trying to iterate over a dictionary passed to a function as a generic. For example, I have a function that loads data from a DB.
public T Load<T>(...)
This function can be called like so, with which I have no problems;
someclasstype data = Load<someclasstype>(...);
List<someclasstype> data = Load<List<someclasstype>>(...);
I've recently tried to extend this to be able to deal with dictionaries as well, and calling it like so:
Dictionary<long, someclasstype> data = Load<Dictionary<long, someclasstype>>(...)
I can load the data without a problem and store it in the dictionary no problem.
At this stage, the dictionary, with all its keyvaluepairs is stored in a variable called result, and I'm creating an IEnumerable with
IEnumerator resultIdx = ((IEnumerable)result).GetEnumerator();
if (!resultIdx.MoveNext())
return (T)result;
object kvp = resultIdx.Current;
So far so good. I can see the value of the key and the value of the value in a watch, or by mouseover on the kvp variable.
But I cannot figure out how to get the value part of the keyvaluepair from kvp.
// None of these work - I get compile time errors, unboxing errors, or invalid cast errors.
object item = ((KeyValuePair<TKey, TValue>)kvp).Value;
object item = ((KeyValuePair<long, object>)kvp).Value;
object item = ((T)kvp).Value // Never had a hope for this, but desperation...
Does anyone have any idea how I can do this?
try adding dynamic kvp = resultIdx.Current; . Then you can use kvp.Value
You can rewrite the function into two functions like.
public T Load<T>(...)
//Or maybe public List<T> Load<T>(...)
and
public Dictionary<long, T> LoadD<T>(...)
Then you can cast result to KeyValuePair<long, T> in LoadD. You can call Load from LoadD to minimize code rewriting.
Answer provided by Dede in comments:
"Use Reflection ?
object key kvp.GetType().GetProperty("Key").GetValue(kvp);
object value kvp.GetType().GetProperty("Value").GetValue(kvp);
Not very optimized, but can work... – Dede 24"

Is there a way to make string a reference type in a collection?

I want to modify some strings that are contained in an object like say an array, or maybe the nodes in an XDocument (XText)XNode.Value.
I want to gather a subset of strings from these objects and modify them, but I don't know at runtime from what object type they come from.
Put another way, let's say I have objects like this:
List<string> fruits = new List<string>() {"apple", "banana", "cantelope"};
XDocument _xmlObject;
I want to be able to add a subset of values from the original collections to new lists like this:
List<ref string> myStrings1 = new List<ref string>();
myStrings1.Add(ref fruits[1]);
myStrings1.Add(ref fruits[2]);
List<ref string> myStrings2 = new List<ref string>();
IEnumerable<XNode> xTextNodes = getTargetTextNodes(targetPath); //some function returns a series of XNodes in the XDocument
foreach (XNode node in xTextNodes)
{
myStrings2.Add(((XText)node).Value);
}
Then change the values using a general purpose method like this:
public void Modify(List<ref string> mystrings){
foreach (ref string item in mystrings)
{
item = "new string";
}
}
Such that I can pass that method any string collection, and modify the strings in the original object without having to deal with the original object itself.
static void Main(string[] args)
{
Modify(myStrings1);
Modify(myStrings2);
}
The important part here is the mystrings collection. That can be special. But I need to be able to use a variety of different kinds of strings and string collections as the originals source data to go in that collection.
Of course, the above code doesn't work, and neither does any variation I've tried. Is this even possible in c#?
What you want is possible with C#... but only if you can fix every possible source for your strings. That would allow you to use pointers to the original strings... at a terrible cost, however, in terms of memory management and unsafe code throughout your application.
I encourage you to pursue a different direction for this.
Based on your edits, it looks like you're always working with an entire collection, and always modifying the entire collection at once. Also, this might not even be a string collection at the outset. I don't think you'll be able to get the exact result you want, because of the base XDocument type you're working with. But one possible direction to explore might look like this:
public IEnumerable<string> Modify(IEnumerable<string> items)
{
foreach(string item in items)
{
yield return "blah";
}
}
You can use a projection to get strings from any collection type, and get your modified text back:
fruits = Modify(fruits).ToList();
var nodes = Modify( xTextNodes.Select(n => (XText)n.Value));
And once you understand how to make a projection, you may find that the existing .Select() method already does everything you need.
What I really suggest, though, is that rather than working with an entire collection, think about working in terms of one record at a time. Create a common object type that all of your data sources understand. Create a projection from each data source into the common object type. Loop through each of the objects in your projection and make your adjustment. Then have another projection back to the original record type. This will not be the original collection. It will be a new collection. Write your new collection back to disk.
Used appropriately, this also has the potential for much greater performance than your original approach. This is because working with one record at a time, using these linq projections, opens the door to streaming the data, such that only one the one current record is ever held in memory at a time. You can open a stream from the original and a stream for the output, and write to the output just as fast as you can read from the original.
The easiest way to achieve this is by doing the looping outside of the method. This allows you to pass the strings by reference which will replace the existing reference with the new one (don't forget that strings are immutable).
And example of this:
void Main()
{
string[] arr = new[] {"lala", "lolo"};
arr.Dump();
for(var i = 0; i < arr.Length; i++)
{
ModifyStrings(ref arr[i]);
}
arr.Dump();
}
public void ModifyStrings(ref string item)
{
item = "blah";
}

Using the Concurrent Dictionary - Thread Safe Collection Modification

Recently I was running into the following exception when using a generic dictionary
An InvalidOperationException has occurred. A collection was modified
I realized that this error was primarily because of thread safety issues on the static dictionary I was using.
A little background: I currently have an application which has 3 different methods that are related to this issue.
Method A iterates through the dictionary using foreach and returns a value.
Method B adds data to the dictionary.
Method C changes the value of the key in the dictionary.
Sometimes while iterating through the dictionary, data is also being added, which is the cause of this issue. I keep getting this exception in the foreach part of my code where I iterate over the contents of the dictionary. In order to resolve this issue, I replaced the generic dictionary with the ConcurrentDictionary and here are the details of what I did.
Aim : My main objective is to completely remove the exception
For method B (which adds a new key to the dictionary) I replaced .Add with TryAdd
For method C (which updates the value of the dictionary) I did not make any changes. A rough sketch of the code is as follows :
static public int ChangeContent(int para)
{
foreach (KeyValuePair<string, CustObject> pair in static_container)
{
if (pair.Value.propA != para ) //Pending cancel
{
pair.Value.data_id = prim_id; //I am updating the content
return 0;
}
}
return -2;
}
For method A - I am simply iterating over the dictionary and this is where the running code stops (in debug mode) and Visual Studio informs me that this is where the error occured.The code I am using is similar to the following
static public CustObject RetrieveOrderDetails(int para)
{
foreach (KeyValuePair<string, CustObject> pair in static_container)
{
if (pair.Value.cust_id.Equals(symbol))
{
if (pair.Value.OrderStatus != para)
{
return pair.Value; //Found
}
}
}
return null; //Not found
}
Are these changes going to resolve the exception that I am getting.
Edit:
It states on this page that the method GetEnumerator allows you to traverse through the elements in parallel with writes (although it may be outdated). Isnt that the same as using foreach ?
For modification of elements, one option is to manually iterate the dictionary using a for loop, e.g.:
Dictionary<string, string> test = new Dictionary<string, string>();
int dictionaryLength = test.Count();
for (int i = 0; i < dictionaryLength; i++)
{
test[test.ElementAt(i).Key] = "Some new content";
}
Be weary though, that if you're also adding to the Dictionary, you must increment dictionaryLength (or decrement it if you move elements) appropriately.
Depending on what exactly you're doing, and if order matters, you may wish to use a SortedDictionary instead.
You could extend this by updating dictionaryLength explicitly by recalling test.Count() at each iteration, and also use an additional list containing a list of keys you've already modified and so on and so forth if there's a danger of missing any, it really depends what you're doing as much as anything and what your needs are.
You can further get a list of keys using test.Keys.ToList(), that option would work as follows:
Dictionary<string, string> test = new Dictionary<string, string>();
List<string> keys = test.Keys.ToList();
foreach (string key in keys)
{
test[key] = "Some new content";
}
IEnumerable<string> newKeys = test.Keys.ToList().Except(keys);
if(newKeys.Count() > 0)
// Do it again or whatever.
Note that I've also shown an example of how to find out whether any new keys were added between you getting the initial list of keys, and completing iteration such that you could then loop round and handle the new keys.
Hopefully one of these options will suit (or you may even want to mix and match- for loop on the keys for example updating that as you go instead of the length) - as I say, it's as much about what precisely you're trying to do as much as anything.
Before doing foreach() try out copying container to a new instance
var unboundContainer = static_container.ToList();
foreach (KeyValuePair<string, CustObject> pair in unboundContainer)
Also I think updating Value property is not right from thread safety perspectives, refactor your code to use TryUpdate() instead.

Accessing properties of objects within a List<T>

I'm trying to use a property of individual object instances stored within a List<T> object, but I can't seem to access the properties directly.
I have an object (sportsCarVehicle) which stores a user-defined name (strVehicleName) (amongst other properties, but that's not important) within itself. The object is then stored within a List<sportsCarVehicle> object called sportsCarVehicleStorage.
I need to access every instance of sportsCarVehicle in List<sportsCarVehicle> and pass the value of strVehicleName to a combo box on a form.
I assume I'll need some kind to loop to cycle through each instance and pass the name to the combo box, but my main issue is not being able to access the property I need. The sportsCarVehicle instances have no reference-able name.
One more thing I should note: the constructor for sportsCarVehicle is called within the sportsCarVehicleStorage.Add() method.
Any suggestions on how I could do this?
Cant you do this
List<string> lst = new List<string>{"Hello", "World"};
int len = lst[0].Length;
Here .Length is a property of string. As long as that property is public we can access it.
In your case
List<sportsCarVehicle> sportsCarVehicleStorage = new List<sportsCarVehicle>();
// Some code to populate list.
mycombobox.Items = sportsCarVehicleStorage
.Select(x => x.strVehicleName).ToArray();
Make Sure property strVehicleName is public in that class.
You can use foreach to loop through the list, assigning each member of the list to a named variable, like:
foreach (sportsCarVehicle scv in sportsCarVehicleStorage)
{
//scv is the name of the currently looping sportsCarVehicle object
//use scv.strVehicleName to access the property.
myComboBox.Items.Add(scv.strVehicleName);
}
foreach (SportsCarVehicle car in myListName)
{
//do stuff here
}
That's the most basic example, you can use PLINQ etc. to do it in a more streamlined way.
An alternative could be to bind the list of sportsCarVehicle directly to the comboBox, for example:
List<sportCarVehicle> sportsCarVehicleStorage= new List<sportsCarVehicle>;
// Set up list content here
// ...
myComboBox.DataSource = sportsCarVehicleStorage;
myComboBox.DisplayMember = "strVehicleName";

Categories

Resources