When the user visually changes a property such as the size or location of a TXTextControl.FrameBase TX Text Control .NET for Windows Forms
TXTextControl Namespace
FrameBase Class
The FrameBase class is the base class of the Image, TextFrame, ChartFrame, BarcodeFrame and DrawingFrame classes.
object such as images or barcodes, events are fired to help developers detect these changes. When properties are changed in a dialog such as the TXTextControl.BarcodeLayoutDialog TX Text Control .NET for Windows Forms
TXTextControl Namespace
TextControl Class
BarcodeLayoutDialog Method
Invokes the built-in dialog box for alter the layout settings, the size and the text distances of a barcode.
, the TX Text Control's Changed event is fired to indicate that changes have been made to the object. However, the exact change is not returned and must be determined by comparing the objects.

Serializing Objects

By serializing the object before and after the change, we can compare and find the differences in a very efficient way. The JsonSerializerHelper class implements the SerializeObjectToJson public method that uses the .NET Json implementation to serialize the object.

public static string SerializeObjectToJson<T>(T obj)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
Converters =
{
new IgnoreIntPtrConverter()
}
};
try
{
string jsonString = JsonSerializer.Serialize(obj, options);
return jsonString;
}
catch (Exception ex)
{
return string.Empty;
}
}
view raw test.cs hosted with ❤ by GitHub

The other methods compare the serialized objects by comparing the values of all keys at all levels. It is important to check the complete object hierarchy to get access to all properties. For example, the TXTextControl.BarcodeFrame TX Text Control .NET for Windows Forms
DataVisualization Namespace
BarcodeFrame Class
An instance of the BarcodeFrame class represents a barcode and its layout in a Text Control document.
class implements the inherited properties of FrameBase, and the actual properties of the barcode, such as text or color, are stored in the underlying TXTextControl.Barcode.TXBarcodeControl TX Text Control .NET for Windows Forms
Barcode Namespace
TXBarcodeControl Class
The TXBarcodeControl class provides properties to specify type and format of barcodes rendered in Windows Forms.
object.

Comparing Barcode Objects

When a barcode object is changed in the dialog, the Changed event is fired. The following code shows how to compare the barcode objects before and after the change:

var curBarcode = textControl1.Barcodes.GetItem();
var curBarcodeJson = JsonSerializerHelper.SerializeObjectToJson(curBarcode);
var dialogResult = textControl1.BarcodeLayoutDialog();
if (dialogResult == DialogResult.OK)
{
var newBarcode = textControl1.Barcodes.GetItem();
var newBarcodeJson = JsonSerializerHelper.SerializeObjectToJson(newBarcode);
var differences = JsonSerializerHelper.CompareSerializedObjects(curBarcodeJson, newBarcodeJson);
foreach (var difference in differences)
{
Debug.WriteLine(difference);
}
}
view raw test.cs hosted with ❤ by GitHub

First, we serialize the barcode object before opening the BarcodeLayoutDialog. After the dialog has been successfully closed, we have the same object and serialize it for comparison. For the following example, we will change the text of the QR code from "A" to "ABC":

Barcode object comparison

In this sample code, we print the differences, which are returned in a list of strings like this:

barcode.text: A != ABC
barcode.upperTextLength: 1 != 3
view raw test.txt hosted with ❤ by GitHub

You can see that the property Barcode.Text and Barcode.UpperTextLength has been updated by the dialog.

JsonSerializerHelper Implementation

Here is the full implementation of the JsonSerializerHelper class:

using System.Text.Json;
using System.Text.Json.Serialization;
public class JsonSerializerHelper
{
public static string SerializeObjectToJson<T>(T obj)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
Converters =
{
new IgnoreIntPtrConverter()
}
};
try
{
string jsonString = JsonSerializer.Serialize(obj, options);
return jsonString;
}
catch (Exception ex)
{
return string.Empty;
}
}
private class IgnoreIntPtrConverter : JsonConverter<IntPtr>
{
public override IntPtr Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, IntPtr value, JsonSerializerOptions options)
{
writer.WriteNullValue();
}
}
public static List<string> CompareSerializedObjects(string json1, string json2)
{
var differences = new List<string>();
try
{
var dictionary1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json1);
var dictionary2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(json2);
foreach (var key in dictionary1.Keys)
{
if (dictionary2.ContainsKey(key))
{
var value1 = dictionary1[key];
var value2 = dictionary2[key];
if (value1.ValueKind == JsonValueKind.Object && value2.ValueKind == JsonValueKind.Object)
{
var subDict1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(value1.GetRawText());
var subDict2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(value2.GetRawText());
CompareDictionaries(subDict1, subDict2, differences, key);
}
}
else
{
differences.Add($"{key}: {dictionary1[key]} != null");
}
}
foreach (var key in dictionary2.Keys)
{
if (!dictionary1.ContainsKey(key))
{
differences.Add($"{key}: null != {dictionary2[key]}");
}
}
}
catch (Exception ex)
{
// exception handling
}
return differences;
}
private static void CompareDictionaries(Dictionary<string, JsonElement> dict1, Dictionary<string, JsonElement> dict2, List<string> differences, string parentKey)
{
foreach (var key in dict1.Keys)
{
var fullKey = $"{parentKey}.{key}";
if (!dict2.ContainsKey(key))
{
differences.Add($"{fullKey}: {dict1[key]} != null");
}
else if (!JsonElementEquals(dict1[key], dict2[key]))
{
differences.Add($"{fullKey}: {dict1[key]} != {dict2[key]}");
}
}
foreach (var key in dict2.Keys)
{
if (!dict1.ContainsKey(key))
{
var fullKey = $"{parentKey}.{key}";
differences.Add($"{fullKey}: null != {dict2[key]}");
}
}
}
private static bool JsonElementEquals(JsonElement element1, JsonElement element2)
{
if (element1.ValueKind != element2.ValueKind)
{
return false;
}
switch (element1.ValueKind)
{
case JsonValueKind.Object:
var dict1 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(element1.GetRawText());
var dict2 = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(element2.GetRawText());
return DictionariesEqual(dict1, dict2);
case JsonValueKind.Array:
if (element1.GetArrayLength() != element2.GetArrayLength())
{
return false;
}
var enumerator1 = element1.EnumerateArray();
var enumerator2 = element2.EnumerateArray();
while (enumerator1.MoveNext() && enumerator2.MoveNext())
{
if (!JsonElementEquals(enumerator1.Current, enumerator2.Current))
{
return false;
}
}
return true;
default:
return element1.ToString() == element2.ToString();
}
}
private static bool DictionariesEqual(Dictionary<string, JsonElement> dict1, Dictionary<string, JsonElement> dict2)
{
if (dict1.Count != dict2.Count)
{
return false;
}
foreach (var kvp in dict1)
{
if (!dict2.ContainsKey(kvp.Key) || !JsonElementEquals(kvp.Value, dict2[kvp.Key]))
{
return false;
}
}
return true;
}
}
view raw test.cs hosted with ❤ by GitHub

Conclusion

By serializing objects before and after a change, you can easily compare the differences. This is a very efficient way to detect changes in complex objects such as barcode objects in TX Text Control.