ObservableCollection Images in Listbox to Content Control master detail WPf - wpf

I have an observablecollection of Images that get populated via the following code:
<StackPanel Orientation="Horizontal" Grid.Column="0">
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True"
SelectedIndex="0" SelectedItem="{Binding CurrentItem}" />
</StackPanel>
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}"
Margin="9,0,0,0" Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Top"/>
However the Content Control is supposed to bind to the BigImageView via an ObservableCollection
BigImage = new ObservableCollection<Image>();
_listView = CollectionViewSource.GetDefaultView(BigImage);
_listView.CurrentChanged += new EventHandler(OnCurrentChanged);
public System.ComponentModel.ICollectionView BigImageView
{
get
{
return _listView;
}
set
{
_listView = value;
OnPropertyChanged("BigImageView");
}
}
I want to return the image to the content control when I move the listbox. I have been racking my brain and trying everyhitn but it does not work. any help would be appreciated.

There is no need to bind the selecteditem, the collectionview should take care of that.
Try this:
<ListBox ItemsSource="{Binding BigImageView}" IsSynchronizedWithCurrentItem="True" />
<ContentControl Name="Detail" Content="{Binding BigImageView, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
<ContentControl.ContentTemplate>

1
Create a viewmodel with a list and a selected item:
public class BigImageViewModel : INotifyPropertyChanged
{
private string bigImage;
//string for path?
public ObservableCollection<string> BigImageView {get; set; } //Of course, make sure it has a value
public string SelectedBigImage
{
get { return bigImage; }
set { bigImage = values; NotifyPropertyChanged("SelectedBigImage"); }
}
}
Set this object on the DataContext of your control in the constructor:
DataContext = new BigImage(); //Make sure you initialize your list
Set the ListBox ItemsSource to your BigImage list, bind your SelectedItem to BigImageView
and use that in your content control:
<ListBox ItemsSource="{Binding BigImageView}" SelectedItem={Binding SelectedBigImage} />
ContentControl:
<ContentControl Name="Detail" Content="{Binding SelectedBigImage, Mode=OneWay}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>
2
Or screw that view model:
Set the list directly in the constructor (after the InitializeComponent() ):
myListBox.ItemsSource = ObservableCollection<string>(); //Make sure you initialize your list with whatever your object is..
Give the list a name:
And bind with an ElementName binding to your selected item:
<ContentControl Name="Detail" Content="{Binding ElementName=myListBox, Path=SelectedItem}" VerticalAlignment="Top">
<ContentControl.ContentTemplate>
<DataTemplate>
<Image Source="{Binding}"/> <!-- Nice template for showing your string BigImage -->
</DataTemplate>
<ContentControl.ContentTemplate>
</ContentControl>

Related

MVVM WPF Combobox: creating a template for comboboxitem

I have a WPF combobox which is populated from code-behind.
Code-behind (xaml.cs):
namespace WpfApplication1
{
private ObservableCollection<TransportType> transportTypes = new ObservableCollection<TransportType>();
transportTypes.Add(new TransportType() {Icon = Properties.Resources.Air, ValueMember = "A100", DisplayMember = "By Air" });
transportTypes.Add(new TransportType() {Icon = Properties.Resources.Maritime, ValueMember = "M200", DisplayMember = "Maritime" });
this.ComboBoxTransportTypes.ItemsSource = transportTypes;
}
TransportType class:
namespace WpfApplication1
{
public class TransportType
{
public Image Icon
{
get;
set;
}
public string DisplayMember
{
get;
set;
}
public string ValueMember
{
get;
set;
}
}
}
View:
<ComboBox x:Name="ComboBoxTransportTypes"
Grid.Column="1"
ItemsSource="{Binding}"
DisplayMemberPath="DisplayMember"
SelectedValuePath="ValueMember"
SelectionChanged="ComboBoxTransportTypes_SelectionChanged">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
Now I am trying to apply a ComboBox ItemTemplate and bound to the "transportTypes" collection. I would like each combobox item to be as below:
<ComboBoxItem>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding bind-icon-here}" />
<TextBlock Foreground="AliceBlue"
VerticalAlignment="Center"
Text="{Binding bind-DisplayMember-here}"/>
</StackPanel>
</ComboBoxItem>
So how can I create the above combobox item template bound to my collection in order to each item to be presented with an icon followed by a string?
I have tried below but it does not work. I also do not know how to bind each item in the collection to the image and textblock within stackpanel, I have done as below but only string is displayed and not icon.
<ComboBox x:Name="ComboBoxTransportTypes"
Grid.Column="1"
ItemsSource="{Binding}"
DisplayMemberPath="DisplayMember" <-- removed from here as I cannot define DisplayMemberPath and item template at the same time.
SelectedValuePath="ValueMember"
SelectionChanged="ComboBoxTransportTypes_SelectionChanged">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate DataType="l:TransportType">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Icon}" />
<TextBlock Foreground="AliceBlue"
VerticalAlignment="Center"
Text="{Binding DisplayMember}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Also, in MVVM is it better to populate combobox from code-behind as I have done here or from view model constructor?
The problem is that you're setting the ItemSource in both the XAML and in the code behind. If you remove ItemSource="{Binding}" from the XAML then it should work.
If you are using MVVM, the collection should be populated in the view model, not in the code behind. There should be very little code in your code behind - only things related to the view should go there (such as displaying a child window).
The problem is that Image.Source takes an ImageSource not an Image. Change ...
<Image Source="{Binding Icon}" />
to...
<Frame Content="{Binding Icon}"/>
and things will start working.
you must use bottom syntax for custom template ComboBox
<TextBlock Text="{Binding Path=DisplayMember}"/>
or
<Image Source="{Binding Path=Icon}" />
in wpf list tags like combobox,listbox,... for custom tamplate like DataTemplate you must use Path

