Switch between different views, following MVVM pattern - wpf

I'm looking for the correct way to switch between different views. My scenario is similar to the Widnows Explorer on Windows 8, where you can switch between 'extra large icons', 'small icons', 'details', etc. In Windows 8 The users do that from the 'View' ribbon of explorer (you can also change view in XP and 7).
In my case, I've a list of friends, and I want to let the user switch between 'small', 'large', and 'details' view.
Assume my view model has a list of friends:
public class FriendVM {
public name { get; set; }
public smallImage { get; set; }
public largeImage { get; set; }
};
public class MainVM : INotifyPropertyChanged {
public ObservableCollection<FriendVM> friends { get; set; }
private string m_viewMode
public string viewMode {
get { return m_viewMode; }
set { m_viewMode=value; this.PropertyChanged( new PropertyChangedEventArags("viewMode") ); }
}
}
In my view, I've a ribbon (on which user can change viewMode), a header (showing details about the user), and a list of friends. Depends on the view, I want to show:
when viewMode = "details" I've a ListView, with a GridView.
when viewMode = "small" I've a ListView, with ItemsPanel is a WrapPanel, and I bind the image to the smallImage
when viewMode = "large" I've a ListView with WrapPanel, using the largeImage property.
Here is how my XML looks like (simplified):
<Window x:Class="friends.MainWindow" ... xmlns:f="clr-namespace:friends" ...>
<Window.DataContext>
<f:MainVM />
<Window.DataContext>
<Window.Resources>
<ControlTemplate x:Key="details">
<ListView ItemsSource="{Binding Path=friends}">
<ListView.View>
<GridView>
...
</GridView>
</ListView.View>
</ControlTemplate>
<ControlTemplate x:Key="small">
<ListView ItemsSource="{Binding Path=friends}">
<ListView.ItemsPanel><WrapPanel Orientation="Horizontal" />
</ListView>
<ListView.ItemTemplate><DataTemplate>
<Image Source={Binding smallPicture} Width="32" Height="32" />
</DataTemplate></ListView.ItemTemplate>
</ControlTemplate>
<ControlTemplate x:Key="large">
<ListView ItemsSource="{Binding Path=friends}">
<ListView.ItemsPanel><WrapPanel Orientation="Horizontal" />
</ListView>
<ListView.ItemTemplate><DataTemplate>
<StackPanel>
<Image Source="{Binding largePicture}" Width="200" Height="200" />
<TextBlock Text="{Binding name}" />
</StackPanel>
</DataTemplate></ListView.ItemTemplate>
</ControlTemplate>
</Window.Resource>
<DockPanel LastChildFill="True">
<Ribbon ...>
...
</Ribbon>
<StackPanel>
... some header stuff
</StackPanel>
<ContentControl x:Name="friendList" Content="{Binding friends}" ?????? />
</DockPanel>
</Window>
So, my question is what do I do in the ????? area. Right now, I've Template="{StaticResource small}" and it is working. Moreover, using code behind I can change the Template to any of the other resourced template (using FindResource). However, I'm not happy with this solution, as I feel it doesn't go well with MVVM pattern. If it was an "Item" (a listbox item, a tab item, etc.), then I could use a data template, and then a date template selector. But since this is a ContentControl, and ControlTemplateSelector seems to be completely broken from the design, I'm not sure what should I do.
Or, if I could put the list of friends "as is" in the tree, then maybe using data template (having TargetType=f:FriendList) I could make it work, but I don't want to instantiate another friend list. There is already one instance instantiated within the DataContext element.

Related

How to choose View dynamically based on current DataContext view model

I have a Page which will receive a different DataContext (View Model), dynamically.
I can't figure out how to use DataTemplate in a switch/case fashion, to render the appropriate view based on the current context.
I would imagine that I will have multiple DataTemplates like this:
<DataTemplate DataType="{x:Type LocalViewModels:ABC}">
<LocalViews:ABC/>
</DataTemplate>
but can't figure out in what container to put them. Only one of them will be rendered at a time, so ListBox makes no sense to me.
Given the following XAML of a Window
<Window.Resources>
<DataTemplate DataType="{x:Type local:ABC}">
<Border BorderThickness="2" BorderBrush="Red">
<TextBlock Text="{Binding Text}"/>
</Border>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ContentControl Content="{Binding}"/>
</StackPanel>
you can simply assign an instance of ABC to the Window's DataContext to create the templated view.
class ABC
{
public string Text { get; set; }
}
...
public MainWindow()
{
InitializeComponent();
DataContext = new ABC { Text = "Hello, World." };
}
All details are here: Data Templating Overview.

WPF databinding from DataTemplate - many-to-many relationship

