Okay, I might be asking an age old question, but I did not get my scenario described in any of them.
I have an Oject which can contain several child Objects. Eg. A Project obejct can have several Resource objects. I have an ObservaleCollection with super set of child obejcts (in my case Resource objects). I also have another ObservableCollection in Project object containing existing childs.
What is the best way to present this to user in WPF windows application? I also need to provide a way for them to chnage the mapping as well.
My initial idea was to use classic Double List approach, with two listboxes, but I am not sure how easy it would be to manipulate view layer alone.
[Resoure Collection] [Resoure Collection in a Project]
-------------------- ---------------------------------
|Resource 1 | > |Resource 3 |
|Resource 2 | >> |Resource 4 |
|Resource 5 | < | |
|Resource 6 | << | |
|Resource 7 | | |
I need similar UI for 4 more similar mappings of different objects. I tried to move this to a user control, but looks like I can’t have Generic collection (private ObservableCollection) in an UserControl.
Any ideas from experience members?
/**************************************************/
Edit: THis is what I got so far, note that I am going with UserControl since I need the same UI in multiple screens, and I feel UserCOntrol will give me more manageable code.
XAML for the user control
<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"
mc:Ignorable="d"
x:Class="TimeTracker.ItemsSelectionLists"
x:Name="ItemsSelectionControl">
<Grid x:Name="LayoutRoot">
<Grid Background="#FFF9FDFD"
Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock x:Name="SourceHeading"
Grid.Column="0"
Margin="8,8,0,0"
TextWrapping="Wrap"
Text="Whole Team"
VerticalAlignment="Top" />
<ListBox x:Name="SourceItemsList"
Grid.Column="0"
Margin="8,30,8,8"
MinWidth="150"
SelectionMode="Multiple"
ItemsSource="{Binding Path=Collection1}"/>
<StackPanel Grid.Column="1"
Margin="0"
Orientation="Vertical"
VerticalAlignment="Center">
<Button Content=">"
Height="25"
Width="25" />
<Button Content=">>"
Height="25"
Width="25" />
<Button Content="<"
Height="25"
Width="25" />
<Button Content="<<"
Height="25"
Width="25" />
</StackPanel>
<TextBlock x:Name="TargetHeading"
Grid.Column="2"
Margin="8,8,8,0"
TextWrapping="Wrap"
Text="Current Team"
VerticalAlignment="Top" />
<ListBox x:Name="SelectedItemsList"
Grid.Column="2"
Margin="8,30,8,8"
MinWidth="150"
ItemsSource="{Binding Path=Collection2}"/>
</Grid>
</Grid>
</UserControl>
Code:
/// <summary>
/// Interaction logic for ItemsSelectionLists.xaml
/// </summary>
public partial class ItemsSelectionLists: UserControl
{
[Bindable(true)]
internal ObservableCollection<TrackerItem> SourceList
{
get
{
return _vm.Collection1;
}
set
{
_vm.Collection1 = value;
}
}
private readonly ViewModel _vm;
public ItemsSelectionLists()
{
this.InitializeComponent();
_vm = new ViewModel();
this.DataContext = _vm;
}
}
public class ViewModel : INotifyPropertyChanged
{
#region Properties
private ObservableCollection<TrackerItem> _collection1;
/// <summary>
/// This is the first collection.
/// </summary>
internal ObservableCollection<TrackerItem> Collection1
{
get
{
return _collection1;
}
set
{
if (value != _collection1)
{
_collection1 = value;
NotifyPropertyChanged("Collection1");
}
}
}
private ObservableCollection<TrackerItem> _collection2;
/// <summary>
/// This is the second collection.
/// </summary>
internal ObservableCollection<TrackerItem> Collection2
{
get
{
return _collection2;
}
set
{
if (value != _collection2)
{
_collection2 = value;
NotifyPropertyChanged("Collection2");
}
}
}
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
public ViewModel()
{
// Create initial collections.
// Populate first collection with sample data
_collection1 = new ObservableCollection<TrackerItem>();
// Seconf collection is empty
_collection2 = new ObservableCollection<TrackerItem>();
}
#endregion
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
Main Window
<TabItem Header="Resource Allocation">
<local:ItemsSelectionLists x:Name="ResourceSelection"/>
</TabItem>
Code
ResourceSelection.SourceList = MainObject.Resources;
//error CS0029: Cannot implicitly convert type 'System.Collections.ObjectModel.ObservableCollection<TimeTracker.Resource>' to 'System.Collections.ObjectModel.ObservableCollection<TimeTracker.TrackerItem>'
It’s not graceful or polished, but here is a workable sample. If I made this polished I would have implemented true MVVM, but as a sample, this will get you started.
XAML:
The code-behind:
The ViewModel:
TrackerItem