How to bind each button's image source from ListView to individual item from observable collection?

I have a Listview of Buttons like this:
<ListView ItemsSource="{Binding TestList}">
<ListViewItem >
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent">
<Button.Template>
<ControlTemplate>
<Grid>
<Image Source="?????????????????????????????????????????????"/>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
</ListView.ItemTemplate>
And the code behind like this:
public class CodeBehind
{
private ObservableCollection<string> testList;
public ObservableCollection<string> TestList
{
get { return testList; }
set
{
testList = value;
}
}
public CodeBehind()
{
dummyModelList = new ObservableCollection<string>() { "/Assets/Image1", "/Assets/Image2", "/Assets/Image3"};
}
}
How to bind each button's image source to individual item from observable collection? I want to do this only in XAML.
What you are looking for is ItemTemplate (Gets or sets the DataTemplate used to display each item.) instead of buttons' template itself.
You use the ItemTemplate to specify the visualization of the data objects. If your ItemsControl is bound to a collection object and you do not provide specific display instructions using a DataTemplate, the resulting UI of each item is a string representation of each object in the underlying collection.
So when you set the ItemTemplate to an instance of DataTemplate, the DataTemplate will be used to render the items. The DataContext of the DataTemplate will be implicitly set to individual items in the bound collection, so you can bind the Image.Source to the DataContext itself using {Binding .} or {Binding}
<ListView ItemsSource="{Binding TestList}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Name="test" Grid.Row="0" Grid.Column="10" Grid.ColumnSpan="4" Grid.RowSpan="4" VerticalAlignment="Center" Background="Transparent">
<Image Source="{Binding .}" />
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You should create a DataTemplate to show any controls in your ItemTemplate of ListView:
<ListView Name="listView">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto*"/>
<RowDefinition Height="Auto*"/>
</Grid.RowDefinitions>
<Button>
<Image Source="{Binding AddressImage}" Width="20" Grid.Row="0" />
</Button>
<TextBlock Grid.Row="1" TextAlignment="Left" FontWeight="Light"
VerticalAlignment="Center" Text="{Binding UserName}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Model:
public class Person
{
public int IdUser {get; set;}
public string UserName {get; set;}
public string AddressImage {get; set;}
}
code-behind:
public MainWindow()
{
PopulateCollection();
}
private void PopulateCollection()
{
ObservableCollection<Person> personColl = new ObservableCollection<Person>();
for (int i = 0; i <= 10; i++)
{
//here you can set any image what you want
personColl.Add(new Person() { IdUser=i, UserName="I am " + i.ToString(),
AddressImage="Assets/1.png"});
//Assets is a folder with images
}
listView.ItemsSource=personColl;
}

How can i add different images to combobox itemssource array

