Home > Software engineering >  checkbox DataBindings does not change the dataSource property if a new dataSource is installed
checkbox DataBindings does not change the dataSource property if a new dataSource is installed

Time:07-27

How to properly issue DataBindings for checkBox?
DataBindings checkBox does not work for the new dataSource


Description

Scenario-1
In MainForm.cs, the sequence of code lines is as follows:

checkBox1.DataBindings.Add(nameof(checkBox1.Checked), settingsParser, nameof(settingsParser.IsTestProperty), false, DataSourceUpdateMode.OnPropertyChanged);  
settingsParser = settingsService.ReadFilfe2(pathFileCampaign);  

Result: The settingsParser.IsTestProperty property is not updated.

Scenario-2
In MainForm.cs, the sequence of lines of code is as follows:

settingsParser = settingsService.ReadFilfe2(pathFileCampaign);
    checkBox1.DataBindings.Add(nameof(checkBox1.Checked), settingsParser, nameof(settingsParser.IsTestProperty), false, DataSourceUpdateMode.OnPropertyChanged);    

Result: The settingsParser.IsTestProperty property is updated.

Question:
How to set up DataBindings correctly?
I thought you could
- create an object (SettingsParser settingsParser);
- configure checkBox1.DataBindings;
And then just change the content of the SettingsParser settingsParser.
Or you need to re-configure checkBox1 every time you change the SettingsParser settingsParserDataBindings?


Main goal.

Main goal:

  • DataGrid is located on MainForm.cs;
  • the user selects an entry in the DataGrid;
  • the program selects the path to the JSON file with the settings for the parser;
  • the program displays the settings from JSON in the linked Controls;

Testings

If the string sequence is:

checkBox1.DataBindings.Add(nameof(checkBox1.Checked), settingsParser, nameof(settingsParser.IsTestProperty), false, DataSourceUpdateMode.OnPropertyChanged);
settingsParser = settingsService.ReadFilfe2(pathFileCampaign);

Journal:
1. Launch the application. --- --- --- --- --- --- --- --- --- --- --- --- ---
Debug.

=== === ====     SettingsParser.cs -- OnPropertyChanged     === === ===
---  ---  ----   Изменено свойство: IsTestProperty
     
 === === ===    `SettingsParser.cs`         === === === 
 --- Значение свойства `IsTestProperty`: False

=== === === === === ===      Start !!!   === === === ===
=== === ===      MainForm.cs -- public MainForm()   === === === ===
--- ---- ---- ---- ---- checkBox1.Checked: False
--- --  SettingsParserProp.IsTestProperty: False

Result: checkBox1.Checked = false.

2. The user installs: checkBox1.Checked = true --- --- --- --- --- --- --- --- --- ---
Debug.

xxx xxx       CheckBox1   CheckBox1_CheckedChanged   xxx xxx xxx xxx
 xxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxx
 --- --- ----   ---- ---- ---- ----  CheckBox1: True
--- --- ---  SettingsParserProp.IsTestProperty: False
     
=== === ====     SettingsParser.cs -- OnPropertyChanged     === === ===
---  ---  ----   Изменено свойство: IsTestProperty
     
 === === ===    `SettingsParser.cs`         === === === 
 --- Значение свойства `IsTestProperty`: True

Result.
As a result , we get:
In CheckBox1_CheckedChanged

--- --- ---- ---- ---- ---- ---- CheckBox1: True   
--- --- --- SettingsParserProp.IsTestProperty: False  

Code

SettingsParser.json

{
  "IsTestProperty": false
}

