INotifyPropertChanged Updating Bound Listview/gridview - c#

Okay so the problem is that my INotifyPropertyChanged isnt updting the list view n XAML
DiscoveredData.NetworkedComputersResults = NetworkedComputers; < this is where it loads the data into the DataContext and then calls the iproperty notify changed.
ListView_LocalComputers.ItemsSource = DiscoveredData.NetworkedComputersResults; < using this works fine and i can see all my data however this apparantly not the way to do it.
since i know that i can load the data into the list view using the ItemsSource im thinking the problem is in the XAML.
i would be greatful if someone could point me in the right direction.
Also if you see that i am doing this incorrectly please advise, im fairly new at this coding language and would like to do it the right way
Thank you in advance
<ListView Name="ListView_LocalComputers" ItemsSource="{Binding NetworkedComputerResults}">
<ListView.View>
<GridView>
<GridViewColumn Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Border CornerRadius="2,2,2,2" Width="20" Height="20" Background="Transparent" BorderBrush="Transparent" Margin="3,3,3,3">
<Image HorizontalAlignment="Left" VerticalAlignment="Center" Width="12" Height="12" Source="{Binding Image}" Stretch="Fill" Margin="2,2,2,2"/>
</Border>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Local Computers" DisplayMemberBinding="{Binding ComputerName}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
//Constructor
public NetworkInformation()
{
InitializeComponent();
this.DataContext = DiscoveredData; //Defines the class to the view
Discovery();
}
//Method
public void Discovery()
{
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
DiscoveredData.NetworkedComputersResults = NetworkedComputers;
ListView_LocalComputers.ItemsSource = DiscoveredData.NetworkedComputersResults;
}
private class GetIcon
{
public BitmapImage IconStorage { get; set; }
public BitmapImage LoadIcon(String IconPath)
{
BitmapImage GeneratedIcon = new BitmapImage();
GeneratedIcon.BeginInit();
GeneratedIcon.UriSource = new Uri("pack://application:,,," + IconPath, UriKind.RelativeOrAbsolute);
GeneratedIcon.EndInit();
IconStorage = GeneratedIcon;
return GeneratedIcon;
}
}
public class NetworkData : INotifyPropertyChanged
{
#region Property Notify Standard for all classes
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
#region Bound Data To View
private List<DiscoveredComputer> _NetworkedComputersResults;
public List<DiscoveredComputer> NetworkedComputersResults {
get { return _NetworkedComputersResults; }
set
{
_NetworkedComputersResults = value;
NotifyPropertyChanged("NetworkedComputersResults");
}
}
#endregion
public class DiscoveredComputer : NetworkData
{
public string ComputerName { get; set; }
public BitmapImage Image { get; set; }
public String MyToolTip { get; set; }
}
}

You should use ObservableCollection. It implements INotifyCollectionChanged which notifies when a collection changed, not just a single Item.
The items themselves should implement INotifyPropertyChanged of course...

