ListBox bound to ObservableCollection not refreshing on view loaded - wpf

I have the following XAML containing a ListBox bound to an ObservableCollection
<ListBox
Margin="0,5"
Grid.Row="1"
Grid.ColumnSpan="3"
Visibility="{Binding ArePicturesAvailable, Converter={StaticResource BoolToVisConv}}"
SelectedItem="{Binding SelectedPicture}"
ItemsSource="{Binding Pictures, NotifyOnSourceUpdated=True}"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Margin="8"
Height="{Binding Size.Height}"
Width="{Binding Size.Width}"
Source="{Binding FullPath, Converter={StaticResource RelativeToFullConv}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Since there is noticeable delay in populating the ListBox, I am attempting to defer this until the ListBox's containing view has loaded using Dispatcher.BeginInvoke as illustrated below:
public class PictureMarkerEditorViewModel
{
private PictureSelectorViewModel _pictureSelectorViewModel;
public string PhotoCollectionDirectory { get; set; }
protected ISymbolEditor GetEditorImpl(ISymbolInfo symbolInfo)
{
var picSymbolInfo = (PictureMarkerSymbolInfo)symbolInfo;
_pictureSelectorViewModel = new PictureSelectorViewModel(picSymbolInfo);
var editor = new PictureMarkerSymbolEditor(_pictureSelectorViewModel, picSymbolInfo);
editor.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
var photos = GetPhotoCollection();
_pictureSelectorViewModel.Pictures.AddRange(photos);
}));
//var photos = GetPhotoCollection();
//_pictureSelectorViewModel.Pictures.AddRange(photos);
return editor;
}
public IEnumerable<Picture> GetPhotoCollection()
{
if (string.IsNullOrEmpty(PhotoCollectionDirectory))
return null;
if (!Directory.Exists(PhotoCollectionDirectory))
throw new ArgumentException("Cannot show images from invalid directory " + PhotoCollectionDirectory + ".");
var files = Directory.GetFiles(PhotoCollectionDirectory, "*.png", SearchOption.AllDirectories);
return files.Select(f => new Picture(Path.GetFileName(f), f, ImageUtils.GetDimensions(f)));
}
}
where PictureSelectorViewModel.Pictures is simply an ObservableCollection of Picture:
public class PictureSelectorViewModel
{
private readonly ObservableCollection<Picture> _pictures= new ObservableCollection<Picture>();
public ObservableCollection<Picture> Pictures
{
get
{
return _pictures;
}
}
}
and Picture is a simple class containing Name and FullPath properties:
public class Picture
{
public string Name { get;set}
public string FullPath {get; set;}
public Size Size {get;set;}
}
I simply cannot get the list populated after it has loaded. If I populate the Pictures collection before the view is loaded (the commented part of the code, I see the images.
Any one know why?
TIA.

Related

how to select an image in a list of image in wrappannel wpf

I add images from the filedialog in my programm. i want to kow how i can give them attribute like select_event on click for example to remove one.
Thanks in advance
XAML Code .
<Grid DockPanel.Dock="Top" Margin="5,5,5,5" Background="#FFA59A9A">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<WrapPanel Name="Picture_Holder" HorizontalAlignment="Center" Orientation="Horizontal"/>
</ScrollViewer>
</Grid>
C# code
OpenFileDialog OpenFile = new OpenFileDialog();
OpenFile.Multiselect = true;
OpenFile.Title = "Select Picture(s)";
OpenFile.Filter = "ALL supported Graphics| *.jpeg; *.jpg;*.png;";
if (OpenFile.ShowDialog() == true)
{
foreach(String file in OpenFile.FileNames)
{
Add_Image(file);
}
}
private void Add_Image(string file)
{
Console.WriteLine("Une image"+file);
Image new_img = new Image();
new_img.Source = new BitmapImage(new Uri(file));
Thickness img_thickness = new Thickness();
img_thickness.Bottom = 2;
img_thickness.Left = 2;
img_thickness.Right = 2;
img_thickness.Top = 2;
new_img.Margin = img_thickness;
new_img.MaxWidth = new_img.MaxHeight = 105;
Picture_Holder.Children.Add(new_img);
}
You should use a ListBox like shown below, because it has built-in support for item selection. The WrapPanel would go into the ItemsPanel property of the ListBox.
<ListBox x:Name="imageListBox" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" MaxWidth="105" MaxHeight="105" Margin="2"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then assign a collection of file path strings to its ItemsSource property:
imageListBox.ItemsSource = OpenFile.FileNames;
You can now get the file path of the selected image by the SelectedItem property of the ListBox.
In order to use BitmapImage items instead of strings - and thus get a BitmapImage as SelectedItem - write:
imageListBox.ItemsSource = OpenFile.FileNames
.Select(path => new BitmapImage(new Uri(path)));
The next step would be to have a view model with a property that holds the image collection:
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ImageSource> Images { get; }
= new ObservableCollection<ImageSource>();
private ImageSource selectedImage;
public ImageSource SelectedImage
{
get { return selectedImage; }
set
{
selectedImage = value;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(nameof(SelectedImage)));
}
}
}
You would assign an instance of the view model to the DataContext property of the MainWindow
DataContext = new ViewModel();
and bind to its properties in XAML:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Images}"
SelectedItem="{Binding SelectedImage}">
...
</ListBox>
To populate the Images collection in the view model:
var vm = (ViewModel)DataContext;
vm.Images.Clear();
foreach (var path in OpenFile.FileNames)
{
vm.Images.Add(new BitmapImage(new Uri(path)));
}
Remove the selected one by e.g.
vm.Images.Remove(vm.SelectedImage);
which could be executed in an ICommand in the view model.