MainForm.cs

 public partial class MainForm : Form
        {
    
            string pathFileCampaign = @"e:\Test\WinFrom\Control\Bindings\01\SettingsParser.json";
    
            SettingsParser settingsParser = new SettingsParser();
            SettingsService settingsService = new SettingsService();
    
            public MainForm()
            {
                InitializeComponent();
    
                checkBox1.DataBindings.Add(nameof(checkBox1.Checked), settingsParser, nameof(settingsParser.IsTestProperty), false, DataSourceUpdateMode.OnPropertyChanged);
                settingsParser = settingsService.ReadFilfe2(pathFileCampaign);
    
                Debug.Print("");
                Debug.Print("=== === === === === ===      Start !!!   === === === ===");
                Debug.Print("=== === ===      MainForm.cs -- public MainForm()   === === === ===");            
                Debug.Print("--- ---- ---- ---- ---- checkBox1.Checked: "   checkBox1.Checked.ToString());
                Debug.Print("--- --  settingsParser.IsTestProperty: "   settingsParser.IsTestProperty.ToString());
                
    
                checkBox1.CheckedChanged  = CheckBox1_CheckedChanged;
            }
    
            private void CheckBox1_CheckedChanged(object sender, EventArgs e)
            {
                Debug.Print(" ");
                Debug.Print(" ");
                Debug.Print(" xxx xxx       CheckBox1   CheckBox1_CheckedChanged   xxx xxx xxx xxx");
                Debug.Print(" xxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxxxxx xxx");
                Debug.Print(" --- --- ----   ---- ---- ---- ----  CheckBox1: "   checkBox1.Checked.ToString());
                Debug.Print("--- --- ---  settingsParser.IsTestProperty: "   settingsParser.IsTestProperty.ToString());
    
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                
                Debug.Print("");
                Debug.Print("=== === ===      MainForm.cs -- button1 - Сохранить   === === === ===");
                Debug.Print("--- ---- ---- ---- ----  pathFileCampaign: "   pathFileCampaign);
                Debug.Print("--- ---- ---- ---- ---- checkBox1.Checked: "   checkBox1.Checked.ToString());
                Debug.Print("--- --  SettingsParserProp.IsTestProperty: "   settingsParser.IsTestProperty.ToString());         
            }
        }

**SettingsParser.cs**  
 public class SettingsParser : INotifyPropertyChanged
    {
        public SettingsParser()
        {
            
        }
        
        #region IsTestProperty  --- --- --- --- --- --- --- --- --- --- --- --- ---        
        private bool _isTestProperty;

        public bool IsTestProperty
        {
            get { return _isTestProperty; }
            set 
            { 
                _isTestProperty = value;                
                OnPropertyChanged();

                
                Debug.Print("     ");
                Debug.Print(" === === ===    `SettingsParser.cs`         === === === ");
                Debug.Print(" --- Значение свойства `IsTestProperty`: "   value.ToString());
                
            }
        }
        #endregion IsTestProperty.END  --- --- --- --- --- --- --- --- --- --- --- --- ---
               

        #region PropertyChanged === === === === === === === === === === === === === === === ===        
        public event PropertyChangedEventHandler PropertyChanged;       

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            Debug.Print("     ");            
            Debug.Print("=== === ====     SettingsParser.cs -- OnPropertyChanged     === === ===");
            Debug.Print("---  ---  ----   Изменено свойство: "   propertyName);
            

        }            
        #endregion PropertyChanged.END === === === === === === === === === === === === === === === ===

    }   

SettingsParser.cs

  public class SettingsParser : INotifyPropertyChanged
{
    public SettingsParser()
    {
        
    }
    
    #region IsTestProperty  --- --- --- --- --- --- --- --- --- --- --- --- ---        
    private bool _isTestProperty;

    public bool IsTestProperty
    {
        get { return _isTestProperty; }
        set 
        { 
            _isTestProperty = value;                
            OnPropertyChanged();

            
            Debug.Print("     ");
            Debug.Print(" === === ===    `SettingsParser.cs`         === === === ");
            Debug.Print(" --- Значение свойства `IsTestProperty`: "   value.ToString());
            
        }
    }
    #endregion IsTestProperty.END  --- --- --- --- --- --- --- --- --- --- --- --- ---
           

