In short, I wanted to create an updatable Listbox, which would update upon choosing an option from a ComboBox
<ListBox Background="Transparent" Width="Auto" Height="Auto" HorizontalAlignment="Right" ScrollViewer.CanContentScroll="true" ItemsSource="{Binding Lessons, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding LessonText, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}">
<StackPanel>
</StackPanel>
</TextBlock>
<Image Source="{Binding LessonImage, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" HorizontalAlignment="Center" Width="auto" MaxWidth="400"/>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Changing a value of a variable Lesson fires an action, which fills an object list with elements
public class LessonData : BaseViewModel
{
public int _lessonID;
public string _lessonText;
public byte[] _lessonImage;
public int LessonID { get { return _lessonID; } set { _lessonID = value; OnPropertyChanged(nameof(LessonID)); } }
public string LessonText { get { return _lessonText; } set { _lessonText = value; OnPropertyChanged(nameof(LessonText)); } }
public byte[] LessonImage { get { return _lessonImage; } set { _lessonImage = value; OnPropertyChanged(nameof(LessonImage)); } }
}
public List<LessonData> editedLessons = new List<LessonData>();
public List<LessonData> EditedLessons { get { return editedLessons; } set { editedLessons = value; OnPropertyChanged(nameof(EditedLessons)); } }
public string Lesson { get { return _lesson; } set { _lesson = value; OnPropertyChanged(nameof(Lesson));
if (Lesson == "None")
{
EditedLessons.Clear();
IsLessonBeingEdited = false;
}
else
{
EditedLessons = lessonRepository.Obtain_Lesson_Content(Lesson);
foreach (LessonData p in EditedLessons) { Console.WriteLine(p.LessonID, p.LessonText, p.LessonImage); }
IsLessonBeingEdited = true;
}
} }
Here is a function from a repository
public List<LessonData> Obtain_Lesson_Content(string Lesson)
{
List<LessonData> lc = new List<LessonData>();
using (var connection = GetConnection())
using (var command = new SqlCommand())
{
connection.Open();
command.Connection = connection;
command.CommandText = "SELECT Id_Lesson_Content, Lesson_Text, Lesson_Image FROM [Lesson_Content] WHERE Id_Lesson_Title = (SELECT Id_Lesson FROM [Lesson_Title] WHERE Lesson_Title = @title)";
command.Parameters.Add("@title", SqlDbType.NVarChar).Value = Lesson;
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
LessonData model = new LessonData();
model.LessonID = (int)reader["Id_Lesson_Content"];
model.LessonText = reader["Lesson_Text"].ToString();
if (reader["Lesson_Image"] != System.DBNull.Value)
model.LessonImage = (byte[])reader["Lesson_Image"];
else
model.LessonImage = null;
lc.Add(model);
}
reader.NextResult();
}
foreach (LessonData p in lc) { Console.WriteLine(p.LessonID, p.LessonText, p.LessonImage); }
return lc;
}
}
However every time I fire the function I meet with the errors thrown off (in that case it's loading two objects, one with and one without an image):
System.Windows.Data Error: 1 : Cannot create default converter to perform 'two-way' conversions between types 'System.Byte[]' and 'System.Windows.Media.ImageSource'. Consider using Converter property of Binding. BindingExpression:Path=LessonImage; DataItem='LessonData' (HashCode=2419756); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='System.Byte[]' BindingExpression:Path=LessonImage; DataItem='LessonData' (HashCode=2419756); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
System.Windows.Data Error: 1 : Cannot create default converter to perform 'two-way' conversions between types 'System.Byte[]' and 'System.Windows.Media.ImageSource'. Consider using Converter property of Binding. BindingExpression:Path=LessonImage; DataItem='LessonData' (HashCode=10026414); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')
But to me the weirdest part is that trying to write all the list objects in a console throws these three errors:
can't convert from int to char[],
from string to int
from byte[] to int
foreach (LessonData p in lc) { Console.WriteLine(p.LessonID, p.LessonText, p.LessonImage); }
Like, why does it even happen? I had looked at my other identical/similar functions which work in the same fashion and none of them throws such compilation errors. On top of that, when I change order of object elements by putting LessonID as the last, it does not display errors, (however the only item displayed is LessonText).
Whatever is the reason, I believe it is hidden behind that error with reading the list.
The source of the error message is the binding expression
Source="{Binding LessonImage, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
While there is a built-in automatic type conversion from string
, Uri
and byte[]
to ImageSource
(the type of the Source
property) there is no automatic conversion back to those types (except string) which would be required for a TwoWay
Binding.
A TwoWay
Binding of the Source
property is pointless in the first place, because an Image element never changes that property by itself. The same applies to the Text
property binding of the TextBlock element.
So just use a OneWay
Binding, where setting UpdateSourceTrigger
is also pointless, because it affects only TwoWay
and OneWayToSource
Bindings.
Source="{Binding LessonImage}"
Besides that, the source property of the ItemsSource
binding must be a collection that implements the INotifyPropertyChanged
interface, e.g. an ObservableCollection instead of a List:
The property should also be readonly:
public ObservableCollection<LessonData> EditedLessons { get; }
= new ObservableCollection<Lesson>();
The Binding is also not TwoWay, but should just be
<ListBox ItemsSource="{Binding EditedLessons}" ...>
because a ListBox never actively changes the value of its ItemsSource
property. This does not mean that the collection referenced by the property can't change.