Gong Solutions Drag drop + using ItemsControl + query

I am using the sample application provided along with the Gong solutions drag drop library.
The solution includes a listbox having itemssource and displaymemberpath set.
I have modified the application to include an itemscontrol and itemTemplate.
But the solution no longer works. There is an exception in the DragInfo.cs file.
Not sure if posting it here is correct.
But can someone help me with this. The sample code is pretty basic.
<ItemsControl
dragDropFramework:DragDrop.IsDragSource="True"
Grid.Column="0" ItemsSource="{Binding Pupils}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FullName}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding Schools}"
dragDropFramework:DragDrop.DropHandler="{Binding}"
dragDropFramework:DragDrop.IsDropTarget="True"
Grid.Column="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" BorderBrush="Brown" BorderThickness="2" Margin="2"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
class PupilViewModel
{
public string FullName { get; set; }
}
internal class WindowViewModel : IDropTarget
{
public ICollectionView Schools { get; private set; }
public ICollectionView Pupils { get; private set; }
public SideWindowViewModel()
{
var pupils = new ObservableCollection<PupilViewModel>
{
new PupilViewModel { FullName = "Alex Thompson" },
new PupilViewModel { FullName = "Tabitha Smith" },
new PupilViewModel { FullName = "Carl Pederson" },
new PupilViewModel { FullName = "Sarah Jones" },
new PupilViewModel { FullName = "Paul Lowcroft" }
};
this.Pupils = CollectionViewSource.GetDefaultView(pupils);
var schools = new SchoolViewModel { Name = "FirstSchool", Pupils = new ObservableCollection<PupilViewModel>() };
this.Schools = CollectionViewSource.GetDefaultView(schools);
}
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel)// && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}
public void Drop(DropInfo dropInfo)
{
throw new NotImplementedException();
}
The Window's dataContext, is set to an instance of WindowViewModel.
This code comes with Gong library also as a part of code project.
http://www.codeproject.com/Articles/43614/Drag-and-Drop-in-WPF
original code looks like this
<ListBox Grid.Column="1" ItemsSource="{Binding Schools.CurrentItem.Pupils}" DisplayMemberPath="FullName"
dd:DragDrop.IsDragSource="True" dd:DragDrop.IsDropTarget="True"/>
In case you haven't figured this one out yet - it looks like you commented out where it checks to see if the thing you are dragging over is a SchoolView. Since you are using DropTargetAdorners.Highlight it is trying to highlight what you are dragging over. Since there isn't anything you're getting the null reference error. So maybe go back to this?
public void DragOver(DropInfo dropInfo)
{
if (dropInfo.Data is PupilViewModel) && dropInfo.TargetItem is SchoolViewModel)
{
dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
dropInfo.Effects = DragDropEffects.Move;
}
}

Tile view for ListView in WPf

How to simulate a tile view for a ListView in WPF?
I was trying the example shown here. But I can't get to the right solution... But I don't want to use that solution as I it's too much specific. So how will be the way to accomplilsh that?
EDIT: I'm trying this now and seems to work...
<ListBox ItemsSource="{Binding Path=ListObservableUsers, ElementName=AdminWindow}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Image Source="{Binding Path=Picture}"></Image>
<Label Content="{Binding Path=Dni}"></Label>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Where the ElementName=AdminWindow comes from <Window .... x:Name="AdminWindow"
And I created my own ObservableCollection<MyUser>
public class MyUser
{
public MyUser(int id, string dni, Bitmap picture)
{
Id = id;
Dni = dni;
Image img = new Image();
FPhiMultipleSources.FromBitmapImage(img, picture);
Picture = img.Source;
}
public int Id { get; set; }
public string Dni { get; set; }
public ImageSource Picture { get; set; }
}
...
public UCAdminMain()
public UCAdminMain()
{
ListObservableUsers = new ObservableCollection<MyUser>();
InitializeComponent();
uiCurrent = SynchronizationContext.Current;
// Create users to add with its image
....
ListObservableUsers.Add(...);
}
And now I'm trying to put them inside a wrap panel. With no luck right now... Any ideas?
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
try to use a wrappanel
An ItemsControl with a WrapPanel as the ItemsContainer would probably be a good fit for what you are trying to do.

How do I bind IsChecked property of CheckBox within an ItemsControl?

