I have a ListView with an Image and some TextBlocks in datatamplate. These controls bind information from an object of a collection but the url for image control is on a file. I tried to bind a string( that is a name to the file) and created a convert to retrieve the url.
xaml:
<ListView Name="list" ItemsSource="{x:Bind Player.PlayerHistory, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:HistoricoPartidas">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Image Source="{x:Bind name, Converter={StaticResource ResourceKey=ItemConvert} }" />...
converter:
class ItemConvert: IValueConverter {
public object Convert(object value, Type targetType, object parameter, string language) {
string s = value.ToString(); //value is binding from an object of a collection
ControlFile controle = new ControlFile();//class with file handler
Character ch = controle.get(s).Result;//return a Character from file "s"
return ch.Icon_URL;
}
I created the code above but it causes a deadlock (.Result?). My question is if there is a way to retrieve the data from the file an use in image control avoiding deadlock?
panda is right, you really shouldn't open the file in the converter. This way, every time Convert() is called, you'll open the file and read from it, which is taking too much time and resources unnecessarily. Not to mention it hangs your UI thread and causes a deadlock. You should open the file before or while you are populating your ListView, read the list of URLs from it and bind to that (observable) collection instead.
If you want to stick with your current solution (which I strongly advise you against), please read the accepted answer here, Mr. Cleary describes the problem perfectly and in depth.
That's how I would do it: You'll need to make PlayerHistory an ObservableCollection of items, so the ListView will get notified when new items are added or old ones are removed from it. Based on your code snippet I suppose you already do it this way. The second thing you'll need is that you have to make the items in PlayerHistory (let's call them HistoryViewModel for now) implement the INotfyPropertyChanged interface, they have to have a property (let's call it PictureUri), which will you bind to in your DataTemplate, and they have to raise the PropertyChanged event in the setter of this property. These written in code:
class HistoryViewModel : INotifyPropertyChanged
{
private Uri _pictureUri;
public Uri PictureUri
{
get
{
return _pictureUri;
}
set
{
if (value == _pictureUri)
return;
_pictureUri = value;
RaisePropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Then your DataTemplate will look like this:
<DataTemplate x:DataType="data:HistoryViewModel ">
<Grid HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Image Source="{Binding PictureUri}" /> ...
This way whenever you update the PictureUri property of the backing ViewModels, the ListView's items will automagically be updated too, thanks to data binding.