C# UWP Change Image-Source in ListView-Binding - c#

I have a C# UWP solution for myself and I have defined the special manifest broadFileSystemAccess, so I can access all files from my PC directly.
I fill a ListView by setting the ItemsSource of the ListView to a ObservableCollection<someModel> variable (more details below). After tapping on it, I want to change it.
In class someModel I set the path to the image and the content as BitmapImage. I do this because, the file is on some path on my PC, e.g. E:\pix\some path\image.jpg, then read this file into the BitmapImage, so I can later bind it as the Image source. This is super slow and inefficient, but it works for now and I'll change it in the future.
class someModel
{
public string ImagePath { get; set; }
public BitmapImage ImageContent { get; set; }
}
and my XAML image:
<Image DataContext="{x:Bind Mode=TwoWay}" Source="{x:Bind ImageContent, Mode=OneWay}" Width="400" Stretch="UniformToFill" Tapped="lvImageEditImage_Tapped" />
my function lvImageEditImage_Tapped :
var picker = new Windows.Storage.Pickers.FileOpenPicker
{
ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail,
SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.PicturesLibrary
};
StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
var item = (sender as Image).DataContext as someModel;
item.ImagePath = file.Path;
BitmapImage bitmapImage = new BitmapImage();
using (IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read))
{
await bitmapImage.SetSourceAsync(fileStream);
}
item.ImageContent = bitmapImage;
}
While I now get the path, the image content bitmapImage doesn't change the viewed image.
Why doesn't the image update to the new BitmapImage content?