    #region PropertyChanged === === === === === === === === === === === === === === === ===        
    public event PropertyChangedEventHandler PropertyChanged;       

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        Debug.Print("     ");            
        Debug.Print("=== === ====     SettingsParser.cs -- OnPropertyChanged     === === ===");
        Debug.Print("---  ---  ----   Изменено свойство: "   propertyName);
        

    }            
    #endregion PropertyChanged.END === === === === === === === === === === === === === === === ===

}  

SettingsService.cs

public class SettingsService
{
    public string pathToDataFile = $"{Environment.CurrentDirectory}\\Settings\\SettingsParserApp.json";


    public void Write(object value, string pathFile)
    {
        using (StreamWriter writer = File.CreateText(pathFile))
        {
            string output = JsonConvert.SerializeObject(value, Formatting.Indented);
            writer.Write(output);
        }
    }

    public SettingsParser ReadFilfe2(string pathFile)
    {
        SettingsParser settings = new SettingsParser();

        ///string path = settings[0].PathToSettingsFile; // для теста

        try
        {
            using (var reader = File.OpenText(pathFile))
            {
                var fileText = reader.ReadToEnd();
                settings = JsonConvert.DeserializeObject<SettingsParser>(fileText);
            }
        }
        catch (Exception ex)
        {
            Debug.Print("--- === === ReadFilfe === "   ex.Message);

            // throw;
        }

        return settings;
    }

}

Update-1

I added the code

BindingSource bs = new BindingSource();
bs.DataSource = settingsParser;
checkBox1.DataBindings.Add(nameof(checkBox1.Checked), bs, nameof(bs.???????????), false, DataSourceUpdateMode.OnPropertyChanged);

The code snippet where the changes were made

  public partial class MainForm : Form
    {

        string pathFileCampaign = @"e:\Test\WinFrom\Control\Bindings\01\SettingsParser.json";

        SettingsParser settingsParser = new SettingsParser();
        SettingsService settingsService = new SettingsService();
        BindingSource bs = new BindingSource();  // !!!!!

        public MainForm()
        {
            InitializeComponent();

            bs.DataSource = settingsParser;  // !!!!!

            checkBox1.DataBindings.Add(nameof(checkBox1.Checked), bs, nameof(bs.????????????????? ), false, DataSourceUpdateMode.OnPropertyChanged);
            // checkBox1.DataBindings.Add(nameof(checkBox1.Checked), settingsParser, nameof(settingsParser.IsTestProperty), false, DataSourceUpdateMode.OnPropertyChanged);
            
            bs.DataSource = settingsService.ReadFilfe2(pathFileCampaign);  // !!!!!
            
            // .......
        }
        // .......
    }

Update-2. Solution-1. Jimi

SettingsParser settingsParser = null; 
BindingSource bs = null; 
// [...] 
settingsParser = settingsService.ReadFilfe2(pathFileCampaign); 

bs = new BindingSource(settingsParser, null); 

checkBox1.DataBindings.Add("Checked", bs, "IsTestProperty", false, DataSourceUpdateMode.OnPropertyChanged);. 

Or use nameof() instead of strings, same thing.
-- When you create a new SettingsParser object, set bs.DataSource = [the new object];

CodePudding user response:

I've read your code carefully, and it seems that what you need is a SettingsService that stays put once you set bindings on it. The implementation example shown below provides bindable properties like IsTestProperty much like you've shown in your code but that won't get disconnected when a new settings.json file is loaded.

public partial class MainForm : Form
{
    // The SettingsService is read-only and set in initialization one time.
    private readonly SettingsService settingsService = new SettingsService();
    ...
}

What makes it work correctly is that when the time comes to read a different settings file using your ReadFilfe2 method it deserializes to a temporary object first, and then copies the properties from the tmp object to the real SettingsService instance whose bindings are still in place. By doing this, the PropertyChange notifications are fired and handled the way they should be.


BEFORE AND AFTER - Selecting the second row chooses SettingsParser.B.json

graphic showing bound property change


You could implement SettingsService something like this:

internal class SettingsService : INotifyPropertyChanged
{
    #region Binding Properties
    public bool IsTestProperty
    {
        get => _IsTestProperty;
        set
        {
            if (!Equals(_IsTestProperty, value))
            {
                _IsTestProperty = value;
                OnPropertyChanged();
            }
        }
    }
    bool _IsTestProperty = false;
    #endregion Binding Properties

    //@"e:\Test\WinFrom\Control\Bindings\01\SettingsParser.A.json";
    string settingsDirectory = Path.Combine(
        AppDomain.CurrentDomain.BaseDirectory,
        "Settings");

    internal void ReadFilfe2(string fileName)
    {
        // Deserialize a temporary copy.
        string fullPath = Path.Combine($"{settingsDirectory}", fileName);
        if (!string.Equals(_currentSettingsFile, fullPath))
        {
            _currentSettingsFile = fullPath;
            SettingsService tmp = 
                JsonConvert
                .DeserializeObject<SettingsService>(
                    File.ReadAllText(_currentSettingsFile));
            // Get the JSON properties
            foreach (
                var property in
                typeof(SettingsService).GetProperties())
            {
                // Copy the tmp property values to SettingsService 
                // without destroying the real object and its bindings.
                property.SetValue(this, property.GetValue(tmp));
            }
        }
    }
    internal void Save()
    {
        if(_currentSettingsFile != null)
        {
            File.WriteAllText(_currentSettingsFile, JsonConvert.SerializeObject(this));
        }
    }

    private string _currentSettingsFile = null;

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Testing

SettingsParser.A.json file:

{
    "IsTestProperty": false
}

SettingsParser.B.json file:

{
    "IsTestProperty": true
}

public partial class MainForm : Form
{
    // The SettingsService and DataGridView bindings
    // are read-only and set in initialization one time..
    private readonly SettingsService settingsService = new SettingsService();
    private readonly BindingList<Record> DGVDataSource = new BindingList<Record>();

    public MainForm()
    {
        InitializeComponent();
        CheckBox1.CheckedChanged  = CheckBox1_CheckedChanged;
        button1.Click  = button1_Click;
        dataGridView1.CurrentCellChanged  = DataGridView1_CurrentCellChanged;
        dataGridView1.AllowUserToAddRows = false;
    }
    protected override void onl oad(EventArgs e)
    {
        base.OnLoad(e);
        {
            CheckBox1.DataBindings.Add(
                nameof(CheckBox1.Checked),
                settingsService, 
                nameof(settingsService.IsTestProperty),
                false,
                DataSourceUpdateMode.OnPropertyChanged);

            dataGridView1.DataSource = DGVDataSource;
            DGVDataSource.Add(new Record 
            { 
                Description = "Use settings A", 
                Settings = "SettingsParser.A.json" 
            });
            DGVDataSource.Add(new Record
            { 
                Description = "Use settings B", 
                Settings = "SettingsParser.B.json" 
            });
            dataGridView1.AutoResizeColumns();

            printDebugMessage();
        }
    }
    private void CheckBox1_CheckedChanged(object sender, EventArgs e)
    {
        // The action must be allowed to complete in order to avoid a
        // race condition and have the printDebugMessage be correct.
        BeginInvoke((MethodInvoker)delegate
        {
            printDebugMessage();

            // Optional Save to current file
            // settingsService.Save();
        });
    }
    private void button1_Click(object sender, EventArgs e) => printDebugMessage();

    private void DataGridView1_CurrentCellChanged(object sender, EventArgs e)
    {
        // Load settings based on selected cell
        if (dataGridView1.CurrentCell != null)
        {
            var settingsFileName = DGVDataSource[dataGridView1.CurrentCell.RowIndex].Settings;
            settingsService.ReadFilfe2(settingsFileName);
        }
    }
    private void printDebugMessage([CallerMemberName]string caller = null)
    {
        Debug.WriteLine($"{caller}: SettingsParser.IsTestProperty: {settingsService.IsTestProperty}");
        Text = $"IsTestProperty: {settingsService.IsTestProperty}";
    }
}
class Record
{
    public string Description { get; set; }
    public string Settings { get; set; }
}
  • Related