MVVM WPF Combobox: creating a template for comboboxitem - wpf

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

Related

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.

Failing to Add UserControl To Existing Grid on SelectedItem in ListBox

I am new into WPF. I am currently developing an application where my solution in MSVC# 2010 has 2 projects. One project(i.e.MSPBoardControl) has a mainwindow.xaml and I have added another wpf window ConnectView.xaml. By using the grid in mainwindow.xaml I am able to add the Connectview.xaml view to my application in mainwindow.xaml.cs. My 2nd project is a classlibrary which has a usercontrol(i.e. Voltage).
Mainwindow.xaml: This grid is the one to which I add my Connectview.xaml UI component
<Grid Grid.Row="0" Name="MainGrid" HorizontalAlignment="Stretch" Style="{DynamicResource styleBackground}" >
</Grid>
I have a listbox in my mainwindow.xaml towards the left side which has list of items(i.e. Voltage, Clock etc). The right side of .xaml has my grid which is shown above(contains the connectview on startup). What I basically need is when I click the item from the listbox, I want to hide the view which is shown on startup(connectview) and make the selecteditem UI component visible.
As mentioned in the beginning I want to make a usercontrol of my classlibrary(VoltageView) visible on clicing "Voltage" item from Listbox.
ListBox in my mainwindow.xaml:
<ListBox Name="ButtonPanel" Style="{DynamicResource styleBanner}" ItemsSource="{Binding BoardOperations, Mode=TwoWay}" SelectedItem="{Binding SelectedList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="BoardTabChanger" Margin="53,27,0,0" Text="{Binding ListName}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel Class of MSPBoardControl is here:
public class BoardControlViewModel : INotifyPropertyChanged
{
public ObservableCollection<BoardControlModel> m_BoardOperation;
public List<ConnectModel> m_BoardNames;
public BoardControlViewModel()
{
//Adding items to ListBox
}
public List<ConnectModel> BoardNames
{
get; set;
}
private ConnectModel m_SelectedBoardItem;
public ConnectModel SelectedBoard
{
get; set;
}
public ObservableCollection<BoardControlModel> BoardOperations
{
get; set;
}
//GIVES ME THE SELECTED ITEM FROM LISTBOX
private BoardControlModel m_SelectedListItem;
public BoardControlModel SelectedList
{
get
{
return m_SelectedListItem;
}
set
{
m_SelectedListItem = value;
onListSelected(value);
NotifyPropertyChanged("SelectedList");
}
}
private void onListSelected(BoardControlModel SelectedItem)
{
if (SelectedItem.ListName == "Voltage")
{
//HOW CAN I ADD THE VOLTAGEVIEW HERE???
}
}
The above method OnListSelected() retrieves the selecteditem and when I check the condition dat item = voltage, I want to add the voltageview component to my maingrid and hide the connectview.
VoltageView.xaml:
<Grid Name="VoltageControl" Style="{DynamicResource styleBackground}" DataContext="{StaticResource VoltageViewModel}" >
<Label Content="Label" FontSize="15" Foreground="Black" Height="28" HorizontalAlignment="Left" Margin="10,31,0,0" Name="label9" VerticalAlignment="Top" Width="117" />
<Label Content="Set Voltage" FontSize="15" Foreground="Black" Height="28" HorizontalAlignment="Left" Margin="150,31,0,0" Name="label10" VerticalAlignment="Top" Width="117" />
<Label Content="Current Value" FontSize="16" Foreground="Black" Height="28" HorizontalAlignment="Left" Margin="300,31,0,0" Name="label11" VerticalAlignment="Top" Width="117" />
<Label Content="Enable/Disable" FontSize="15" Foreground="Black" Height="28" HorizontalAlignment="Left" Margin="460,31,0,0" Name="label12" VerticalAlignment="Top" Width="127" />
<Button Content="Refresh All" FontSize="13" Height="25" HorizontalAlignment="Left" Margin="230,520,0,0" Name="RefreshBtn" VerticalAlignment="Top" Width="105" />
</Grid>
Please help!!!
I'm going to keep this general(ish). Your best bet is probably to add a property on BoardControlViewModel of type Grid (or whatever UIElement that is common to voltage and connect). As the selection changes, change the UI property to the view you want and hit NotifyPropertyChanged for that property. You can call NotifyPropertyChanged right from onListSelected if you like. Bind the content of whatever grid you're using to this property, prefferably right in the xaml. When the property changes, WPF will notify the grid that its bound content has changed and it will query your new property to see what it should be.

ObservableCollection Images in Listbox to Content Control master detail 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>

How to inherit from ListViewItem in WPF?

I need to create table that all ListViewItem of this table will be build hold
1. Image
2. Text
3. Button
How can i do it ?
In WPF you don't have to inherit ListViewItem to add your properties, you can create your class with your properties and bind it to ListView's or ListBox's ItemSource and create ItemTemplate to show item as you like.
public class MyItem
{
public string MyImagePath{get; set;}
public string MyText{get; set;}
}
<ListView x:Name="GroupsView" ItemsSource="{Binding MyItemsList}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=MyImagePath}" />
<TextBlock Margin="2,0,2,0" Text="{Binding MyText}"/>
<Button .../>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here are some resources about that
1.http://huydinhpham.blogspot.com/2008/11/using-listvew-to-display-complex-data.html
2.http://www.codewrecks.com/blog/index.php/2008/11/08/wpf-and-wrapping-text-inside-elements-of-a-listview/

How do I insert ToolBar separators when binding ItemSource

I am binding a ToolBar to a collection of command view model objects. The objects in the collection have a property IsSeparator that when true I would like represented with a <Separator/> in the ToolBar.
My basic markup looks like this:
<ToolBar Grid.Row="1" ItemsSource="{Binding Path=ToolBarCommands}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button ToolTip="{Binding Path=ToolTip}" Command="{Binding Path=Command}">
<Button.Content>
<Image Width="16" Height="16" Source="{Binding Path=IconStream}"/>
</Button.Content>
</Button>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
I've played around with ItemContainerStyle much like this example for MenuItems but to no avail.
Any help is appreciated.
I followed Josh's suggestion about using a DataTemplateSelector and I'm just going to post code to help others.
public class ToolBarItemTemplateSelector : DataTemplateSelector
{
public DataTemplate ButtonTemplate { get; set; }
public DataTemplate SeparatorTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var toolBarItem = (ToolBarItemViewModel) item;
Debug.Assert(toolBarItem != null);
if (!toolBarItem.IsSeparator)
{
return ButtonTemplate;
}
return SeparatorTemplate;
}
}
<DataTemplate x:Key="buttonTemplate" DataType="{x:Type infrastructure:ToolBarItemViewModel}">
<Button Command="{Binding Command}" ToolTip="{Binding ToolTip}" Style="{DynamicResource ResourceKey={x:Static ToolBar.ButtonStyleKey}}">
<Image Source="{Binding ImageSource}" Width="16" Height="16" />
</Button>
</DataTemplate>
<DataTemplate x:Key="separatorTemplate">
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
</DataTemplate>
<local:ToolBarItemTemplateSelector ButtonTemplate="{StaticResource buttonTemplate}" SeparatorTemplate="{StaticResource separatorTemplate}" x:Key="toolBarItemTemplateSelector" />
<ToolBar AutomationProperties.AutomationId="toolBar" ItemsSource="{Binding ToolBarItems}" x:Name="toolBar" Band="1" BandIndex="1" ItemTemplateSelector="{StaticResource toolBarItemTemplateSelector}"/>
Rather than inserting Separator objects, you could just grab Separator's ControlTemplate (in Blend, right click a separator -> Edit Template -> Edit a Copy) and incorporate it directly into your button template. You can use a DataTrigger to control its visibility, perhaps binding to a "BeginGroup" property on your object.
If you want to have a dedicated Separator object in your collection you could use a DataTemplateSelector.

Resources