Please add the INotifyPropertyChanged interface to the data model.
public class someModel:INotifyPropertyChanged
{
private BitmapImage _imageContent;
public BitmapImage ImageContent
{
get => _imageContent;
set
{
_imageContent = value;
OnPropertyChanged();
}
}
public string ImagePath { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
After inheriting the INotifyPropertyChanged interface, you can use {x:Bind ImageContent, Mode=OneWay} to complete the binding. When the data source is modified, the UI interface will be notified to change.
Best regards.

Related

Serial Port Communication WPF MVVM [duplicate]

The following is part of my View in which I have bound an Image to a property in my ViewModel:
<Image Source="{Binding Image}" Grid.Column="2" Grid.ColumnSpan="2"/>
My ViewModel is this:
public class MainWindowViewModel : INotifyPropertyChanged
{
public BitmapImage Image
{
get { return _image; }
set
{
_image = value;
OnPropertyChanged();
}
}
Action _makeScannerAlwaysOnAction;
private BitmapImage _image;
public MainWindowViewModel()
{
AddNewPersonCommand = new RelayCommand(OpenFrmAddNewPerson);
FingerPrintScannerDevice.FingerPrintScanner.Init();
MakeScannerAlwaysOn(null);
}
private void MakeScannerAlwaysOn(object obj)
{
_makeScannerAlwaysOnAction = MakeScannerOn;
_makeScannerAlwaysOnAction.BeginInvoke(Callback, null);
}
private void Callback(IAsyncResult ar)
{
FingerPrintScannerDevice.FingerPrintScanner.UnInit();
var objFingerPrintVerifier = new FingerPrintVerifier();
objFingerPrintVerifier.StartVerifingProcess();
var ms = new MemoryStream();
ms.Position = 0;
objFingerPrintVerifier.MatchPerson.Picture.Save(ms, ImageFormat.Png);
var bi = new BitmapImage();
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
Thread.Sleep(2000);
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
//Image = bi;
_makeScannerAlwaysOnAction.BeginInvoke(Callback, null);
}
private void MakeScannerOn()
{
while (true)
{
if (FingerPrintScannerDevice.FingerPrintScanner.ScannerManager.Scanners[0].IsFingerOn)
{
return;
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
My Problem:
The problem is when I want to bind the Image it gives me the error
Must create DependencySource on same Thread as the DependencyObject
I have googled a lot and I have seen the post in SO but neither of them worked for me.
any kind of help would be very appreciated.
BitmapImage is DependencyObject so it does matter on which thread it has been created because you cannot access DependencyProperty of an object created on another thread unless it's a Freezable object and you can Freeze it.
Makes the current object unmodifiable and sets its IsFrozen property to true.
What you need to do is call Freeze before you update Image:
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
bi.Freeze();
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
as pointed out by #AwkwardCoder here is Freezable Objects Overview
While bi.Freeze(); worked for me in one case, I've seen no difference from adding/removing
Dispatcher.CurrentDispatcher.Invoke(() => Image = bi);
The second time I used DataTemplate in xaml and all the same classes as in the first case, but I kept getting the same Error.
This was a thing that helped:
Application.Current.Dispatcher.Invoke(() => Image = bi);
Maybe accepted answer can be improved because Dispatcher.CurrentDispatcher don't actually give you UI thread.

Binding image to embedded resource in wpf

I'm new to WPF, and I'm encountering a problem. I have a ListBox with images arranged in a floorplan, the image source is bound to a uri in the code. uri is to an embedded resource in the project. This all works fine on startup, until I have to change an image. No exceptions, but the images do not change. When I run in debug, I can see that even though the ObservableCollection items change, the mainwindow images do not.
Is this due to caching, or is it something else? I try to disable caching in the xaml, but I get the error The TypeConverter for "CacheMode" does not support converting from a string.
I'll do my best to be brief with the code and only include relevant information:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public ObservableCollection<ComputerInfo> CompListObs;
public ObservableCollection<ComputerInfo> compListObsNotifier
{
get { return CompListObs; }
set
{
CompListObs = value;
RaisePropertyChanged("compListObsNotifier");
}
}
//...
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
// This correctly loads the images from the xml and displays them on the main window:
var items = XDocument.Load(#"path to xml")
.Descendants("Computer")
.Select(i => new ComputerInfo {
MachineName = (string)i.Element("MachineName"),
Lat = (double)i.Element("Long"),
Lon = (double)i.Element("Lat"),
CurrentImage = ResourceHelper.LoadBitmapURIFromResource((string)i.Element("Img"))
}).ToList();
CompListObs = new ObservableCollection<ComputerInfo>(items);
}
public void MainTimer_Tick(object sender, EventArgs e)
{
// This runs fine and I can see the members of CompListObs are changing,
// but the images on the mainwindow are not changing.
foreach(var comp in CompListObs) { comp.CurrentImage = ResourceHelper.LoadBitmapURIFromResource("another image");
}
// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public class ComputerInfo : INotifyPropertyChanged
{
public ClientInfo ClientsInfo { get; set; }
public string MachineName { get; set; }
public Uri CurrentImage { set; get; }
public double Lat { get; set; }
public double Lon { get; set; }
public Uri currentImageNotifier
{
get { return CurrentImage; }
set
{
CurrentImage = value;
RaisePropertyChanged("compListObsNotifier");
}
}
// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void RaisePropertyChanged(string propName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Here is the xaml:
<ListBox ItemsSource="{Binding compListObsNotifier}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Path=CurrentImage}" Height="65"></Image>
</DataTemplate>
</ListBox.ItemTemplate>
<!-- after this, I place <ListBox.ItemsPanel> <ItemsPanelTemplate> and <Canvas> in the ListBox.
When you bind to an observable collection you are subscribing to changes in the collection (for instance, adding or removing items from the collection). This does not subscribe you to changes to properties on the individual items in the collection. As others have stated, if you want to bind to a property on the items, you will need to raise a PropertyChanged event when the property changes.
private Uri currentImage;
public Uri CurrentImage
{
get { return currentImage; }
set
{
currentImage = value;
RaisePropertyChanged("CurrentImage");
}
}
Note: You may want to wrap the setter in an if statement and use Uri.Compare() to determine if the given value is actually different from the current value. This way, you only raise a PropertyChanged event when the property actually changes.
Also, in your code example you are setting the CurrentImage property in this foreach loop:
foreach(var comp in CompListObs)
{
comp.CurrentImage = ResourceHelper.LoadBitmapURIFromResource("another image");
}
However, you're raising your PropertyChanged event from the currentImageNotifier property. It's okay to raise PropertyChanged events from a location outside of the property being modified, but you either need to replace the CurrentImage assignment in your foreach loop with currentImageNotifier, or modify your CurrentImage property to raise its own PropertyChanged event. As it stands, you're not actually raising a PropertyChanged event on the property you're binding to.
Honestly, it doesn't look like you even need the currentImageNotifier property. It's not doing anything you couldn't just do with the CurrentImage property directly.
When you are binding to CurrentImage and the value of CurrentImage changes, you must raise the property changed event for CurrentImage.
Based on the supplied code, you also could do this:
public Uri currentImageNotifier
{
get { return CurrentImage; }
set
{
CurrentImage = value;
RaisePropertyChanged("CurrentImage");
}
}

WPF image binding and INotifyPropertyChanged, issue with image display

I am trying to bind an image in a wpf application. I am using vs2010.
I am pasting code below and explain what I have done, what works and what doesn't.
XAML code:
<Image Name="newImage" ImageFailed="newImage_ImageFailed" HorizontalAlignment="Right" Width="auto" Height="auto" Margin="5" Source="{Binding imgSource}">
C# code:
public MainWindow()
{
InitializeComponent();
arraytoImage atim = new arraytoImage();
newImage.DataContext = atim;
}
Code below is in different namespace, where arraytoImage class is implemented. This class takes a cuda array, creates a bitmap and then converts it in into a bitmapimage using memorystream. For now, I am setting a random color to all the pixels, just to see if that binding works. But it doesn't. Below I have pasted a code that displays the image.
I am sure that bitmapimage is correctly created. I think the issue is incorrect binding.
class arraytoImage : INotifyPropertyChanged
{
// displays images (focused files)
private BitmapImage bitmapImage = new BitmapImage();
private BitmapImage testim = new BitmapImage();
public BitmapImage arraytoImageCon(cuFloatComplex[] dataIn, int wid, int ht)
{
//code that generates bitmapimage
}
public BitmapImage imgSource
{
get { return testim1; }
set
{
if (testim1 != value)
{
testim1 = value;
OnPropertyChanged("imgSource");
}
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Edit: calling arrayToImageCon:
public class ReadRawFiles
{
//Tons of code
public void focusdata()
{
//tons of code
arraytoImage atoi = new arraytoImage();
BitmapImage tmp= atoi.arraytoImageCon(datafft_azi, nazimuth,nrange);
atoi.imgSource=tmp;
}
}
My question is, what am I doing wrong.
Thanks a lot in advance. Kindly ask further details if I missed something.
Regards
binding is set to one instance. I was making multiple instances.

Getting Image Source from ListBox SelectedIndex on navigation in C#

I have a ListBox bind with images from SampleData source. On selection of the ListBox Item, I want to display the image in next page so I have passed the SelectedIndex on navigation but I am unable to get the image or display it. My code is below:`
//AlbumImages.cs
public class AlbumImages: ObservableCollection
{
}
//AlbumImage.cs
public class AlbumImage
{
public string title { get; set; }
public string content { get; set; }
}
//App.xaml.cs
private static MainViewModel viewModel = null;
public AlbumImages albumImages = new AlbumImages();
public int selectedImageIndex;
//MainPage.xaml.cs
private void listBoxPhoto_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (listBoxPhoto.SelectedIndex == -1) return;
this.NavigationService.Navigate(new Uri("/Photogallery.xaml?SelectedIndex=" + listBoxPhoto.SelectedIndex, UriKind.Relative));
}
//Photogallery.xaml.cs
// Reference to App
private App app = App.Current as App;
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
IDictionary<string, string> parameters = this.NavigationContext.QueryString;
if (parameters.ContainsKey("SelectedIndex"))
{
app.selectedImageIndex = Int32.Parse(parameters["SelectedIndex"]);
}
else
{
app.selectedImageIndex = 0;
}
LoadImage();
}
private void LoadImage()
{
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.UriSource=new Uri(app.albumImages[app.selectedImageIndex].content, UriKind.RelativeOrAbsolute);
image.Source = bitmapImage;
}`
To achieve your requirement, you have to make the photoCollection globally available to Photogallery page as well.
You can do this by assigning a copy of photoCollection (which you are using to bind to Listbox) to albumImages of App.xaml.cs from your MainPage.xaml.cs page
Note: There were some changes made your code in the question (I am not sure who did those), but the code is very near to working.

The most appropriate bitmap class for the model

I am writing a simple WPF application using MVVM.
What is the most convenient class to retrieve bitmaps from models and further data binding: Bitmap, BitmapImage, BitmapSource?
public class Student
{
public <type?> Photo
{
get;
}
}
Or maybe I can somehow convert Bitmap to BitmapSource using ViewModel?
I always use BitmapImage, it's quite specialized and offers nice properties and events that might be useful (e.g. IsDownloading, DownloadProgress & DownloadCompleted).
I suppose the more flexible way is to return photo (or any other bitmap) as a stream.
Furthermore, if the photo has been changed, the model should fire the photo changed event and the client should handle the photo changed event to retrieve a new one photo.
public class PhotoChangedEventArgs : EventArgs
{
}
public class Student
{
public Stream GetPhoto()
{
// Implementation.
}
public event EventHandler<PhotoChangedEventArgs> OnPhotoChanged;
}
public class StudentViewModel : ViewModelBase
{
// INPC has skipped for clarity.
public Student Model
{
get;
private set;
}
public BitmapSource Photo
{
get
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = Model.Photo;
image.EndInit();
image.Freeze();
return image;
}
}
public StudentViewModel(Student student)
{
Model = student;
// Set event handler for OnPhotoChanged event.
Model.OnPhotoChanged += HandlePhotoChange;
}
void HandlePhotoChange(object sender, PhotoChangedEventArgs e)
{
// Force data binding to refresh photo.
RaisePropertyChanged("Photo");
}
}

Categories

Resources