I've got the following ItemsControl that gives me a check box for every database within the available collection. These checkboxes allow the user to select which ones to filter on. The databases to filter on are in a separate collection (FilteredDatabases). How exactly do I do this? I could add an InFilter property to the database item class. But, I don't want to start changing this code yet. The problem I can't get around in my head is the fact that I need to bind to a property that is not on the database item itself. Any ideas?
<ItemsControl ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding ???}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// In view model
public IBindingList FilteredDatabases
{
get;
private set;
}
public IBindingList AvailableDatabases
{
get;
private set;
}
Bind CheckBox.Command to routed command instance
Bind routed command to method
Use IBindingList.Add and IBindingList.Remove methods
The following code illustrates what you are trying to do, in order to do this you are better off using ObservableCollection instead of as your collection object, if an ItemsControl is bound to it it will automatically update the UI when viewmodels are added and removed.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ItemsControl Grid.Column="0" ItemsSource="{Binding AvailableDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl Grid.Column="1" ItemsSource="{Binding FilteredDatabases}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
View Models:
public class MainViewModel
{
private ObservableCollection<DBViewModel> _availableDatabases;
private ObservableCollection<DBViewModel> _filteredDatabases;
public ObservableCollection<DBViewModel> AvailableDatabases
{
get
{
if (_availableDatabases == null)
{
_availableDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>()
{
new DBViewModel(this) { Name = "DB1" , IsChecked = true},
new DBViewModel(this) { Name = "DB2" },
new DBViewModel(this) { Name = "DB3" },
new DBViewModel(this) { Name = "DB4" },
new DBViewModel(this) { Name = "DB5" },
new DBViewModel(this) { Name = "DB6" },
new DBViewModel(this) { Name = "DB7" , IsChecked = true },
});
}
return this._availableDatabases;
}
}
public ObservableCollection<DBViewModel> FilteredDatabases
{
get
{
if (_filteredDatabases == null)
_filteredDatabases = new ObservableCollection<DBViewModel>(new List<DBViewModel>());
return this._filteredDatabases;
}
}
}
public class DBViewModel
{
private MainViewModel _parentVM;
private bool _isChecked;
public string Name { get; set; }
public DBViewModel(MainViewModel _parentVM)
{
this._parentVM = _parentVM;
}
public bool IsChecked
{
get
{
return this._isChecked;
}
set
{
//This is called when checkbox state is changed
this._isChecked = value;
//Add or remove from collection on parent VM, perform sorting here
if (this.IsChecked)
_parentVM.FilteredDatabases.Add(this);
else
_parentVM.FilteredDatabases.Remove(this);
}
}
}
View models should also implement INotifyPropertyChanged, I omitted it since it was not necessary in this particular case.

Get name of selected listbox item (WPF C#)?

all. I have an app that scans a picture folder and displays the images along with their names in a listbox. Each image and image name (displayed in a textblock next to the image) is stored in a horizontal stackpanel inside the listbox.
I've been trying all afternoon to find a way of displaying the image name in a textbox when the user selects it in the listbox. Sounds very simple, and I'm sure it is, but I can't seem to get it to work.
Can anyone point me in the right direction as to the best way of doing this? Thanks.
Here is my xaml if it helps:
<Grid>
<ListBox ItemsSource="{Binding AllImages}" Margin="0,0,262,0" Name="listBox1" MouseLeftButtonDown="listBox1_MouseLeftButtonDown">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}" Width="50" Height="50" Margin="6"/>
<TextBlock Text="{Binding Name}" Margin="6" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Height="23" HorizontalAlignment="Left" Margin="265,148,0,0" Name="textBox1" VerticalAlignment="Top" Width="198" />
</Grid>
And my code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public class MyImage
{
private ImageSource _image;
private string _name;
public MyImage(ImageSource image, string name)
{
_image = image;
_name = name;
}
public override string ToString()
{
return _name;
}
public ImageSource Image
{
get { return _image; }
}
public string Name
{
get { return _name; }
}
}
public List<MyImage> AllImages
{
get
{
List<MyImage> result = new List<MyImage>();
string filePath = #"D:\Projects\Visual Studio 2010\WpfApplication5\WpfApplication5\bin\Debug\ImageFolder";
string[] files = Directory.GetFiles(filePath);
foreach (string filename in files)
{
try
{
result.Add(
new MyImage(
new BitmapImage(
new Uri(filename)),
System.IO.Path.GetFileNameWithoutExtension(filename)));
}
catch { }
}
return result;
}
}
}
Take a look at this question.
How do I bind a Listview SelectedItem to a Textbox using the TwoWay mode?
In your case use
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="265,148,0,0"
Name="textBox1"
VerticalAlignment="Top" Width="198"
Text="{Binding SelectedItem.Name, ElementName=listBox1}"/>
To retrieve the selected image from code, you have at least 3 options (I assume your images are represented by a class ImageItem)
Set IsSynchronizedWithCurrentItem to true on your ListBox, and use the following code to retrieve the selected item:
ICollectionView view = CollectionViewSource(AllImages);
ImageItem selectedImage = (ImageItem)view.CurrentItem;
Bind the SelectedItem of the ListBox to a property in your DataContext:
<ListBox .... SelectedItem="{Binding SelectedImage}">
Access the SelectedItem property directly from code-behind:
ImageItem selectedImage = (ImageItem)listBox1.SelectedItem;
Of course, if you just want to show the name in a TextBlock, you can use Russell Troywest's solution

Resources