Patrick
Patrick

Reputation: 2583

C# WPF how to update observable collection after having programmatically updated datagrid - No MVVM

I have a datagrid linked with an observable collection. They have a large number of columns/properties I have to operate on. I need to do a string replace on a column chosen by the user at the runtime through a combobox. enter image description here So what I do at first is to find out the index of the column (in our case Description index=2 starting from 0) and then operate the substitutions with the code below. The variable iii indicates the current row

DataGridRow row = (DataGridRow)dtgFeatures.ItemContainerGenerator.ContainerFromIndex(iii);
if (row != null)
{
    var content = dtgFeatures.Columns[indexColumnToOperateOn].GetCellContent(row);
    if (content != null)
    {
        string str = ((TextBlock)content).Text;
        if (str.ToUpper().Trim().Contains(tbxSrc.Text.ToUpper().Trim()))
        {
            ((TextBlock)content).Text = str.Replace(tbxSrc.Text, tbxDest.Text);
            obcCfgPartPrograms = (ObservableCollection < CfgPartPrograms >) dtgFeatures.ItemsSource;
        }
    }
}

In our example we can change the string "pcacd" with "AAA"

enter image description here

enter image description here

So that "graphically" works but the observable collection is not back-updated with the command obcCfgPartPrograms = (ObservableCollection < CfgPartPrograms >) dtgFeatures.ItemsSource;

I know I could do that through each properties but that would be ackward for

--EDIT-- Sorry from the comments I see I have not made it clear. My bad.

So I have a very large number of properties (here 9 but potentially many many more) so I would not write something like:

if(comboBoxValue="Description")
    obc.Description = dtgFeatures[2,row];
else  if(comboBoxValue="Notes")
    obc.Description = dtgFeatures[4,row];

...

Since I can MANUALLY edit the datagrid and reflect the changes on the observable collection

datagrid MANUAL EDIT ---> changes on observable collection

why not having the possibility of PROGRAMMATICALLY (e.g. with string compare) edit the datagrid and reflect the changes on the obc?

datagrid AUTOMATIC EDIT ---> changes on observable collection

So in short: 1. I am able to change the datagrid --> dtgFeatures[property_X,row_Y] = "AAAAAA"; 2. I would like to be able to automatically change obc[Y].X = "AAAAAA";

there are a very large number of them.

Thanks for any help Patrick

Upvotes: -1

Views: 1094

Answers (3)

mm8
mm8

Reputation: 169200

If the TextBlock is bound to a source property of the CfgPartPrograms object, you could get the name of this property using the GetBindingExpression method. You could then use reflection to set the property to a new value:

DataGridRow row = (DataGridRow)dtgFeatures.ItemContainerGenerator.ContainerFromIndex(iii);
if (row != null)
{
    var content = dtgFeatures.Columns[indexColumnToOperateOn].GetCellContent(row);
    if (content != null)
    {
        TextBlock textBlock = (TextBlock)content;
        if (textBlock.Text.ToUpper().Trim().Contains(tbxSrc.Text.ToUpper().Trim()))
        {
            string str = textBlock.Text.Replace(tbxSrc.Text, tbxDest.Text);
            textBlock.Text = str;
            BindingExpression be = textBlock.GetBindingExpression(TextBlock.TextProperty);
            if (be != null && be.ParentBinding != null && be.ParentBinding.Path != null && !string.IsNullOrEmpty(be.ParentBinding.Path.Path))
            {
                object cfgPartPrograms = textBlock.DataContext;
                if (cfgPartPrograms != null)
                {
                    System.Reflection.PropertyInfo pi = typeof(CfgPartPrograms).GetProperty(be.ParentBinding.Path.Path);
                    if (pi != null)
                        pi.SetValue(cfgPartPrograms, str);
                }
            }
        }
    }
}

Upvotes: 1

user4367090
user4367090

Reputation:

Yes, that can be done. The path to go through is the following:

imagine having the following class:

public class MyClass
{
    public string s1 { get; set; }
    public string s2 { get; set; }
    public string s3 { get; set; }

    public MyClass(string _s1, string _s2, string _s3)
    {
        s1 = _s1;
        s2 = _s2;
        s3 = _s3;
    }
}

So now we can create and fill the obc:

var obc = new ObservableCollection<MyClass>();
obc.Add(new MyClass("a1","a2","a3"));
obc.Add(new MyClass("b1", "b2","b3"));

enter image description here

We can modify it with:

foreach (var itemObc in obc)
{
    PropertyInfo[] Fields = itemObc.GetType().GetProperties();
        foreach (PropertyInfo field in Fields)
    {
    var currentField = field.GetValue(itemObc, null);
    var t = currentField.GetType();
    if (t == typeof(string))
        field.SetValue(itemObc, "XXXX");
    }
}

and the result is:

enter image description here

You might even use extensions:

public static class ObservableCollectionExtensions
{
public static ObservableCollection<T> SetValue<T>(this ObservableCollection<T> obc, int rowNumFromZero, int propNumFromZero, string str)
{
    int rowCounter = -1;
    foreach (var itemObc in obc)
    {
        rowCounter++;
        PropertyInfo[] Fields = itemObc.GetType().GetProperties();
        int propCounter = -1;
        foreach (PropertyInfo field in Fields)
        {
            propCounter++;
            if (rowCounter == rowNumFromZero)
            {
                var currentField = field.GetValue(itemObc, null);
                if (currentField != null)
                {
                    var t = currentField.GetType();
                    if (t == typeof(string) && propCounter == propNumFromZero)
                        field.SetValue(itemObc, str);
                }
            }
        }
    }
    return obc;
}

}

and use it like that:

obc.SetValue(numRow, numCol, strNewValue);

Upvotes: 2

Nick
Nick

Reputation: 5042

You should modify the data in the ObservableCollection instead. Generally, always avoid manipulating the UI controls. Modify your data and, if necessary, raise OnPropertyChanged event.

Upvotes: 0

Related Questions