I would like to add different images next to each item in a combobox itemssource. Here's what i have at the moment.
<ComboBox x:Name="cmb" HorizontalAlignment="Left" Width="135" Height="22"
SelectedItem="{Binding myViewMode}" Margin="5,0,0,0">
<ComboBox.ItemsSource>
<x:Array Type="sys:String" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>Oranges</sys:String>
<sys:String>Mangoes</sys:String>
</x:Array>
</ComboBox.ItemsSource>
</ComboBox>
How should add the two diffent images using an itemtemplate. Thanks
Edit One
This is what i have tried with itemtemplate
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding OrangesImage}" Height="100"/>
<Image Source="{Binding MangoesImage}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
It's here that am really stuck.
At the moment your item template contains only two images, so you will show two images and no text for each item!
I would suggest you change your ItemsSource to code behind so you can have text and image properties.
First make a simple Fruit class:
public class Fruit
{
public string FruitName { get; set; }
public string FruitImage { get; set; }
}
Then create a list of these fruits and set the ItemsSource of your combo box to this list:
var fruits = new List<Fruit>();
fruits.Add(new Fruit() { FruitName = "Mangos", FruitImage = #"C:\mangoimage.jpg" });
fruits.Add(new Fruit() { FruitName = "Oranges", FruitImage = #"C:\mangoimage.jpg" });
cmb.ItemsSource = fruits;
Then simplify your XAML thus:
<ComboBox x:Name="cmb" HorizontalAlignment="Left" Width="135" Height="22" SelectedItem="{Binding myViewMode}" Margin="5,0,0,0">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FruitName}"/>
<Image Source="{Binding FruitImage}" Height="100"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In your ItemSource your image should consists as Uri path with BitMapImage Class then only Images are accepted in ItemTemplate in ComboBox
Xaml Code
<ComboBox x:Name="cmb" HorizontalAlignment="Left" Width="135" Height="22"
SelectedItem="{Binding myViewMode}" Margin="5,0,0,0">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="25" Height="25" Source="{Binding FruitName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Your Model Class
public class Fruit
{
public string FruitName { get; set; }
}
Your ItemSource should Consists as:
fruitCollection.Add(new Fruit() {FruitName= new BitmapImage(new Uri("C:\mangoimage.jpg", UriKind.Relative))});

How to make custom control with ListBox?

I want to make horizontal ListBox with customized item template, so I make a basic template of it.
However, I couldn't find an example of binding 'things' to that WPF XAML, especially with ListBox filled with customized items.
I simply want to dynamically add/remove items in the ListBox with Image, Label, ComboBox with previously filled with number 1 to 10.
the ADD/REMOVE button will be placed outside WPF control, it means that the buttons will be on the Main Window Form.
Also, there are TextBox and picture selector in the Main Window Form so that I can change the text and image.
Below is code behind XAML :
Public Class listSequence
Public Sub New()
InitializeComponent()
listbox.Items.Add("hi")
listbox.Items.Add("there")
End Sub
End Class
Below is XAML :
<ListBox Name="listbox" VerticalContentAlignment="Stretch" ScrollViewer.VerticalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.CanContentScroll="True" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="10" Margin="5" BorderThickness="1" BorderBrush="Aqua" CornerRadius="0" Width="120" VerticalAlignment="Stretch">
<StackPanel>
<Image />
<Label Content="{Binding}" />
<TextBlock Text="hi" />
<ComboBox x:Name="cboRepeat" ItemsSource="{Binding}" DisplayMemberPath="RepeatCounter" IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Let's say we have a basic class named Item:
public class Item : INotifyPropertyChanged
{
public string Text { get; set; } // Implement INotifyPropertyChanged
public string ImagePath { get; set; } // properly on these properties
}
And a collection of these in a view model:
public ObservableCollection<Item> Items { get; set; }
Now to display these items in the UI, we use a ListBox and set the ItemsSource property:
<ListBox ItemsSource="{Binding Items}">
</ListBox>
When it comes to defining the ListBox.ItemTemplate, you need to understand that this DataTemplate will be applied to each item and that it has access to all of the properties defined in the Item class:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image ImageSource="{Binding ImagePath}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Therefore, you can access the properties in the collection class as shown above. You can find out the full story by looking at the ItemsControl.ItemTemplate Property page on MSDN.

How can I set my data binding in a WPF ItemsControl child to use a property on the parent data context?

I have a WPF ListView with a collection of RadioButtons. I want to set the GroupName of the child controls to be bound to a property on the parent data context. At the moment I am doing this by duplicating the property in each of the children's data context but that can't be right.
My XAML:
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
GroupName is a property on the parent View Model. I currently pass this onto the child View Model (where it is also a property) in the child constructor:
var item = new FilterOptionsRadioListItemViewModel(option, this.GroupName);
What is the correct way of doing this?
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.GroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You certainly can do that, but if you don't want to have to copy the GroupName to every child Option instance, you could instead do something like the following
// I'm imagining your viewmodels look something like this
public class ParentViewModel
{
private ObservableCollection<Option> m_OptionItems;
public ObservableCollection<Option> OptionItems
{
get { return m_OptionItems; }
set { m_OptionItems = value; }
}
private string m_ParentGroupName;
public string ParentGroupName
{
get { return m_ParentGroupName; }
set
{
m_ParentGroupName = value;
}
}
}
public class Option
{
private string m_Value;
public string Value
{
get { return m_Value; }
set
{
m_Value = value;
}
}
}
Then you can use a relative binding so you can look up the control tree to find the right datacontext, so you don't have to copy the ParentGroupName to each child Option instance.
If you name your window, you can traverse the root DataContext.
<Window x:Class="MyNamespace.MainWindow"
Name="Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="725">
<ListView ItemsSource="{Binding OptionItems}" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type Logging:FilterOptionsRadioListViewModel}">
<RadioButton GroupName="{Binding ElementName=Window, Path=DataContext.ParentGroupName}" Content="{Binding Option.Value}" Tag="{Binding Option.Key}" IsChecked="{Binding IsChecked}" Command="Logging:FilterOptionsRadioListViewModel.CheckedChangedCommand" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Resources