Reputation: 111
I know that there were many questions about this and I searched a lot and tried everything which I found about this but I can't get it working. Simply put, for some reason I'm unable to show an image inside a ListView item template.
So I have this ItemViewModel class:
public class ItemViewModel : BaseViewModel, IItemViewModel
{
public ItemViewModel()
{
if (dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if (eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
SaveCommand = new Command(SaveChanges, true);
DeleteCommand = new Command(RemoveItem, true);
AddNewItemCommand = new Command(AddNewItem, true);
}
public ICommand SaveCommand { get; set; }
public ICommand DeleteCommand { get; set; }
public ICommand AddNewItemCommand { get; set; }
private Item data;
public int ID { get; private set; }
private string title;
public string Title
{
get { return title; }
set
{
title = value;
NotifyPropertyChanged("Title");
}
}
private string author;
public string Author
{
get { return author; }
set
{
author = value;
NotifyPropertyChanged("Author");
}
}
private string shortDescription;
public string ShortDescription
{
get { return shortDescription; }
set
{
shortDescription = value;
NotifyPropertyChanged("ShortDescription");
}
}
private string buyPrice;
public string BuyPrice
{
get { return buyPrice; }
set
{
buyPrice = value;
NotifyPropertyChanged("BuyPrice");
}
}
private string borrowPrice;
public string BorrowPrice
{
get { return borrowPrice; }
set
{
borrowPrice = value;
NotifyPropertyChanged("BorrowPrice");
}
}
private int quantity;
public int Quantity
{
get { return quantity; }
set
{
quantity = value;
NotifyPropertyChanged("Quantity");
}
}
private string detailedDescription;
public string DetailedDescription
{
get { return detailedDescription; }
set
{
detailedDescription = value;
NotifyPropertyChanged("DetailedDescription");
}
}
private string imagePath;
public string ImagePath
{
get { return imagePath; }
set
{
imagePath = value;
NotifyPropertyChanged("ImagePath");
}
}
private Image image;
public Image Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
public void SetData(Item item)
{
data = item;
ID = data.ID;
Author = data.Author;
Title = data.Title;
Quantity = data.Quantity;
ShortDescription = data.ShortDescription;
DetailedDescription = data.DetailedDescription;
BuyPrice = data.BuyPrice;
BorrowPrice = data.BorrowPrice;
Image = GetImage(data.ImagePath);
}
private Image GetImage(string imagePath)
{
var imageUri = new Uri(imagePath, UriKind.RelativeOrAbsolute);
var bitmapImage = new BitmapImage(imageUri);
var image = new Image
{
Source = bitmapImage
};
return Image;
}
private void SaveChanges()
{
UpdateChanges(data);
dalInterface.UpdateItem(data);
}
private void RemoveItem()
{
dalInterface.RemoveItem(data);
}
private void AddNewItem()
{
var newItem = new Item();
if (AllDataCorrect())
{
UpdateChanges(newItem);
dalInterface.AddNewItem(newItem);
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = true });
}
else
{
eventCenter.Publish(new AddItemEventArgs { OperationSuccess = false });
}
}
private void UpdateChanges(Item itemToUpdate)
{
itemToUpdate.Author = Author;
itemToUpdate.Title = Title;
itemToUpdate.BorrowPrice = BorrowPrice;
itemToUpdate.BuyPrice = BuyPrice;
itemToUpdate.DetailedDescription = DetailedDescription;
itemToUpdate.ShortDescription = ShortDescription;
itemToUpdate.Quantity = Quantity;
itemToUpdate.ImagePath = ImagePath;
}
private bool AllDataCorrect()
{
float val = -1.0F;
float.TryParse(BuyPrice, out val);
if (val <= 0.0F)
{
return false;
}
float.TryParse(BorrowPrice, out val);
if (val <= 0.0F)
{
return false;
}
if ((ShortDescription == string.Empty) ||
(DetailedDescription == string.Empty) ||
(Author == string.Empty) ||
(Title == string.Empty)
)
{
return false;
}
if (Quantity <= 0)
{
return false;
}
return true;
}
public void Clear()
{
Author = string.Empty;
Title = string.Empty;
ImagePath = string.Empty;
ShortDescription = string.Empty;
DetailedDescription = string.Empty;
BuyPrice = string.Empty;
BorrowPrice = string.Empty;
Quantity = 0;
}
}
And for this class I have the following user control:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Grid.RowSpan="4"
Style="{StaticResource ImageBorderStyle}">
<Image Source="{Binding Image, Mode=TwoWay}"
MinWidth="80"
MinHeight="80"
Stretch="UniformToFill"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Wiki"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding ShortDescription}"/>
</Border>
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource BaseBorderStyle}"
HorizontalAlignment="Left"
Width="100">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="About"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="Auto"
Text="{Binding DetailedDescription}"/>
</Border>
</Grid>
</Grid>
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border Grid.Row="0"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Title"
TextAlignment="Center"
HorizontalAlignment="Stretch"/>
</Border>
<Border Grid.Row="1"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Title}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Author"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Author}"/>
</Border>
<Border Grid.Row="4"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Quantity"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="5"
Grid.Column="0"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding Quantity}"/>
</Border>
<Border Grid.Row="0"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Buy Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="1"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BuyPrice}"/>
</Border>
<Border Grid.Row="2"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch">
<TextBlock Style="{StaticResource BaseTextBlockStyle}"
Text="Borrow Price"
TextAlignment="Center"/>
</Border>
<Border Grid.Row="3"
Grid.Column="1"
Style="{StaticResource DetailsBorderStyle}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBox Style="{StaticResource DataTextBoxStyle}"
HorizontalAlignment="Stretch"
Text="{Binding BorrowPrice}"/>
</Border>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource SaveButtonStyle}"/>
<Button Grid.Row="5"
Grid.Column="1"
Style="{StaticResource RemoveButtonStyle}"
HorizontalAlignment="Right"/>
</Grid>
</Grid>
Using this I want to show into a page, inside a list view, based on an observable collection, loaded from a database some items. The page view model is the follow:
public class ManageItemsViewModel : BaseViewModel, IManageItemsViewModel
{
public ManageItemsViewModel()
{
if(dalInterface == null)
{
dalInterface = ApplicationContext.Container.Resolve<IDalInterface>();
}
if(eventCenter == null)
{
eventCenter = ApplicationContext.Container.Resolve<IEventCenter>();
}
Items = new ObservableCollection<ItemViewModel>();
}
public ObservableCollection<ItemViewModel> Items { get; set; }
public void Refresh()
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var vm = Items.Where(v => v.ID.Equals(item.ID));
if(vm.Equals(null))
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
}
NotifyPropertyChanged("Items");
}
public void LoadData()
{
if(Items.Count == 0)
{
var dalItems = dalInterface.GetAllItems();
foreach(Item item in dalItems)
{
var newItemVm = (ItemViewModel)ApplicationContext.Container.Resolve<IItemViewModel>();
newItemVm.SetData(item);
Items.Add(newItemVm);
}
NotifyPropertyChanged("Items");
}
else
{
Refresh();
}
}
}
And the page view is the follow:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Margin="2"
Style="{StaticResource AddButtonStyle}"
Click="GoToAddNewItem"/>
<Button Grid.Row="0"
Margin="2"
HorizontalAlignment="Right"
Style="{StaticResource CloseButtonStyle}"
Click="GoToItems"/>
<ListView Grid.Row="1"
ItemsSource="{Binding Items, Mode=TwoWay}"
Margin="5">
<ListView.ItemTemplate>
<DataTemplate>
<templates:EditableItem/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
That page should be like (at the moment I only have one item in DB for tests):
The problem which I facing now is that I cannot see the Image inside this item template, although all the other properties are correctly shown inside the item template. I've tried to use the image path as I get it from DB and bind into item xaml the source path property to this, also I tried to have an ImageSource or BitmapImage property inside item VM to have the xaml image source bound to this property but without any success so far.
After reading many questions about and trying again and again without success I'm here... Any hints about what I'm doing wrong? Many thanks!
(P.S. The app which I'm working to is an UWP app, maybe it matters...)
Upvotes: 0
Views: 658
Reputation: 111
As I searched again and again about this problem and tried lots of suggested solutions I finally found a solution which is suitable for me and solves this problem. I implemented a little demo to show how exactly is working. The UWP app is not constraint to interact with the pictures folder only. The images can be loaded from an arbitrary folder on disk. Hope that this will help others to.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
PickFileCommand = new ActionCommand(PickFile, true);
}
public event PropertyChangedEventHandler PropertyChanged;
public ICommand PickFileCommand { get; set; }
private BitmapImage imageSrc;
public BitmapImage ImageSrc
{
get { return imageSrc; }
set
{
imageSrc = value;
NotifyPropertyChanged("ImgSource");
}
}
private async void PickFile()
{
var filePicker = new FileOpenPicker
{
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
filePicker.FileTypeFilter.Add(".jpg");
filePicker.FileTypeFilter.Add(".jpeg");
filePicker.FileTypeFilter.Add(".png");
StorageFile file = await filePicker.PickSingleFileAsync();
if (file != null)
{
var stream = await file.OpenAsync(FileAccessMode.Read);
var bitmap = new BitmapImage
{
UriSource = new Uri(file.Path, UriKind.Absolute)
};
await bitmap.SetSourceAsync(stream);
ImageSrc = bitmap;
}
}
protected void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
Now, the trick which solved the image problem is in View code behind class.
public sealed partial class MainPage : Page
{
private ViewModel dataContext;
public MainPage()
{
this.InitializeComponent();
dataContext = new ViewModel();
DataContext = dataContext;
}
**private void PageLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is ViewModel dc)
{
dc.PropertyChanged += Dc_PropertyChanged;
}
}
private void Dc_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName.Equals("ImgSource"))
{
if (DataContext is ViewModel dc)
{
ShowImage.Source = dc.ImageSrc;
}
}
}**
}
What was needed it was that the source of the Image UI element to be set explicitly. I made this by subscribing to the PropertyChanged event from ViewModel and set the image source.
Upvotes: 0
Reputation: 128098
You can't use an Image control as the value of the Source
property of another Image control.
Use an ImageSource
instead:
private ImageSource image;
public ImageSource Image
{
get { return image; }
set
{
image = value;
NotifyPropertyChanged("Image");
}
}
And change the GetImage
method to:
private ImageSource GetImage(string imagePath)
{
return new BitmapImage(new Uri(imagePath, UriKind.RelativeOrAbsolute));
}
That said, you could simply drop the Image
property and bind directly to ImagePath
. Built-in type conversion will automatically convert from a path string to ImageSource.
<Image Source="{Binding ImagePath}"/>
Note also that Mode=TwoWay
makes no sense for this Binding.
Upvotes: 2