I have a WPF control for laying out items and set ItemsSource for it as
ItemsSource="{Binding item_portfolio}" DataContext="{Binding SelectedItem}"
In the layout control's Resources I set the template for its items:
<Style>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding portfolio.PortfolioName}" />
<ListView ItemsSource="{Binding ?}">
</ListView>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The data for the bindings is a many-to-many relationship (one item can have many portfolios and one portfolio can have many items) and specified in 3 separate tables in the database (I use Entity Framework to access it). Schema and example data below:
item item_portfolio portfolio
ID (PK) Name itemID (FK) portfolioID (FK) ID PortfolioName
1 Item 1 1 1 1 Portfolio 1
2 Item 2 1 2 2 Portfolio 2
1 3 3 Portfolio 3
2 2
2 3
TextBlock binding under DataTemplate works correctly.
I don't however know how to bind the ListView ItemsSource so that it would show all the item objects belonging to that portfolio.
Edit:
I want to list portfolios in the layout control. Then under portfolio, I want to show what items it contains. The below image shows the UI when the SelectedItem is Item 1.
(First I show what portfolios this item has. This gives 3 portfolios. This works ok. Then on UI I click the portfolio 3, and it should show all items (Item 1 and Item 2) belonging to that portfolio. That doesn't work yet.)
We don't model our data models in code in the same way as they are modelled in the database. We don't make 'joining classes' to model 'joining tables'. Instead, we create hierarchical data models. In your case, you should have an Item class that has a Portfolio property of type Portfolio. There should be no ItemPortfolio class, as it makes no sense. Then, your Binding should be this`:
<ItemsControl ItemsSource="{Binding Items}" ... />
Then your DataTemplate (which will automatically have its DataContext set to an item from the Item class collection) should look more like this:
<DataTemplate>
<StackPanel Orientation="Vertical" Margin="5">
<TextBlock Text="{Binding PortfolioName}" />
<ListView ItemsSource="{Binding Portfolio}">
</ListView>
</StackPanel>
</DataTemplate>
Clearly, to make this work, you'd need to have those properties defined in your Item class.
UPDATE >>>
In that case, make a Portfolio class with an Items property of type ObservableCollection<Item> and set the ItemsSource like this:
<ItemsControl ItemsSource="{Binding Item.Portfolios}" ... />
To clarify, your Item class should also have a collection property of type ObservableCollection<Portfolio>... this will lead to some data object duplication, but in WPF it is more important to provide your views with the data that they require.
I used Expander control here as you want PortfolioName as header and on click of
PortfolioName you want to display corresponding PortfolioItemList.
xaml code
<ItemsControl MaxHeight="300" ItemsSource="{Binding PortfolioInfo}" ScrollViewer.VerticalScrollBarVisibility="Auto" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander>
<Expander.Header>
<TextBlock Background="YellowGreen" Text="{Binding name}"></TextBlock>
</Expander.Header>
<Expander.Content>
<ListBox ItemsSource="{Binding ItemList}"></ListBox>
</Expander.Content>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C# code
public partial class MainWindow : Window
{
public ObservableCollection<Portfolio> PortfolioInfo { get; set; }
public MainWindow()
{
InitializeComponent();
PortfolioInfo = new ObservableCollection<Portfolio>()
{
new Portfolio(){name="Portfolio1", ItemList= new List<string>(){"Item1","Item2","Item3"}},
new Portfolio(){name="Portfolio2", ItemList= new List<string>(){"Item1","Item2","Item3"}},
new Portfolio(){name="Portfolio3", ItemList= new List<string>(){"Item1","Item2","Item3"}}
};
this.DataContext = this;
}
}
public class Portfolio
{
public string name { get; set; }
public List<string> ItemList { get; set; }
}
Note : Edit Expander style/template to achieve desired UI
Rseult

Load controls on runtime based on selection

I'm new to XAML and I have a case where I need to change controls based on a selection on a combobox with templates.
For example, let's say that a user selects a template that requires a day of week and a time range that something will be available. I would like that, on the moment of the selection, the control with the information needed get build on the screen and that the bindings get to work as well.
Can someone give me a hint or indicate an article with an elegant way to do so?
Thanks in advance.
The solution you are looking for is a ContentControl and DataTemplates. You use the selected item of the ComboBox to change ContentTemplate of the Content Control.
You question mentions binding so I will assume you understand the MVVM pattern.
As an example, lets use MyModel1 as the Model
public class MyModel1
{
private Collection<string> values;
public Collection<string> Values { get { return values ?? (values = new Collection<string> { "One", "Two" }); } }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
And MyViewModel as the ViewModel
public class MyViewModel
{
public MyViewModel()
{
Model = new MyModel1();
}
public MyModel1 Model { get; set; }
}
And the code behind does nothing but instantiate the ViewModel.
public partial class MainWindow : Window
{
public MainWindow()
{
ViewModel = new MyViewModel();
InitializeComponent();
}
public MyViewModel ViewModel { get; set; }
}
All three are very simple classes. The fun comes in the Xaml which is
<Window x:Class="StackOverflow._20893945.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:this="clr-namespace:StackOverflow._20893945"
DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="MyModel1Template1" DataType="{x:Type this:MyModel1}">
<StackPanel>
<TextBlock Text="Template 1"></TextBlock>
<ComboBox ItemsSource="{Binding Path=Values}" SelectedItem="{Binding Path=Field1}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="MyModel1Template2" DataType="{x:Type this:MyModel1}">
<StackPanel>
<TextBlock Text="Template 2"></TextBlock>
<TextBox Text="{Binding Path=Field2}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="2">
<ComboBox x:Name="TypeSelector">
<system:String>Template 1</system:String>
<system:String>Template 2</system:String>
</ComboBox>
</StackPanel>
<ContentControl Content="{Binding Path=Model}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=TypeSelector, Path=SelectedItem}" Value="Template 2">
<Setter Property="ContentTemplate" Value="{StaticResource MyModel1Template2}" />
</DataTrigger>
</Style.Triggers>
<Setter Property="ContentTemplate" Value="{StaticResource MyModel1Template1}" />
</Style>
</ContentControl.Style>
</ContentControl>
</DockPanel>
</Window>
The notable points of the view are
The DataContext is initialised on the Window element, allowing for auto-complete on our binding expressions
The definition of 2 template to display 2 different views of the data.
The ComboBox is populated with a list of strings and has a default selection of the first element.
The ContentControl has its content bound to the Model exposed via the ViewModel
The default DataTemplate is the first template with a ComboBox.
The Trigger in the ContentControl's style will change the ContentTemplate if the SelectedItem of the ComboBox is changed to 'Template 2'
Implied facts are
If the SelectedItem changes back to 'Template 1', the style will revert the the ContentTemplate back to the default, ie MyModel1Template1
If there were a need for 3 separate displays, create another DataTemplate, add a string to the ComboBox and add another DataTrigger.
NOTE: This is the complete source to my example. Create a new C#/WPF project with the same classes and past the code in. It should work.
I hope this helps.

Conditional DataTemplates when binding to a collection

Sample Application:
The sample application that the supplied code belongs to displays a list of Vehicle objects via Binding. The Vehicle class is a top level class that subclasses can derive from e.g. Car and Bike. The sample application at the moment displays the owner's name of the Vehicle.
Sample Model code:
public class Vehicle
{
private string _ownerName;
public string ownerName
{
get { return _ownerName; }
set { _ownerName = value; }
}
}
public class Car : Vehicle
{
public int doors;
}
public class Bike : Vehicle
{
// <insert variables unique to a bike, ( I could not think of any...)>
}
UserControl XAML Code:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="itemTemplate">
<WrapPanel>
<TextBlock Text="{Binding Path=ownerName}"/>
</WrapPanel>
</DataTemplate>
</Grid.Resources>
<ListBox x:Name="list" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" ItemsSource="{Binding}" ItemTemplate="{StaticResource itemTemplate}" />
</Grid>
UserControl code behind:
public List<Vehicle> vehicleList = new List<Vehicle>();
public CustomControl()
{
InitializeComponent();
createSomeVehicles();
list.DataContext = vehicleList;
}
public void createSomeVehicles()
{
Car newcar = new Car();
newcar.doors = 5;
newcar.ownerName = "mike";
Bike newbike = new Bike();
newbike.ownerName = "dave";
vehicleList.Add(newcar);
vehicleList.Add(newbike);
}
What I want to be able to do:
I would like to be able to display a button in the list object dependant upon the Type of the Vehicle object. E.g. I would like to display a Open Boot button within the list item for Car's; Type Bike does not have a boot and so no button would display within the list item.
Idea's on how to accomplish this:
I have looked into the custom binding of different DataTemplates based upon what type of object it is. E.g. from the code behind I could call:
object.Template = (ControlTemplate)control.Resources["templateForCar"];
The problem here is that I am using a Binding on the whole list and so there is no way to manually bind a DataTemplate to each of the list items, the list binding controls the DataTemplate of it's items.
You can create a DataTemplate for each Bike and Car (and for any CLR type). By specifying the DataTemplate's DataType property, the template will automatically be applied whenever WPF sees that type.
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:Car}">
<WrapPanel>
<TextBlock Text="{Binding Path=ownerName}"/>
<Button Content="Open Boot" ... />
</WrapPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Bike}">
<WrapPanel>
<TextBlock Text="{Binding Path=ownerName}"/>
</WrapPanel>
</DataTemplate>
</Grid.Resources>
<ListBox x:Name="list" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5" ItemsSource="{Binding}" />
</Grid>

Suggestions for synchronization of lists in Silverlight

We are creating a business application (Silverlight 4) at our company where we, in several of our views, use the functionality to synchronize two lists of some kind.
More precise we are, at the moment, using two list boxes (one source and one target) and two buttons (one add and one remove). Both the source list and the target lists are bound to collections with the same data type.
A user may select one or more items in the source list and press the add button to have the items moved to the target list (i.e. the items are removed from the source and added to the target).
Likewise the user could select one or more items in the target list and press remove to have the items moved from the target back to the source list.
There is also the possibility to add a validation rule that says that the user must add at least one item to the target list.
Pretty simple...
Until now we are using our own created user control which incapsulates these 4 controls (2 list boxes and 2 buttons) and the logic for keeping the lists in sync. Dependency properties are used for binding the source and target collections.
Now to the problem. Our customer now wants our user control to be more flexible.
They want to be able to have an arbitray number of columns in both the source and target list (i.e. the source list may have different columns and a different number of columns than the target list). The customer also wants to be able to sort on any column.
My first thought was to replace the list box to the data grid instead. But then I realized I don't know how to, in an easy way, let the consumer (the developer) define his or her columns and bindings. This may be to my limited knowledge of SL. Maybe a custom user control isn't the way to go?
I would appreciate any kind of help. Right now we are implementing the same logic over and over again in our views and it doesn't feel right. There has to be some way we can make this a reusable component that is easy to use.
Thanks!
"Grid version" of the answer:
(see list version below)
As the question has changed (clarified) I am adding a new answer. The first one is still useful for those that only want lists so I will leave it there.
To do a similar thing with grids you don't expose the templates as datagrid columns are not templated (they are lists of controls, which can individually be templated).
Instead you expose the left grid and right grid column collections as properties and simply set the LeftColumns and RightColumns properties of the control in your parent page. The only trick is that the datagrid-column collections have no setter so you need to update the grid collections items from the new property collections:
The code-behind is now:
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class GridSelectionControl : UserControl
{
public GridSelectionControl()
{
InitializeComponent();
}
public ObservableCollection<DataGridColumn> LeftColumns
{
get
{
return leftDataGrid.Columns;
}
set
{
leftDataGrid.Columns.Clear();
foreach (var col in value)
{
leftDataGrid.Columns.Add(col);
}
}
}
public ObservableCollection<DataGridColumn> RightColumns
{
get
{
return rightDataGrid.Columns;
}
set
{
rightDataGrid.Columns.Clear();
foreach (var col in value)
{
rightDataGrid.Columns.Add(col);
}
}
}
}
}
The Xaml of the control is now:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="SilverlightApplication1.GridSelectionControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<sdk:DataGrid x:Name="leftDataGrid" Margin="10" AutoGenerateColumns="False"/>
<sdk:DataGrid x:Name="rightDataGrid" Margin="10" Grid.Column="2" AutoGenerateColumns="False"/>
<StackPanel Grid.Column="1" Orientation="Vertical">
<Button Content="Add >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
</StackPanel>
</Grid>
</UserControl>
The Xaml of the test page (which defines the columns) is now:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication1" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
mc:Ignorable="d"
x:Class="SilverlightApplication1.TestGridSelectionControl"
d:DesignWidth="640" d:DesignHeight="480">
<Grid x:Name="LayoutRoot">
<local:GridSelectionControl x:Name="SelectionControl">
<local:GridSelectionControl.LeftColumns>
<sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
<sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
</local:GridSelectionControl.LeftColumns>
<local:GridSelectionControl.RightColumns>
<sdk:DataGridCheckBoxColumn Header="Machine?" Binding="{Binding IsMachine}"/>
<sdk:DataGridTextColumn Header="First Name" Binding="{Binding FirstName}"/>
<sdk:DataGridTextColumn Header="Last Name" Binding="{Binding LastName}"/>
</local:GridSelectionControl.RightColumns>
</local:GridSelectionControl>
</Grid>
</UserControl>
And the test code-behind is now:
using System.Collections.ObjectModel;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class TestGridSelectionControl : UserControl
{
public TestGridSelectionControl()
{
// Required to initialize variables
InitializeComponent();
SelectionControl.leftDataGrid.ItemsSource = Person.People;
SelectionControl.rightDataGrid.ItemsSource = Person.Machines;
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsMachine { get; set; }
public Person(string firstname, string lastname, bool robot)
{
this.FirstName = firstname;
this.LastName = lastname;
this.IsMachine = robot;
}
public static ObservableCollection<Person> People = new ObservableCollection<Person>()
{
new Person("Tom", "Jones", false),
new Person("Elis", "Presley", false),
new Person("Joe", "Blogs", false)
};
public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
{
new Person("Marvin", "Android", true),
new Person("Hal", "9000", true),
new Person("B", "9", true)
};
}
}
}
Since you are creating a control to be consumed by other developers then its usually best to be using a Template Control rather than a UserControl. In which case the developers can specify a custom template for the control. However that's not as helpful as you could be, especially if the set of headers for both grids are the same.
One approach you could take is to provide dependency property of the type DataTemplate called "ListTemplate". At the two points in your controls Xaml where you would display the lists use two ControlPresenter elements. One named "SourceContent" the other "TargetContent". For both bind ContentTemplate to this new "ListTemplate".
Code up the assignment of the Content property on these presenters and then assign the appropriate collection the ItemsSource of either the ItemsControl or DataGrid the presenter has loaded.
If you include a simple ListBox base data template as the default value for the "ListTemplate" property then you control should be usuable in its simplest form yet if the developer wants use a DataGrid with various columns they can define one in the
ListTemplate property.
Of course you will need to write code in your control to cope with the lists possibly being a DataGrid elements.
"List version" of the answer
(see also the grid version)
A custom control sounds right for this, but you want both lists to be templated so the developer can define the per item views. I have assumed you do not need headings so have stuck with listboxes. The test code below results in this:
As the listboxes already have item templates, you really want to expose those as properties in your custom user control. You can then edit the 2 templates individually (the example below simply has both Left and Right templates set to the same FirstName/LastName stackpanel, this is where you define the format of your listboxes):
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SilverlightApplication1"
mc:Ignorable="d"
x:Class="SilverlightApplication1.TestListSelectionControl"
d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate1">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding FirstName}"/>
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Left" VerticalAlignment="Top" Width="65" Text="{Binding LastName}"/>
</StackPanel>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<local:ListSelectionControl x:Name="SelectionControl" d:LayoutOverrides="Height" LeftItemTemplate="{StaticResource DataTemplate1}" RightItemTemplate="{StaticResource DataTemplate1}"/>
</Grid>
</UserControl>
The example ListSelectionControl XAML is below:
<UserControl x:Class="SilverlightApplication1.ListSelectionControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ListBox HorizontalAlignment="Stretch" Margin="12" Name="leftListBox" VerticalAlignment="Stretch" />
<ListBox HorizontalAlignment="Stretch" Margin="12" Name="rightListBox" VerticalAlignment="Stretch" Grid.Column="2" />
<StackPanel Grid.Column="1" Orientation="Vertical">
<Button Content="Add >" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
<Button Content="< Remove" Height="23" HorizontalAlignment="Left" Width="75" Margin="10" />
</StackPanel>
</Grid>
</UserControl>
And the simple code-behind of the control:
using System.Windows;
using System.Windows.Controls;
namespace SilverlightApplication1
{
public partial class ListSelectionControl : UserControl
{
public DataTemplate LeftItemTemplate
{
get
{
return leftListBox.ItemTemplate;
}
set
{
leftListBox.ItemTemplate = value;
}
}
public DataTemplate RightItemTemplate
{
get
{
return rightListBox.ItemTemplate;
}
set
{
rightListBox.ItemTemplate = value;
}
}
public ListSelectionControl()
{
InitializeComponent();
}
}
}
And just to complete the example, this is the code behind to populate the sample GUI:
using System.Windows.Controls;
using System.Collections.ObjectModel;
namespace SilverlightApplication1
{
public partial class TestListSelectionControl : UserControl
{
public TestListSelectionControl()
{
// Required to initialize variables
InitializeComponent();
SelectionControl.leftListBox.ItemsSource = Person.People;
SelectionControl.rightListBox.ItemsSource = Person.Machines;
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstname, string lastname)
{
this.FirstName = firstname;
this.LastName = lastname;
}
public static ObservableCollection<Person> People = new ObservableCollection<Person>()
{
new Person("Tom", "Jones"),
new Person("Elis", "Presley"),
new Person("Joe", "Blogs")
};
public static ObservableCollection<Person> Machines = new ObservableCollection<Person>()
{
new Person("Marvin", "Android"),
new Person("Hal", "9000"),
new Person("B", "9")
};
}
}

Resources