The most appropriate bitmap class for the model - c#

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");
}
}

Related

C# UWP Change Image-Source in ListView-Binding

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.

WPF, C#, MVVM Notify ViewModel dynamically in changes from static vars in Model

I have ViewModel that makes some functions. Funcs are initiate by button, i have command on button click.
ViewModel.cs
class WindowViewModel : INotifyPropertyChanged
{
public WindowViewModel()
{
canExecute = true;
}
public ICommand ApplyKMMCommand //command for button click, works great, tested
{
get
{
return applyKMMCommand ?? (applyKMMCommand = new Commands.CommandHandler(() =>
ApplyKMMToNewImage(), canExecute));
}
}
private bool canExecute;
private ICommand applyKMMCommand;
public void ApplyKMMToNewImage()
{
ApplyKMM.Init(); //algorithm name
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public BitmapImage DisplayedImage //displaying image that i work with
{
get { return Bitmaps.Filepath; }
set { Bitmaps.Filepath = value; NotifyPropertyChanged(nameof(DisplayedImage)); }
}
}
Now, my ApplyKMM.Init()
class ApplyKMM
{
public static void Init()
{
Bitmaps.Filepath = //do some thing with it...
}
}
And my Models.Bitmaps.cs
static public BitmapImage Filepath
{
get { return filepath; }
set { filepath = value; }
}
static private BitmapImage filepath{ get; set; }
The problem is, when i make ApplyKMM.Init the Image control that is binded to View not change their value.
Without ApplyKMM i can do in ViewModel that thing:
DisplayedImage = //do things with bitmap...
And then, Image that is presented in View change (after making things with that image).
Can you tell me, how to notify ViewModel, that somewhere in code filepath from Models changed?
EDIT:
Binding in View Looks like standard binding:
<Image Source="{Binding DisplayedImage}"/>
Button click works too, i have problem only with communication between Models->ApplyKMM->ViewModel
EDIT2:
Properties Filepath is storage in Models folder, not folder where function ApplyKMM is. Look into my edit, i try to make something like:
Models -> ApplyKMM -> ViewModel. From Models, i get Filepath. Then, i using function ApplyKMM that is in another namespace. Then, after working on bitmap with ApplyKMM func i want to somehow notify ViewModel, that work on Model is done (for example, convert to grayscale) and i want to show that grayscale image in VM. It works, when i want to do Model -> ViewModel (ApplyKMM is in VM class) but i want to move out ApplyKMM away from ViewModel. And that when staris starts for me.
It appears like you want to notify when a static property changes. For this, you may use the StaticPropertyChanged event.
class ApplyKMM
{
#region Properties
static BitmapImage _Filepath;
public static BitmapImage Filepath
{
get { return _Filepath; }
set { if (_Filepath != value) { _Filepath = value; NotifyPropertyChanged(nameof(Filepath)); } }
}
#endregion
#region Static NotifyPropertyChanged
public static void NotifyStaticPropertyChanged(string propertyName)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
public void NotifyAllStaticPropertyChanged()
{
NotifyStaticPropertyChanged(string.Empty);
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
#endregion
}
Please take note that this is available from WPF Version 4.5.
You might also find this question interesting.
Basically it is not a good practice to notify changes of a static variable to an instance.
Then, let’s look at your code:
Bitmaps class does not implement INotifyPropertyChanged, so when Filepath changes, nothing will be notified (of course, it is a static property)
In your case, you should use local variable to hold DisplayedImages. Then changes on DisplayedImage should be updated by the binding.
BitmapImage _displayedImage;
public BitmapImage DisplayedImage
{
get { return displayedImage; }
set { displayedImage = value; NotifyPropertyChanged(nameof(DisplayedImage)); }
}

Robust way to generically initialize derived class' fields from base class?

I'm working on a project that has a variety of classes that derive from class View, where View provides some common methods and where the derived classes have fields that reference UI elements specific to that view. For example (in C#):
public abstract class View
{
public virtual void Initialize(){}
public virtual void Activate(){}
public virtual void Deactivate(){}
}
public class MainScreenView : View
{
private ImageView portraitImageView;
private ImageView landscapeImageView;
public MainScreenView(ImageView portrait, ImageView landscape)
{
portraitImageView = portrait;
landscapeImageView = landscape;
}
public override Initialize()
{
base.Initialize();
portraitImageView.Initialize(); // I would like to eliminate these calls!
landscapeImageView.Initialize();
}
public ImageView GetPortrait() { return portraitImageView; }
public ImageView GetLandscape() { return landscapeImageView; }
}
public class ImageView : View
{
private Image image;
public ImageView(Image image) { this.image = image; }
public override void Initialize() { base.Initialize(); image.Show(); }
public Image GetImage() { return image; }
}
In this example I have to call Initialize() on all the ImageViews when MainScreenView.Initialize is called. This feels error prone and inconvenient, because an Initialize() call has to be added every time a new sub-view is added to the MainScreenView composition. Therefore, I would like to eliminate the need for these calls in the derived classes, but I want to maintain the fields to the view-specific fields.
My idea is to add a collection of Views to the base class, which can then recursively be Initialized(), as follows:
public abstract class View
{
private List<View> subViews;
public virtual void Initialize()
{
foreach(View in subViews) { view.Initialize(); }
}
// This gets called before Initialize() is called.
public void AddSubViews(View[] views)
{
subViews = new List<View>();
subViews.AddRange(views);
}
}
public class MainScreenView : View
{
private ImageView portraitImageView;
private ImageView landscapeImageView;
public MainScreenView()
{
portraitImageView = ???;
landscapeImageView = ???;
}
// Even if View.subViews had been protected instead of private, this couldn't return an element from the list because the required index is unknown.
public ImageView GetPortrait() { return portraitImageView; }
public ImageView GetLandscape() { return landscapeImageView; }
}
public class ImageView : View
{
private Image image;
public ImageView() { this.image = ??? }
public override void Initialize() { base.Initialize(); image.Show(); }
public Image GetImage() { return image; } // Even if View.subViews had been protected instead of private, this couldn't return an element from the list because the required index is unknown.
}
However, because all the individual sub-views are now 'anonymous' (they are accessed by index instead of a field name), this won't work for me, unless I also add the sub-views through the derived class' constructor as I did in my first example, where I can't enforce that the objects passed to the contructor are the same objects that are in the list, or call AddSubViews from the derived class' constructor where the sub-views are manually added every time a new sub-view is added... which has the same issue as calling Initialize() on sub-views in the derived classes.
So my question is: is there a way to have all Initialization calls of sub-views being done in the View base class, while still being able to provide derived-class-specific elements without passing references to those elements to the derived class' constructor?
UPDATE: If you want to be sure all sub views are initialized (i.e. nobody forget to add them to base class list of sub views) you can use reflection approach. Here is main idea:
public interface IView // you don't need abstract class
{
void Initialize();
}
Use reflection to get all class fields which implement IView and was initialized:
public class View : IView
{
private IView portraitView;
private IView landscapeView;
// assign some values to sub-views
public virtual void Initialize()
{
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var subViews = from field in GetType().GetFields(flags)
let value = field.GetValue(this)
where value != null && value is IView
select (IView)value;
foreach (var subView in subViews)
subView.Initialize();
}
}
Simple as that. Now if anyone will add field of IView type to your class it will be initialized with other sub-views.
ORIGINAL ANSWER: Just add both views to base class subViews list:
public MainScreenView(ImageView portrait, ImageView landscape)
{
portraitImageView = portrait;
landscapeImageView = landscape;
AddSubViews(new View [] { portrait, landscape });
}
Also keep in mind that you are re-creating subViews list each time when you are trying to add new views:
public void AddSubViews(View[] views)
{
subViews = new List<View>(); // here
subViews.AddRange(views);
}
I believe it's better to create subViews list only once during class field initialization:
private readonly List<View> subViews = new List<View>();
public void AddSubViews(params View[] views) // you can use params here
{
subViews.AddRange(views);
}
Now you simply can call
AddSubViews(portrait, landscape);
You can use the following pattern:
public abstract class View
{
private IEnumerable<View> SubViews { get; }
protected View(params View[] subViews)
{
SubViews = subViews;
}
public void Initialize()
{
OnInitialize();
foreach (var view in SubViews)
{
view.Initialize();
}
}
protected abstract void OnInitialize();
}
Now you concrete views will look like:
public class MainScreenView : View
{
private readonly ImageView portraitImageView;
private readonly ImageView landscapeImageView;
public MainScreenView(ImageView portrait, ImageView landscape)
: base(portrait, landscape)
{
portraitImageView = portrait;
landscapeImageView = landscape;
}
protected override void OnInitialize() { }
public ImageView GetPortrait() { return portraitImageView; }
public ImageView GetLandscape() { return landscapeImageView; }
}
public class ImageView : View
{
private readonly Image image;
public ImageView(Image image)
: base()
{
this.image = image;
}
protected override void OnInitialize() { image.Show(); }
public string GetImage() { return image; }
}
And finally,
var main = new MainScreenView(new ImageView(portraitImage), new ImageView(landScapeImage));
main.Initialize();
will initialize correctly all views.

Getting a notification from another class

I have a WPF application that displays a window with various information in it. In my code I create an instance of a custom class that I created which reads information from RFID card reader. To keep it simple - every now and then someone would swipe their card using the card reader which would generate a string that I successfully capture using my custom class.
The problem that I have is that I need to return that value to the window application so that I can update the information displayed in the window based on the value read. This is not as simple as calling a function in the custom class and returning a value as I don't know when exactly someone would swipe their card.
One solution that I could think of was to make a timer and pool the custom class every second or so to check if someone swiped their card, however, I don't think that's an effective solution.
Since I'm relatively new to WPF I'm assuming that the right way to do it is using INotifyProperyChanged but I'm unsure how to do it. Open to any other suggestions as well, thank you!
Create an event on your CardReader class that you can listen to on your ViewModel.
class CardInfo
{
public string CardDetails { get; set; }
}
class CardSwipedEventArgs
: EventArgs
{
public CardInfo SwipedCard { get; set; }
}
interface ICardReader
{
event EventHandler<CardSwipedEventArgs> CardSwiped;
}
class MyViewModel : INotifyPropertyChanged
{
private ICardReader _cardReader;
private string _lastCardSwiped;
public ICardReader CardReader
{
get
{
return _cardReader;
}
set
{
_cardReader = value;
_cardReader.CardSwiped += OnCardSwiped;
}
}
private void OnCardSwiped(object sender, CardSwipedEventArgs e)
{
LastCardSwiped = e.SwipedCard.CardDetails;
}
public string LastCardSwiped
{
get
{
return _lastCardSwiped;
}
set
{
_lastCardSwiped = value;
this.OnPropertyChanged("LastCardSwiped");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Thank you all for your posts. Using events was definitely the way but it wasn't easy to understand how they worked. Your feedback definitely helped but this article helped me understand how events worked best and how to implement them so I could deal with the issue successfully:
http://www.codeproject.com/Articles/9355/Creating-advanced-C-custom-events
Create an event on the class that reads the data from RFID.
public class CardSweepedEventArgs : EventArgs {
private readonly string _data;
public string Data { get { return _data; } }
public CardSweepedEventArgs(string data) {
_data = data;
}
}
public class YourReadinClass {
public EventHandler<CardSweepedEventArgs> CardSweeped;
// rest of logic.
}
In your class then subscribe to the event and do the necessary.

Clear data from ObservableCollection

I've got a class that stores an image along with it's filename. I've created an ObservableCollection of this class, and bound that to a WPF listbox. (It's an image viewer of sorts)
I load 50 meg worth of data (currently about 10 images), then I want to remove one, some or all of the images from the observable collection and replace them (so that the memory footprint doesn't get too big while scrolling through many images).
For a start, I've actually got a button on the GUI to load the "next 10 images" - which it does, but it doubles the memory footprint. I've tried calling .Clear() and .ClearItems() from the collection, and also .Remove() and .RemoveAt(n) but the memory doesn't decrease. Am I mistunderstanding how ObservableCollection works?
Here's a synopsis of my code:
public class ImageDataList : ObservableCollection
{
public static ImageDataList Load(string path,int startVal, ImageDataList images)
{
// Load 10 images from defined start position
if (startVal<0)
startVal=0;
int endVal = startVal + 10;
if (endVal > Directory.GetFiles(path).Length)
endVal = Directory.GetFiles(path).Length;
// Attempt to clear all data from collection
images.ClearItems();
images.Clear();
while (images.Count>1)
{
images.RemoveAt(0);
}
for (int i = startVal; i < endVal; i++)
{
string filename = Directory.GetFiles(path)[i];
if (filename.Contains(".tif"))
{
ImageData imgData = ImageData.Load(filename);
images.Add(imgData);
}
}
return images;
}
}
After loading the images, it's passed to the GUI via:
listBox.DataContext = images;
I hope I've been clear... let me know if I should add anything!
Edit: For now I seem to have solved the problem by overwriting an item in the ObservableCollection, rather than trying to remove/clear it then add a new one. I still don't understand the memory problem though.
It may be that the garbage collector hasn't deleted the image objects from memory yet. The reason may be that you have enough memory available on your system so there is no need to delete the objects yet.
Does the memory consumption continue to rise when you load the next 10 images and the next 10 after that?
You should also consider disposing the images as Rakesh Gunijan suggests.
Why dont you create your own view model class like ImageDataViewModel and create observable collection of it instead of inheriting from ObservableCollection.
public class ImageDataViewModel : INotifyPropertyChanged, IDisposable
{
private string _id;
private string _imagePath;
public string Id
{
get
{
return _id;
}
set
{
_id = value;
OnPropertyChanged("Id");
}
}
public string ImagePath
{
get
{
return _imagePath;
}
set
{
_imagePath = value;
OnPropertyChanged("ImagePath");
}
}
private void OnPropertyChanged(string propertyName)
{
if(PropertyChanged!=null)
{
PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
}
}
public void Dispose()
{
//Do dispose of resources.
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class YourViewModel : INotifyPropertyChanged, IDisposable
{
private ObservableCollection _images;
public ObservableCollection Images
{
get
{
return _images;
}
set
{
_images = value;
OnPropertyChanged("Images");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void Dispose()
{
Images = null;
}
}

Categories

Resources