Problem with the use of converters in WPF

45 views Asked by At

I have in my database a string corresponding to a serialized byte array. I'd like to be able to modify bytes present in that string and add or remove some of them.

I'm converting my string retrieved from my database to my viewmodel to a list of bytes in my XAML via a converter present in my listbox binding. I then use the byte array represented as textboxes with a datatemplate. But the problem is that I'm not able to retrieve changes done in the textboxes back to the listbox and then back to the Serialized string. What I tried:

<ListBox ItemsSource="{Binding ByteArray, Converter={converter:HexStringToListOfBytesConverter}}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBox Text="{Binding Path=., Mode=TwoWay}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

I'm having an idea about why it isn't working but can't get my head around how to make it work. I think that the converter doesn't affect the textboxes. Ideally the string to byte array conversion should only be used to edit an existing Serialized array. And a new made one from the modifications done should replace the old one.

1

There are 1 answers

0
BionicCode On

You can't bind to a string TwoWay. You bind to properties of objects, the binding source. In a TwoWay binding you can change the value of these properties. This means, in theory, you could bind to properties of string to change its content.
Additionally, string is immutable.

You must introduce a wrapper type to represent a byte or character representation. This way you can modify the property value of a binding source.

In general, your approach is overly complicated. You can directly work on string or even char. There is a bult-in compiler conversion between char and byte. It doesn't make sense to convert a string to byte[] only to convert it back to string for editing in the view. Which human being does not prefer to edit text presented as characters instead of bytes...
I recommend to convert the hex string to a text string and edit those values.

The following example introduces a ByteValue wrapper and encapsulates the conversion in a ByteValueCollection that you can directly bind to.

The ByteValue enables you to edit both the byte value and the character representation. MainWindow.xaml.cs

public partial class MainWindow : Window
{
  public ByteValueCollection Bytes
  {
    get => (ByteValueCollection)GetValue(BytesProperty);
    set => SetValue(BytesProperty, value);
  }

  public static readonly DependencyProperty BytesProperty = DependencyProperty.Register(
    "Bytes",
    typeof(ByteValueCollection),
    typeof(MainWindow),
    new PropertyMetadata(default));

  public MainWindow()
  {
    InitializeComponent();

    string hexText = "48616C6C6F"; // Hallo
    this.Bytes = new ByteValueCollection(hexText);

    string hexText = "48 61 6C 6C 6F"; // Hallo
    this.Bytes = new ByteValueCollection(hexText, ' ');

    string hexString = this.Bytes.ToHexString(); // "48616C6C6F"
    string hexString = this.Bytes.ToHexString(' '); // "48 61 6C 6C 6F"
  }
}

MainWIndow.xaml

<Window x:Name="Root">
  <ListBox ItemsSource="{Binding ElementName=Root, Path=Bytes}">
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type local:ByteValue}">
        <StackPanel Orientation="Horizontal">
          <TextBox Text="{Binding Character}" /> <!-- SHow/edit the text character representation -->
          <TextBox Text="{Binding Value}" /> <!-- Show/edit the byte value -->
        </StackPanel>
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Window>

ByteValue.cs

public class ByteValue : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler? PropertyChanged;
  public ByteValue(byte value) => this.Value = value;

  private byte value;
  public byte Value
  {
    get => this.value;
    set
    {
      this.value = (byte)value;
      OnPropertyChanged(null);
    }
  }

  public char Character
  {
    get => (char)this.Value;
    set
    {
      this.Value = (byte)value;
      OnPropertyChanged(null);
    }
  }

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

ByteValueCollection.cs

public class ByteValueCollection : ObservableCollection<ByteValue>
{
  public ByteValueCollection()
  {
  }

  public ByteValueCollection(string hexString) : this(hexString, ' ')
  {
  }

  public ByteValueCollection(string hexString, char separator)
  {
    string[] hexTextValues = hexString.Split(separator);
    if (hexTextValues.Length != 1)
    {
      for (int index = 0; index < hexTextValues.Length; index++)
      {
        string hexTextValue = hexTextValues[index];
        byte byteValue = Convert.ToByte(hexTextValue, 16);
        var item = new ByteValue(byteValue);
        Add(item);
      }
    }
    else
    {
      for (int index = 0; index < hexString.Length; index += 2)
      {
        string hexTextValue = hexString.Substring(index, 2);
        byte byteValue = Convert.ToByte(hexTextValue, 16);
        var item = new ByteValue(byteValue);
        Add(item);
      }
    }
  }

  public ByteValueCollection(IEnumerable<ByteValue> collection) : base(collection)
  {
  }

  public ByteValueCollection(List<ByteValue> list) : base(list)
  {
  }

  public byte[] ToByteArray()
  {
    byte[] bytes = new byte[this.Count];
    for (int index = 0; index < bytes.Length; index++)
    {
      byte value = this[index].Value;
      bytes[index] = value;
    }

    return bytes;
  }

  public char[] ToCharArray()
  {
    char[] chars = new char[this.Count];
    for (int index = 0; index < chars.Length; index++)
    {
      char value = this[index].Character;
      chars[index] = value;
    }

    return chars;
  }

  public string ToHexString()
  {
    byte[] bytes = new byte[this.Count];
    for (int index = 0; index < bytes.Length; index++)
    {
      byte value = this[index].Value;
      bytes[index] = value;
    }

    return Convert.ToHexString(bytes);
  }

  public string ToHexString(char separator)
  {
    byte[] bytes = ToByteArray();
    string hexString = Convert.ToHexString(bytes);
    var hexStringBuilder = new StringBuilder(hexString);
    int separatorIndex = 2;
    for (int readInndex = 0; readInndex < this.Count - 1; readInndex++)
    {
        
      hexStringBuilder.Insert(separatorIndex, separator);
      separatorIndex += 3;
    }

    return hexStringBuilder.ToString();
  }
}