You are binding a plain List<T> to your ListView. However this works fine, it will not fulfill the requirement of dynamic updating the list view when items are added / removed.
If you need the dynamic add/remove elements in your ListView simply use an ObservableCollection<T> instead of the List`.
private ObservableCollection<DiscoveredComputer> _NetworkedComputersResults;
public ObservableCollection<DiscoveredComputer> NetworkedComputersResults {
get { return _NetworkedComputersResults; }
set
{
_NetworkedComputersResults = value;
NotifyPropertyChanged("NetworkedComputersResults");
}
}
If all you need is elements be dynamically be added/removed, then the elements in the observable collection do not need to implement the INotifyPropertyChanged interface.

public class NetworkData
{
public NetworkData()
{
NetworkedComputersResults = new ObservableCollection<DiscoveredComputer>();
}
public ObservableCollection<DiscoveredComputer> NetworkedComputersResults{get;set;}
}
DiscoveryMethod
public void Discovery()
{
GetIcon Icon = new GetIcon();
BitmapImage IconOfComputer = null;
List<DiscoveredComputer> NetworkedComputers = new List<DiscoveredComputer>();
DirectoryEntry Discover = new DirectoryEntry("WinNT://Workgroup");
BitmapImage On = Icon.LoadIcon(#"/Images/Icons/ComputerOn.ico");
BitmapImage Off = Icon.LoadIcon(#"/Images/Icons/ComputerOff.ico");
foreach (DirectoryEntry Node in Discover.Children)
{
try
{
if (Node.Properties.Count > 0)
{
IconOfComputer = On;
}
}
catch
{
IconOfComputer = Off;
}
if (Node.Name != "Schema") { NetworkedComputers.Add(new DiscoveredComputer { Image = IconOfComputer, ComputerName = Node.Name, MyToolTip = "Node Type = " + Node.SchemaEntry.Name }); }
}
//Use Clear and Add .Dont assign new instance DiscoveredData.NetworkedComputersResults=new ....
DiscoveredData.NetworkedComputersResults.Clear();
foreach (var item in NetworkedComputers)
{
DiscoveredData.NetworkedComputersResults.Add(item);
}
}
I hope this will help.From my personal View it would be good If you create this Discovery method in ViewModel and would Call it from the Constructor of ViewModel . It seems two way communication like you setting property of ViewModel from View thats the job of Binding not code behind

Related

How to update UI from List inside ObservableCollection?

I have ObservableCollection of DeviceInformation which is added in MainWindowViewModel and linked with DataContext.
public partial class MainWindow : Window
{
MainWindowViewModel viewModel = new MainWindowViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel;
}
}
Here is the MainWindowViewModel:
public class MainWindowViewModel : ViewModelBase
{
private ObservableCollection<DeviceInformation> allDeviceInfo = new ObservableCollection<DeviceInformation>();
public MainWindowViewModel()
{
// here some of the commands
}
public ObservableCollection<DeviceInformation> AllDeviceInfo
{
get { return allDeviceInfo; }
set
{
allDeviceInfo = value;
this.RaisePropertyChanged(nameof(AllDeviceInfo));
}
}
}
The RaisePropertyChanged is done with implementing ViewModelBase which looks like this:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
this.RaisePropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void RaisePropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
Inside my DeviceInformation I have a List of SyntaxMessages:
public class DeviceInformation : ViewModelBase
{
private List<SyntaxMessages> list = new List<SyntaxMessages>();
private string test = "";
public List<SyntaxMessages> ComConsoleMessages{
get { return list; } // get method
set
{
list = value;
RaisePropertyChanged(nameof(ComConsoleMessages));
} // set method
}
public string Test{
get { return test; } // get method
set
{
test = value;
RaisePropertyChanged(nameof(Test));
} // set method
}
}
This is how the SyntaxMessages looks:
public class SyntaxMessages : ViewModelBase
{
#region private values
private string message = "";
private string status = "";
private string color = "Transparent";
#endregion
#region Public values
public string Message {
get { return message; }
set
{
message = value;
RaisePropertyChanged(nameof(Message));
}
}
public string Status {
get { return status; }
set
{
status = value;
RaisePropertyChanged(nameof(Status));
}
}
public string Color {
get { return color; }
set
{
color = value;
RaisePropertyChanged(nameof(Color));
}
}
#endregion
}
So when I running my program and connecting device to it will collect and add information the the DeviceInformation and this will be added to ObervableCollection of DeviceInformation. This will update my MainTabControl by adding new tab and binding many strings like "Test" (there is more then one) to the TextBoxes, and also update the SubTabControl which is inside the main one. Inside both of the TabItems inside SubTabControl I also have a ListView to which I want link the List of SyntaxMessages this looks like this:
<ListView
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="5"
MinHeight="40"
Padding="0"
Margin="2"
ItemsSource="{Binding ComConsoleMessages, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView
AllowsColumnReorder="False">
<GridViewColumn
DisplayMemberBinding="{Binding Message}"
Header="Message" />
<GridViewColumn
Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Status}" Foreground="{Binding Color}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Problem
All works fine except a ListView. When I add some SyntaxMessages to the List of SyntaxMessages called ComConsoleMessages then I have to switch between SubTabControl tabs(SubTabItem1/2) to see ListView updated. I want to update ListView every single time when new message is added to the List of SyntaxMessages which is inside DeviceInfromation which is indside ObservableCollection of DeviceInfromations that is linked via MainWindowViewMode to the window DataContext.
Here is the view:

use dataBinding to put images into a ListViewItem according to a web Service values

I have a listview in wich each ListViewItem has a list of default images, those images will be replaced by other images if the value recovered from a web service is different from 0
this is the json data:
{
success: 1,
total: 2,
locals: [
{
id_local: "82",
fav: 0,
aime: 0,
aimepas: 0,
},
{
id_local: "83",
fav: 1,
aime: 1,
aimepas: 0,
}
]
}
I tried this code:
ObservableCollection<Item> Locals = new ObservableCollection<Item>();
public async void getListePerSearch()
{
UriS = "URL";
var http = new HttpClient();
http.MaxResponseContentBufferSize = Int32.MaxValue;
var response = await http.GetStringAsync(UriS);
var rootObject1 = JsonConvert.DeserializeObject<NvBarberry.Models.RootObject>(response);
listme.ItemsSource = rootObject1.locals;
foreach (var item in listme.Items.Cast<Locals>())
{
if (item.fav == 1)
{
btnStar.Background = new SolidColorBrush(Colors.Yellow); //yellow
//Debug.Write("fav=1");
}
else
{
btnStar.Background = new SolidColorBrush(Colors.Gray);//Gray
//Debug.Write("fav=0");
}
if (item.aime == 1)
{
coeur.Source = new BitmapImage(new Uri("ms-appx:///images/11.png", UriKind.Absolute));
//Debug.Write("aime=1");
}
else
{
coeur.Source = new BitmapImage(new Uri("ms-appx:///images/1.png", UriKind.Absolute));
//Debug.Write("aime=0");
}
if (item.aimepas == 1)
{
deslikeimage.Source = new BitmapImage(new Uri("ms-appx:///images/22.png", UriKind.Absolute));
//Debug.Write("aimepas=1");
}
else
{
deslikeimage.Source = new BitmapImage(new Uri("ms-appx:///images/2.png", UriKind.Absolute));
//Debug.Write("aimepas=0");
}
}
and this is Locals.cs:
public class Locals
{
public int fav { get; set; }
public int aime { get; set; }
public int aimepas { get; set; }
}
and this is the xaml file:
<ListView x:Name="listme">
<ListView.ItemTemplate >
<DataTemplate >
<Grid>
...
<Button Background="Gray" x:Name="btnStar"/>
<Button>
<Image Source="images/1.png" x:Name="coeur"/>
</Button>
<Button>
<Image Source="images/2.png" x:Name="deslikeimage"/>
</Button>
</Grid>
</DataTemplate >
</ListView.ItemTemplate >
</ListView >
so my question is,how can I use DataBinding in my case
thanks for help
Your class Item must implement INotifyPropertyChanged to notify to the view when some of your properties changed after that you need to set your ObservableCollection Locals as ItemsSource in your List control.
Here is the doc.
https://msdn.microsoft.com/library/windows/apps/windows.ui.xaml.data.inotifypropertychanged.propertychanged.aspx
You need to understand what is Binding How to use it.
http://blogs.msdn.com/b/jerrynixon/archive/2012/10/12/xaml-binding-basics-101.aspx
Here is an example:
public class Employee : INotifyPropertyChanged
{
private string myUrl;
private string myUrl2;
public string MyUrl
{
get { return myUrl; }
set
{
_name = value;
RaisePropertyChanged("MyUrl");
}
}
public string MyUrl2
{
get { return myUrl2; }
set
{
_organization = value;
RaisePropertyChanged("MyUrl2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Now in your foreach:
foreach (var item in MyOriginalCollection)
{
your logic here...
Locals.Add(Item);
}
Finally you need to set your Locals Collection to your ListControl.
ObservableCollection notifies to the view when some object is added or removed.
Here is the documentation
https://msdn.microsoft.com/en-us/library/ms668604(v=vs.110).aspx
In Xaml
you need to personalize your ItemTemplate of your ListControl
<ListView x:Name="myList"
>
<ListView.ItemTemplate>
<DataTemplate>
<Button>
<Image Source="{Binding MyUrl}" x:Name="coeur"/>
</Button>
<Button>
<Image Source="{Binding MyUrl2}" x:Name="coeur"/>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I hope this information and example would be useful for you.
I know there are many concepts to learn.
Here a final example showing how to use Binding Approach
http://10rem.net/blog/2012/03/27/tip-binding-an-image-elements-source-property-to-a-uri-in-winrt-xaml

Autoscroll ListView in WPF using MVVM

I try to have a list that automatically scrolls to the end when adding a new line.
Here is a simple MVVM example in which I want to integrate :
There is a button that adds lines to the list when clicked.
Model code :
public class Student
{
public string Lastname {get; set;}
public string Firstname {get; set;}
public Student(string lastname, string firstname) {
this.Lastname = lastname;
this.Firstname = firstname;
}
}
public class StudentsModel: ObservableCollection<Student>
{
private static object _threadLock = new Object();
private static StudentsModel current = null;
public static StudentsModel Current {
get {
lock (_threadLock)
if (current == null)
current = new StudentsModel();
return current;
}
}
private StudentsModel() {
for (int i = 1; i <= 50; i++)
{
Student aStudent = new Student("Student " + i.ToString(), "Student " + i.ToString());
Add(aStudent);
}
}
public void AddAStudent(String lastname, string firstname) {
Student aNewStudent = new Student(lastname, firstname);
Add(aNewStudent);
}
}
ViewModel code :
public class MainViewModel : ViewModelBase
{
public StudentsModel Students { get; set; }
public MainViewModel()
{
Students = StudentsModel.Current;
}
private ICommand _AddStudent;
public ICommand AddStudent
{
get
{
if (_AddStudent == null)
{
_AddStudent = new DelegateCommand(delegate()
{
Students.AddAStudent("New Student lastname", "New Student firstname");
});
}
return _AddStudent;
}
}
View code :
<Window x:Class="demo.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:demo.Commands">
<Grid>
<ListView Grid.Row="2" BorderBrush="White" ItemsSource="{Binding Path=Students}"
HorizontalAlignment="Stretch">
<ListView.View>
<GridView>
<GridViewColumn Header="Lastname" DisplayMemberBinding="{Binding Path=Lastname}" />
<GridViewColumn Header="Firstname" DisplayMemberBinding="{Binding Path=Firstname}" />
</GridView>
</ListView.View>
</ListView >
<Button Content="Add" Command="{Binding AddStudent}" Margin="601.94,36.866,96.567,419.403" />
</Grid>
Thank you
I wrote a simple AttachedProperty that I use to auto-scroll to the bottom when a bound ObservableCollection changes:
public class AutoScroller : Behavior<ScrollViewer>
{
public object AutoScrollTrigger
{
get { return (object)GetValue( AutoScrollTriggerProperty ); }
set { SetValue( AutoScrollTriggerProperty, value ); }
}
public static readonly DependencyProperty AutoScrollTriggerProperty =
DependencyProperty.Register(
"AutoScrollTrigger",
typeof( object ),
typeof( AutoScroller ),
new PropertyMetadata( null, ASTPropertyChanged ) );
private static void ASTPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs args )
{
var ts = d as AutoScroller;
if( ts == null )
return;
// must be attached to a ScrollViewer
var sv = ts.AssociatedObject as ScrollViewer;
// check if we are attached to an ObservableCollection, in which case we
// will subscribe to CollectionChanged so that we scroll when stuff is added/removed
var ncol = args.NewValue as INotifyCollectionChanged;
// new event handler
if( ncol != null )
ncol.CollectionChanged += ts.OnCollectionChanged;
// remove old eventhandler
var ocol = args.OldValue as INotifyCollectionChanged;
if( ocol != null )
ocol.CollectionChanged -= ts.OnCollectionChanged;
// also scroll to bottom when the bound object itself changes
if( sv != null && ts.AutoScroll )
sv.ScrollToBottom();
}
private void OnCollectionChanged(object sender, EventArgs args)
{
App.Current.Dispatcher.Invoke(delegate {
(this.AssociatedObject as ScrollViewer).ScrollToBottom();
});
}
}
Note: I use Rx to subscribe to the CollectionChanged event, but that can be done with normal .NET event handling plus Dispatcher.Invoke to get the .ScrollToBottom() call on the UI thread.
Also Note: This attached property is in a Behavior class called TouchScroller, which does other stuff too, but it can be simplified to just a simple attached property.
EDIT:
To use it, in the xaml you would simply bind the property from the viewmodel:
<ScrollViewer ...>
<i:Interaction.Behaviors>
<util:TouchScroller AutoScrollTrigger="{Binding Students}" />
</i:Interaction.Behaviors>
...
</ScrollViewer>
EDIT2:
I edited the code to contain a full Behavior. I use a Behavior instead of simpley a static class with an attached behavior because it gives access to .AssociatedObject, which is the ScrollViewer that you need to call .ScrollToBottom() on. Without using a behavior, you would have to keep track of these objects manually. I also removed the Rx subscription and added simple event handlers and Dispatcher.Invoke.
This is a stripped down and modified version of what I use, but I haven't tested this version.

Cannot get Silverlight HierarchicalDataTemplate to display IsolatedStorage folders

This is my first foray into Hierarchical data and am having a bit of a problem.
In Silverlight 4, I am trying to get a list of isolated storage folders to display in a TreeView. Nothing displays at all. My Treeview is completely blank. What am I missing? I am getting data and it is correct.
Any help would be appreciated.
XAML
<sdk:TreeView x:Name="FolderTreeView" Grid.Column="0" Margin="0,0,3,0" ItemsSource="{Binding _Folders}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Folders}">
<TextBlock Margin="0" Text="{Binding Name, Mode=OneWay}"/>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
CS
internal class Folder
{
public Folder() { Folders = new List<Folder>(); }
public string Name { get; set; }
public List<Folder> Folders { get; set; }
}
private List<Folder> _Folders = new List<Folder>();
public OpenFileDialog()
{
InitializeComponent();
ifs = IsolatedStorageFile.GetUserStoreForApplication();
var folder = new Folder
{
Name = "Root",
Folders = (from c in ifs.GetDirectoryNames()
select new Folder
{
Name = c,
Folders = LoadFolders(c)
}).ToList()
};
_Folders.Add(folder);
FolderTreeView.DataContext = new { _Folders };
}
private List<Folder>LoadFolders(string folderName)
{
if(folderName == null)
return null;
return (from c in ifs.GetDirectoryNames(folderName + "\\*.*")
select new Folder
{
Name = c,
Folders = LoadFolders(c)
}).ToList();
}
Thanks
A few things
<sdk:TreeView x:Name="FolderTreeView" Grid.Column="0" Margin="0,0,3,0"
ItemsSource="{Binding _Folders}">
you can't bind to private members.
You need to use ObservableCollections instead of Lists. The binding manager effectively listens for the CollectionChanged events fired by ObservableCollection and notifies the bound controls.
You'll want to implement INotifyPropertyChanged and raise PropertyChanged notifications in your property setters.
Finally, have you set the DataContext for the Treeview?
Also, look in your Output debug window for errors relating to binding.
Edit, ok try:
FolderTreeView.DataContext = this;
and wrap _Folders in a property
public ObservableCollection <Folder> Folders
{
get
{
return _Folders;
}
set
{
_Folders = value;
OnPropertyChanged("Folders");
}
}
change your binding to
<sdk:TreeView x:Name="FolderTreeView" Grid.Column="0" Margin="0,0,3,0"
ItemsSource="{Binding Folders}">
I've changed things quite a bit,
public class Folder : INotifyPropertyChanged
{
public Folder(string folderName)
{
Name = folderName;
Folders = new ObservableCollection<Folder>();
var _ifs = IsolatedStorageFile.GetUserStoreForApplication();
if (folderName != null)
{
Folders = new ObservableCollection<Folder>(
(from c in _ifs.GetDirectoryNames(folderName + "\\*.*")
select new Folder(folderName + "\\" + c)
));
}
else
{
Folders = new ObservableCollection<Folder>(
(from c in _ifs.GetDirectoryNames()
select new Folder(folderName + "\\" + c)
));
}
}
public string Name { get; set; }
private ObservableCollection<Folder> _Folders;
public ObservableCollection<Folder> Folders
{
get { return _Folders; }
set { _Folders = value; OnPropertyChanged("RootFolder"); }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class OpenFileDialog : UserControl
{
public OpenFileDialog()
{
InitializeComponent();
RootFolder = new Folder (null);
RootFolders = new ObservableCollection<Folder>();
RootFolders.Add(RootFolder);
FolderTreeView.DataContext = this;
}
private Folder _RootFolder;
public Folder RootFolder
{
get { return _RootFolder; }
set { _RootFolder = value; }
}
private ObservableCollection<Folder> _RootFolders;
public ObservableCollection<Folder> RootFolders
{
get { return _RootFolders; }
set { _RootFolders = value; }
}
}
xaml
<sdk:TreeView x:Name="FolderTreeView" Margin="0,0,3,0" ItemsSource="{Binding RootFolders}">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding Path=Folders}">
<StackPanel>
<TextBlock Margin="0" Text="{Binding Name, Mode=OneWay}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>
Ok...problem solved. Don't understand why though.
I had the ChildWindow set as internal scope instead of public scope because I didn't want the window itself to be viewed external of my Silverlight class library. If anyone can answer why this would stop Hierarchical data binding but not standard data binding I would like to know.

how to retrived and save selected image to/from the listbox to/from isolatedstorage

I am retriving images from the media library to list box inside a wrap panel now I want to save the images selected (its a multiple selection listbox) to the isolatedstorage.
xaml of listbox
<ListBox Name="vaultbox" SelectionMode="Multiple"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}">
<TextBlock Text="It is so lonely here..." Visibility="Collapsed" />
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel ItemWidth="200" ItemHeight="200"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Name="image2"
Stretch="Fill"
VerticalAlignment="Top" Source="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I am lost here. I was trying to do this.
List<BitmapImage> vltBitmapImage = new List<BitmapImage>();
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
using(IsolatedStorageFileStream imageStream =
fileStorage.OpenFile(filepath,FileMode.Open,FileAccess.Read))
{
var imageSource=PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
vltBitmapImage.Add(bitmapImage);
}
}
this.vaultbox.ItemsSource = vltBitmapImage;
using above code i get this exception
'System.Invalid.Operation.Exception Items collection must be empty
before using ItemsSource'
dont know why its the same code almost from what i am showing pictures from media library to the listbox.
also from a similar list box above but different one i try to save the files to isolatedstorage but i can seem to find out how can i get the image name...
see here. Currently am using "name" what can I do for that?
foreach (BitmapImage item in lstImageFromMediaLibrary.SelectedItems)
{
string filepath =System.IO.Path.Combine("images", "name");
IsolatedStorageFileStream ifs = fileStorage.CreateFile(filepath);
{
var bmp = new WriteableBitmap(item);
bmp.SaveJpeg(ifs,item.PixelWidth,item.PixelHeight,0,90);
}
}
The exception occures because of this line:
<TextBlock Text="It is so lonely here..." />
You have a child of ListBox. And you try to add more.
I would like to recommend you using MVVM pattern: create the ViewModel and put the collection property inside. Also, it it very convenient to have the "selected item" property in this ViewModel class and bind the selected item of the ListBox to it.
It seems it is necessary to introduce the image item representation: it is image with name.
public class ImageViewModel : ViewModelBase
{
public ImageViewModel(string name, string image)
{
Name = name;
Image = image;
}
// Name or Path? Please make the choice by yourself! =)
public string Name { get; private set; }
public BitmapImage Image { get; private set; }
}
ViewModelBase class (implements INotifyPropertyChanged interface):
public abstract class ViewModelBase : INotifyPropertyChanged
{
protected ViewModelBase()
{
}
#region Implementation of INotifyPropertyChanged interface
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
var args = new PropertyChangedEventArgs(propertyName);
handler(this, args);
}
}
#endregion
}
The main (root) view model.
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
// Call this directly:
// var images = LoadImages();
// Images = images;
// or bind the Command property of Button to the LoadImagesCommand.
LoadImagesCommand = new RelayCommand((param) =>
{
var images = LoadImages();
Images = new ObservableCollection<ImageViewModel>(images);
});
}
private ObservableCollection<ImageViewModel> _images;
public ObservableCollection<ImageViewModel> Images
{
get { return _images; }
private set
{
if (value == _images)
return;
_images = value;
RaisePropertyChanged("Images");
}
}
private ImageViewModel _selectedImage;
public ImageViewModel SelectedImage
{
get { return _selectedImage; }
set
{
if (value == _selectedImage)
return;
_selectedImage = value;
RaisePropertyChanged("SelectedImage");
}
}
public ICommand LoadImagesCommand { get; private set; }
private List<ImageViewModel> LoadImages()
{
List<ImageViewModel> images = new List<ImageViewModel>();
// Part of the original code.
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
using (IsolatedStorageFileStream imageStream = fileStorage.OpenFile(filepath,FileMode.Open,FileAccess.Read))
{
var imageSource = PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
ImageViewModel imageViewModel = new ImageViewModel(fileName, bitmapImage);
images.Add(imageViewModel);
}
}
}
}
Window.
public class MainView : Window
{
public MainView()
{
InititializeComponent();
// It is just for simplicity. It would be better to use MVVM Light framework: ViewModel Locator!
DataContext = new MainViewModel();
}
}
XAML:
<!-- Please correct the binding for the Image property inside the ItemStyle -->
<ListBox Name="vaultbox"
ItemsSource="{Binding Images}"
SelectedItem={Binding SelectedImage}"
ItemContainerStyle="{StaticResource ListBoxItemStyle1}"
Height="493"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Width="442"
SelectionMode="Multiple">
...
</ListBox>
Also, please consider asynchronous loading: it won't freeze the UI.

Categories