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 settingsParser
DataBindings?
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
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; }
}