I try get original value on WPF treeview.
Commonly, Treeview we get selected item' original value using
object Item = treeview1.SelectedItem;
MessageBox.Show(Item.ToString());
but, my attempts to get it using this method were unsuccessful.
If I try it, then I get a "WPFName+TreeItem" MessageBox
This is my code with WPF
C#
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
object temp = treeView.SelectedItem;
MessageBox.Show(temp.ToString());
}
private static IEnumerable<TreeItem> GetChannelTreeForTreeView(QueryRunner queryRunner)
{
List<ChannelTreeItem> channelTree = queryRunner.Utils.GetChannelTree(false);
foreach (ChannelTreeItem channelTreeItem in channelTree)
{
TreeItem treeViewItem = new TreeItem { Data = channelTreeItem.Channel };
FillTreeViewItem(treeViewItem, channelTreeItem);
yield return treeViewItem;
}
}
private static void FillTreeViewItem(TreeItem treeViewItem, ChannelTreeItem channelTreeItem)
{
foreach (ClientListEntry clientListEntry in channelTreeItem.Clients)
if (clientListEntry.Nickname.Contains("serveradmin from") == false)
{
treeViewItem.Children.Add(new TreeItem { Data = clientListEntry });
}
foreach (ChannelTreeItem childChannelTreeItem in channelTreeItem.Children)
{
TreeItem childTreeViewItem = new TreeItem { Data = childChannelTreeItem.Channel };
treeViewItem.Children.Add(childTreeViewItem);
FillTreeViewItem(childTreeViewItem, childChannelTreeItem);
}
}
public class TreeItem
{
public object Data { get; set; }
public List<TreeItem> Children { get; private set; }
public TreeItem()
{
Children = new List<TreeItem>();
}
}
WPF
<TreeView x:Name="treeView" HorizontalAlignment="Left"
Height="265" VerticalAlignment="Top" Width="353"
SelectedItemChanged="treeView_SelectedItemChanged"
MouseUp="treeView_MouseUp">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:ViewItemWPF+TreeItem}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<ContentPresenter Content="{Binding Data}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type HelperClasses:ChannelListEntry}">
<StackPanel Orientation="Horizontal">
<Border Background="Green" Width="8" Height="12" BorderBrush="#00000000"></Border>
<TextBlock Text="{Binding Path=Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type HelperClasses:ClientListEntry}" >
<StackPanel Orientation="Horizontal">
<Border Background="DarkBlue" Width="8" Height="12" BorderBrush="#00000000"></Border>
<TextBlock Text="{Binding Path=Nickname}" Foreground="blue" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
Can I get original value using foreach or others methods?
You could cast the SelectedItem property to a TreeItem and then cast its Data property to the appropriate type. You can then access any properties of your classes as usual.
It is unclear what type "Channel" is but the following sample code should give you the idea:
private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
TreeItem temp = treeView.SelectedItem as TreeItem;
if (temp != null)
{
ClientListEntry clientListEntry = temp.Data as ClientListEntry;
if (clientListEntry != null)
{
//Data is a ClientListEntry
//...
return;
}
Channel channel = temp.Data as ClientListEntry;
if (channel != null)
{
//...
}
}
MessageBox.Show(temp.ToString());
}
Related
I'm using a CollectionView to group my item as following:
<CollectionViewSource Source="{Binding Competitions}" x:Key="GroupedData">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Item.Country" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
the full XAML structure is this:
<ComboBox x:Name="CompetitionCombo"
ItemsSource="{Binding Source={StaticResource GroupedData}}"
ItemTemplate="{StaticResource CombinedTemplate}">
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource containerStyle}"
HeaderTemplate="{StaticResource GroupHeader}">
</GroupStyle>
</ComboBox.GroupStyle>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
</DataTemplate>
<DataTemplate x:Key="GroupHeader">
<DockPanel>
<Image Source="{Binding Name.ISO,
Converter={StaticResource CountryIdToFlagImageSourceConverter}}"
Stretch="None" Width="23" Height="18" RenderOptions.BitmapScalingMode="HighQuality" />
<TextBlock Text="{Binding Name.Name}" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
<CheckBox Margin="5,0,0,0" HorizontalAlignment="Right" IsChecked="True" x:Name="PART_DisplayLeague"
Unchecked="CheckBoxCountry_Unchecked" Checked="CheckBoxCountry_Checked" />
</DockPanel>
</DataTemplate>
<Style TargetType="{x:Type GroupItem}" x:Key="containerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="False" x:Name="ComboExpander"
Header="{TemplateBinding Content}"
HeaderTemplate="{StaticResource GroupHeader}" >
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
as you can see in the GroupHeader I've a CheckBox, I'm trying to access to this CheckBox in the following way:
//current value of league.Country = "England";
//Iterate the available groups
foreach(CollectionViewGroup gp in CompetitionCombo.Items.Groups)
{
if(league.Country.Name == (gp.Name as Country).Name)
{
//Get the container
GroupItem container = CompetitionCombo.ItemContainerGenerator.ContainerFromItem(gp) as GroupItem;
//Get the control
var control = container.Template.FindName("PART_DisplayLeague", container);
control.IsChecked = true;
}
}
I get correctly the groups, but the control variable is even Null, seems that the code cannot find PART_DisplayLeague: the CheckBox associated to the Country in the header of the container.
Note:
the league is a simple implementation of the following:
public class League
{
public string Name { get; set; }
public Country Country { get; set; }
}
public class Country
{
public string Name { get; set; }
public string ISO { get; set; }
}
what am I doing wrong?
UPDATE
Forgot to add the collection implementation, in particular Competition:
public List<CheckedListItem<League>> Competitions = new List<CheckedListItem<League>>();
for add element to the collection:
Competitions.Add(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo" }
});
is possible find an implementation of CheckedListItem here
as you can see Item is part of CheckedListItem.
UPDATE #2
Practice example of the situation:
Suppose that Competitions contains the following Items:
Competitions.Add(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo", Country = new Country { Name = "Italy"}}
});
Competitions.Add(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo2", Country = new Country { Name = "Italy"}}
});
Competitions.Add(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo", Country = new Country { Name = "England"}}
});
the UI Structure will this:
[] Italy <- this is the GroupHeader sort by xaml
[] foo <- Items of the container
[] foo2
[] England
[] foo3
now what I'm asking for: When I check foo2 and foo the Italy CheckBox need to be Checked, also when I uncheck an Item contained in the Italy container, then, Italy must be unchecked.
This mean two thing:
if all items in the container are checked, then also the header checkbox need to be checked.
if almost one item in the container isn't checked, then the checkbox in the container must be unchecked 'cause this mean that not all the items associated to this country are checked.
My problem's that I can't find the CheckBox associated to the Country 'cause is generated by the Xaml, that's why I can't use a Binding property as suggested.
post the code that someone can just copy paste and reproduce your problem, then people can not only solve your problem but also suggest a better solution.
in the meantime, you can find your checkbox in the following manner:
var control = FindVisualChildren<CheckBox>(container);
Here you can find the method FindVisualChildren.
Edit: a full working example
public class League
{
public string Name { get; set; }
public Country Country { get; set; }
}
public class Country
{
public string Name { get; set; }
public string ISO { get; set; }
}
public class CheckedListItem<T>
{
private bool isChecked;
private T item;
public CheckedListItem() { }
public CheckedListItem(T item, bool isChecked = false)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
}
}
}
public partial class MainWindow : Window
{
public ObservableCollection<CheckedListItem<League>> Competitions { get; set; }
public MainWindow()
{
InitializeComponent();
Competitions = new ObservableCollection<CheckedListItem<League>> { (new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo", Country = new Country { Name = "Italy" } }
}),
(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo2", Country = new Country { Name = "Italy" } }
}),
(new CheckedListItem<League>
{
IsChecked = true,
Item = new League { Name = "foo", Country = new Country { Name = "England" } }
}) };
this.DataContext = this;
}
public void CheckBoxCountry_Checked(object sender, EventArgs args)
{
foreach (CollectionViewGroup gp in CompetitionCombo.Items.Groups)
{
//Get the container
GroupItem container = CompetitionCombo.ItemContainerGenerator.ContainerFromItem(gp) as GroupItem;
//Get the control
var control = FindVisualChildren<CheckBox>(container);
}
}
public IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
yield return (T)child;
foreach (T childOfChild in FindVisualChildren<T>(child))
yield return childOfChild;
}
}
}
}
.xaml
<Window x:Class="WpfApp2.MainWindow"
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:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<CollectionViewSource Source="{Binding Competitions}" x:Key="GroupedData">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Item.Country" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<DataTemplate x:Key="NormalItemTemplate">
<DockPanel>
<TextBlock Text="sdf" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}"
/>
</DataTemplate>
<DataTemplate x:Key="GroupHeader">
<DockPanel>
<TextBlock Text="{Binding Name.Name}" Margin="10,0,0,0" Foreground="Black" FontWeight="Bold"/>
<CheckBox Margin="5,0,0,0" HorizontalAlignment="Right" IsChecked="True" x:Name="PART_DisplayLeague"
Checked="CheckBoxCountry_Checked" />
</DockPanel>
</DataTemplate>
<Style TargetType="{x:Type GroupItem}" x:Key="containerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="False" x:Name="ComboExpander"
Header="{TemplateBinding Content}"
HeaderTemplate="{StaticResource GroupHeader}" >
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ComboBox x:Name="CompetitionCombo"
ItemsSource="{Binding Source={StaticResource GroupedData}}"
ItemTemplate="{StaticResource CombinedTemplate}">
<ComboBox.GroupStyle>
<GroupStyle ContainerStyle="{StaticResource containerStyle}"
HeaderTemplate="{StaticResource GroupHeader}">
</GroupStyle>
</ComboBox.GroupStyle>
</ComboBox>
</Grid>
</Window>
I added NormalItemTemplate because you didn't provide the template.
When you check the checkbox in the UI, you will call the method CheckBoxCountry_Checked in mainwindow.cs and that method will find the checkbox inside all 3 comboboxes.
I need some advice. I have two ListViews first ListView is "ListViewAlbums" and the second "ListViewTracks".
Items in ListViewTracks are organized into groups.
ListViewAlbums is set SelectionMode = "Extended" and Behaviours: MultiSelectorBehaviours.SynchronizedSelectedItems.
MultiSelectorBehaviours
When I select multiple items from ListViewAlbums, so I want to show all the items in ListViewTracks based ALBUMID.
My current code:
ListViewTracks GroupDescriptions:
PropertyGroupDescription groupDescription = new PropertyGroupDescription("AlbumID");
viewTrack.GroupDescriptions.Add(groupDescription);
viewTrack.SortDescriptions.Add(new System.ComponentModel.SortDescription("AlbumTitle", System.ComponentModel.ListSortDirection.Ascending));
viewTrack.SortDescriptions.Add(new System.ComponentModel.SortDescription("TitleTrack", System.ComponentModel.ListSortDirection.Ascending));
Employees data:
private ObservableCollection<EmployeesAlbums> _listAlbums = new ObservableCollection<EmployeesAlbums>();
public ObservableCollection<EmployeesTracks> _listTrack = new ObservableCollection<EmployeesTracks>();
public ObservableCollection<EmployeesTracks> PlayListTracks
{
get { return _listTrack; }
set { _listTrack = value; RaisePropertyChanged("PlayListTracks"); }
}
public ObservableCollection<EmployeesAlbums> PlayListAlbums
{
get { return _listAlbums; }
set { _listAlbums = value; RaisePropertyChanged("PlayListAlbums"); }
}
EmployeesAlbums model = new EmployeesAlbums
{
IdAlbums = int.Parse(rdr["Id"].ToString()),
TitleAlbum = rdr["TitleAlbums"].ToString(),
ArtistAlbum = rdr["ArtistAlbums"].ToString()
};
modelTracks = new EmployeesTracks
{
IdTrack = int.Parse(rdr["Id"].ToString()),
TitleTrack = rdr["TitleTrack"].ToString(),
PathTrack = rdr["Path"].ToString(),
AlbumTitle = rdr["AlbumTitle"].ToString(),
ArtistTrack = rdr["ArtistTrack"].ToString(),
AlbumID = int.Parse(rdr["AlbumId"].ToString()).ToString()
};
SelectedItem of ListViewAlbums:
SelectionChangedItemsAlbumCommand = new GalaSoft.MvvmLight.CommandWpf.RelayCommand(SelectionChangedItemsAlbum);
private EmployeesAlbums _selectedPlayListFileAlbum;
public EmployeesAlbums SelectedPlayListFileAlbum
{
get { return _selectedPlayListFileAlbum; }
set
{
if (_selectedPlayListFileAlbum != value)
{
_selectedPlayListFileAlbum = value;
RaisePropertyChanged("SelectedPlayListFileAlbum");
}
}
}
private IEnumerable<EmployeesAlbums> _selectedPlayListFilesAlbums;
public IEnumerable<EmployeesAlbums> SelectedPlayListFilesAlbums
{
get { return this.selectedPlayListFilesAlbums; }
set { Set(ref selectedPlayListFilesAlbums, value); }
}
Filtering:
public string AlbumID { get; set; }
void SelectionChangedItemsAlbum()
{
foreach (var items in SelectedPlayListFilesAlbums)
{
ListCollectionView empView = CollectionViewSource.GetDefaultView(PlayListTracks) as ListCollectionView;
// Enable Live Filtering of the ListViewCollection
empView.IsLiveFiltering = true;
// Enable the filtering on AlbumID
empView.LiveFilteringProperties.Add("AlbumID");
AlbumID = items.IdAlbums.ToString();
// Filter based upon AlbumID
empView.Filter = new Predicate<object>(IsMatchFoundAlbums);
// Refresh Collection
empView.Refresh();
}
}
bool IsMatchFoundAlbums(object d)
{
bool res = false;
EmployeesTracks emp = d as EmployeesTracks;
if (emp.AlbumID == AlbumID)
{
res = true;
}
return res;
}
XAML Code:
<ListView x:Name="ListViewTracks"
VirtualizingPanel.ScrollUnit="Pixel"
VirtualizingStackPanel.CacheLength="20,20"
VirtualizingStackPanel.CacheLengthUnit="Item"
ItemsSource="{Binding PlayListTracks}"
Style="{StaticResource CommonListViewStyleTracks}"
ItemContainerStyle="{DynamicResource styleListViewItem}"
>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</GroupStyle.Panel>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<StackPanel>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" />
<ItemsPresenter Margin="0,0,0,20" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate >
<Grid d:DesignWidth="460" Height="51">
<StackPanel>
<TextBlock DataContext="{Binding Items}" Text="{Binding AlbumTitle}" FontSize="18" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource ItemsListViewForeground}"/>
<TextBlock DataContext="{Binding Items}" Text="{Binding ArtistTrack}" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource AccentColorApps}"/>
</StackPanel>
</Grid>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
<ListView
x:Name="ListViewAlbums"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding PlayListAlbums}"
SelectionMode="Extended"
SelectedItem="{Binding SelectedPlayListFileAlbum,UpdateSourceTrigger=PropertyChanged}" SelectionMode="Extended"
behaviours:MultiSelectorBehaviours.SynchronizedSelectedItems="{Binding SelectedPlayListFilesAlbums, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Background="{x:Null}" Grid.Column="1" Grid.RowSpan="4"
ItemTemplate="{DynamicResource AlbumsDataTemplate}"
BorderBrush="{x:Null}"
>
<ie:Interaction.Triggers >
<ie:EventTrigger EventName="SelectionChanged">
<ie:InvokeCommandAction Command="{Binding SelectionChangedItemsAlbumCommand}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"/>
</ie:EventTrigger>
</ie:Interaction.Triggers>
</ListView>
Thanks a lot.
Try this
Right after you have initialized the property PlaylistTracks, do:
ListCollectionView empView = CollectionViewSource.GetDefaultView(PlayListTracks) as ListCollectionView;
empView.Filter = IsMatchFoundAlbums;
And let your Match method be:
bool IsMatchFoundAlbums(object d)
{
EmployeesTracks emp = d as EmployeesTracks;
return SelectedPlayListFilesAlbums.Any(x => x.IdAlbums == emp.AlbumID);
}
How can I get my app to navigate to a specific page based on the item within the semantic zoom that was tapped? Each item has a link to its own page and I want to use the 'item.Link' element so that the app reads the link and uses it to navigate to the specified page.
MetropolitanDataSource.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Exits_Expert_London_Lite
{
using System;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
// To significantly reduce the sample data footprint in your production application, you can set
// the DISABLE_SAMPLE_DATA conditional compilation constant and disable sample data at runtime.
#if DISABLE_SAMPLE_DATA
internal class SampleDataSource { }
#else
public class Item : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
private string _Station = string.Empty;
public string Station
{
get
{
return this._Station;
}
set
{
if (this._Station != value)
{
this._Station = value;
this.OnPropertyChanged("Station");
}
}
}
private string _Zone = string.Empty;
public string Zone
{
get
{
return this._Zone;
}
set
{
if (this._Zone != value)
{
this._Zone = value;
this.OnPropertyChanged("Zone");
}
}
}
private string _Link = string.Empty;
public string Link
{
get
{
return this._Link;
}
set
{
if (this._Link != value)
{
this._Link = value;
this.OnPropertyChanged("Link");
}
}
}
}
public class GroupInfoList<T> : List<object>
{
public object Key { get; set; }
public new IEnumerator<object> GetEnumerator()
{
return (System.Collections.Generic.IEnumerator<object>)base.GetEnumerator();
}
}
public class StoreData
{
public StoreData()
{
Item item;
item = new Item();
item.Station = "Amersham";
item.Zone = "Fare zone 9";
item.Link = "/Lines and Stations/Metropolitan/AMR_(Metropolitan).xaml";
Collection.Add(item);
item = new Item();
item.Station = "Chalfont & Latimer";
item.Zone = "Fare zone 8";
item.Link = "/Lines and Stations/Metropolitan/CFO_(Metropolitan).xaml";
Collection.Add(item);
item = new Item();
item.Station = "Chesham";
item.Zone = "Fare zone 9";
item.Link = "/Lines and Stations/Metropolitan/Chesham.xaml";
Collection.Add(item);
}
private ItemCollection _Collection = new ItemCollection();
public ItemCollection Collection
{
get
{
return this._Collection;
}
}
internal List<GroupInfoList<object>> GetGroupsByCategory()
{
List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
var query = from item in Collection
orderby ((Item)item).Zone
group item by ((Item)item).Zone into g
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList<object> info = new GroupInfoList<object>();
info.Key = g.GroupName;
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
internal List<GroupInfoList<object>> GetGroupsByLetter()
{
List<GroupInfoList<object>> groups = new List<GroupInfoList<object>>();
var query = from item in Collection
orderby ((Item)item).Station
group item by ((Item)item).Station[0] into g
select new { GroupName = g.Key, Items = g };
foreach (var g in query)
{
GroupInfoList<object> info = new GroupInfoList<object>();
info.Key = g.GroupName;
foreach (var item in g.Items)
{
info.Add(item);
}
groups.Add(info);
}
return groups;
}
}
// Workaround: data binding works best with an enumeration of objects that does not implement IList
public class ItemCollection : IEnumerable<Object>
{
private System.Collections.ObjectModel.ObservableCollection<Item> itemCollection = new System.Collections.ObjectModel.ObservableCollection<Item>();
public IEnumerator<Object> GetEnumerator()
{
return itemCollection.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(Item item)
{
itemCollection.Add(item);
}
}
#endif
}
Metropolitan_line.xaml.cs
using Exits_Expert_London_Lite.Common;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
namespace Exits_Expert_London_Lite.Lines_and_Stations.Metropolitan
{
public sealed partial class Metropolitan_line : Page
{
public Metropolitan_line()
{
this.InitializeComponent();
StoreData _storeData = null;
// creates a new instance of the sample data
_storeData = new StoreData();
// sets the list of categories to the groups from the sample data
List<GroupInfoList<object>> dataLetter = _storeData.GetGroupsByLetter();
// sets the CollectionViewSource in the XAML page resources to the data groups
cvsMetropolitan.Source = dataLetter;
// sets the items source for the zoomed out view to the group data as well
(semanticZoom.ZoomedOutView as ListViewBase).ItemsSource = cvsMetropolitan.View.CollectionGroups;
(semanticZoom.ZoomedInView as ListViewBase).SelectedIndex = -1;
}
#region Data Visualization
void ItemsGridView_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
ItemViewer iv = args.ItemContainer.ContentTemplateRoot as ItemViewer;
if (args.InRecycleQueue == true)
{
iv.ClearData();
}
else if (args.Phase == 0)
{
iv.ShowPlaceholder(args.Item as Item);
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 1)
{
iv.ShowStation();
args.RegisterUpdateCallback(ContainerContentChangingDelegate);
}
else if (args.Phase == 2)
{
iv.ShowZone();
}
args.Handled = true;
}
private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> ContainerContentChangingDelegate
{
get
{
if (_delegate == null)
{
_delegate = new TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs>(ItemsGridView_ContainerContentChanging);
}
return _delegate;
}
}
private TypedEventHandler<ListViewBase, ContainerContentChangingEventArgs> _delegate;
#endregion //Data Visualization
private void backButton_Tapped(object sender, TappedRoutedEventArgs e)
{
this.Frame.Navigate(typeof(Station_Chooser));
}
void GridView_ItemClicked(object sender, ItemClickEventArgs args)
{
var item = (Item)args.ClickedItem;
if (item.Link == "/Lines and Stations/Metropolitan/AMR_(Metropolitan).xaml")
((Frame)Window.Current.Content).Navigate(typeof(AMR__Metropolitan_));
else if (item.Link == "/Lines and Stations/Metropolitan/CFO_(Metropolitan).xaml")
((Frame)Window.Current.Content).Navigate(typeof(CFO__Metropolitan_));
else if (item.Link == "/Lines and Stations/Metropolitan/Chesham.xaml")
((Frame)Window.Current.Content).Navigate(typeof(Chesham));
}
}
}
ItemViewer.xaml
<UserControl
x:Class="Exits_Expert_London_Lite.ItemViewer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Exits_Expert_London_Lite"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Margin="0,0,0,20" HorizontalAlignment="Left">
<StackPanel>
<StackPanel Margin="10,0,0,0" Width="420">
<TextBlock x:Name="stationTextBlock" Foreground="{StaticResource ApplicationForegroundThemeBrush}" VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="Segoe UI" FontSize="32" FontWeight="Light" />
<TextBlock x:Name="zoneTextBlock" TextWrapping="Wrap" Foreground="White" FontSize="20" FontWeight="Light" VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
Metropolitan_line.xaml
<Page
x:Class="Exits_Expert_London_Lite.Lines_and_Stations.Metropolitan.Metropolitan_line"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Exits_Expert_London_Lite"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource x:Name="cvsMetropolitan" IsSourceGrouped="true" />
</Page.Resources>
<Grid Background="Black">
<Grid.ChildrenTransitions>
<TransitionCollection>
<EntranceThemeTransition/>
</TransitionCollection>
</Grid.ChildrenTransitions>
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button x:Name="backButton" Margin="39,59,39,0"
Style="{StaticResource NavigationBackButtonNormalStyle}"
VerticalAlignment="Top"
AutomationProperties.Name="Back"
AutomationProperties.AutomationId="BackButton"
AutomationProperties.ItemType="Navigation Button" Tapped="backButton_Tapped"/>
<TextBlock x:Name="pageTitle" Text="Metropolitan line" Style="{StaticResource HeaderTextBlockStyle}" Grid.Column="1"
IsHitTestVisible="false" TextWrapping="NoWrap" VerticalAlignment="Bottom" Margin="0,0,30,40" Foreground="#FF9B0056"/>
</Grid>
<Grid x:Name="Output" Grid.Row="1">
<!-- This shows a hard-coded width to show within the SDK Sample framework. In a real application you likely wouldn't set a width on the SemanticZoom -->
<SemanticZoom x:Name="semanticZoom" Margin="0,0,0,0">
<SemanticZoom.ZoomedOutView>
<GridView ScrollViewer.IsHorizontalScrollChainingEnabled="False" >
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Group.Key}" HorizontalAlignment="Center" FontFamily="Segoe UI" FontWeight="Light" FontSize="56" Foreground="White"/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid ItemWidth="200" ItemHeight="200" MaximumRowsOrColumns="4" VerticalAlignment="Center"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="#9B0056" />
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
</GridView.ItemContainerStyle>
</GridView>
</SemanticZoom.ZoomedOutView>
<SemanticZoom.ZoomedInView>
<GridView x:Name="ItemsGridView"
ItemsSource="{Binding Source={StaticResource cvsMetropolitan}}"
ShowsScrollingPlaceholders="False"
ContainerContentChanging="ItemsGridView_ContainerContentChanging"
IsSwipeEnabled="True" ScrollViewer.IsHorizontalScrollChainingEnabled="False" ItemClick="GridView_ItemClicked">
<GridView.ItemTemplate>
<DataTemplate>
<local:ItemViewer/>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text='{Binding Key}' Foreground="#FF9B0056" Margin="5" FontSize="50" FontFamily="Segoe UI" FontWeight="Light" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
</Grid>
</Grid>
</Page>
Here you go. In your code-behind...
void GridView_ItemClicked(object sender, ItemClickEventArgs args)
{
var item = (Item)args.ClickedItem;
if (item.Link == "/Page_1.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_1));
else if (item.Link == "/Page_2.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_2));
else if (item.Link == "/Page_3.xaml")
((Frame)Window.Current.Content).Navigate(typeof (Page_3));
}
Best of luck!
I am new with WPF - I want to create a tester for my server
I want to have on the right side of the application a TreeView and whenever a user selects a node - appropriate items is shown on the right side. For example I have a Connection node and under it many Sessions nodes, The Connection and Session have different parameters. I have built the tree view using mvvm and all works fine, but how can I achieve the second goal?
the xaml
<Window x:Class="Tree1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self ="clr-namespace:Tree1"
xmlns:models ="clr-namespace:Tree1.Models"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type self:TestPlanViewModel}" ItemsSource="{Binding Connections}">
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type self:ConnectionViewModel}" ItemsSource="{Binding Sessions}">
<StackPanel>
<TextBlock Text="Connection" Margin="10, 0, 0,0"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type self:SessionViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="10,0,0,0" Text="Session"></TextBlock>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid Margin="10">
<Button Height="23" VerticalAlignment="Top" Margin="277,10,144,0" Name="addSessionBtn" Width="76"></Button>
<TreeView Name="testPlanTview" Margin="0,10,283,0" SelectedItemChanged="testPlanTview_SelectedItemChanged">
<TreeViewItem ItemsSource="{Binding Connections}" Header="Test Plan">
</TreeViewItem>
</TreeView>
</Grid>
You can use DataTemplates. There are two possible solution for your problem.
First of all let's create a base class:
public abstract class SelectableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isSelected;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool IsSelected
{
get
{
return isSelected;
}
set
{
if (isSelected != value)
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
}
For our example we have two classes:
Car, whose properties are "Hp" (int) and "Matriculation" (DateTime)
Person, whose properties are "Name" (string) and "Surname" (string)
Both of them extend SelectableObject.
Now the ViewModel (of course it is just a sample):
public class ViewModel : SelectableObject
{
private ArrayList tree = new ArrayList();
private ObjectWrapper current;
private Car car = new Car();
private Person person = new Person();
public ViewModel()
{
car.Hp = 120;
car.Matriculation = DateTime.Today;
car.PropertyChanged += new PropertyChangedEventHandler(OnItemPropertyChanged);
person.Name = "John";
person.Surname = "Doe";
person.PropertyChanged += new PropertyChangedEventHandler(OnItemPropertyChanged);
tree.Add(car);
tree.Add(person);
}
private void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
SelectableObject impl = (SelectableObject)sender;
if (e.PropertyName == "IsSelected" && impl.IsSelected)
{
Current = new ObjectWrapper(impl);
}
}
public ObjectWrapper Current
{
get
{
return current;
}
private set
{
current = value;
OnPropertyChanged("Current");
}
}
public IEnumerable Tree
{
get
{
return tree;
}
}
}
The ViewModel uses a the ObjectWrapper class:
public class ObjectWrapper
{
private readonly object wrappedInstance;
private readonly ReadOnlyCollection<PropertyWrapper> propertyWrappers;
public ObjectWrapper(object instance)
{
Collection<PropertyWrapper> collection = new Collection<PropertyWrapper>();
Type instanceType;
wrappedInstance = instance;
instanceType = instance.GetType();
foreach (PropertyInfo propertyInfo in instanceType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
{
collection.Add(new PropertyWrapper(instance, propertyInfo));
}
propertyWrappers = new ReadOnlyCollection<PropertyWrapper>(collection);
}
public ReadOnlyCollection<PropertyWrapper> PropertyWrappers
{
get
{
return propertyWrappers;
}
}
public object Instance { get { return wrappedInstance; } }
}
public class PropertyWrapper
{
private readonly object instance;
private readonly PropertyInfo propertyInfo;
public PropertyWrapper(object instance, PropertyInfo propertyInfo)
{
this.instance = instance;
this.propertyInfo = propertyInfo;
}
public string Label
{
get
{
return propertyInfo.Name;
}
}
public Type PropertyType
{
get
{
return propertyInfo.PropertyType;
}
}
public object Value
{
get
{
return propertyInfo.GetValue(instance, null);
}
set
{
propertyInfo.SetValue(instance, value, null);
}
}
}
First solution (the easiest one)
You can use implicit datatemplating:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="600">
<DockPanel>
<TreeView ItemsSource="{Binding Tree}" DockPanel.Dock="Left" Margin="5">
<TreeView.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<ContentControl Content="{Binding Path=Current.Instance, Mode=OneWay}" Margin="5">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:Car}">
... define your car template here ...
</DataTemplate>
<DataTemplate DataType="{x:Type local:Person}">
... define your person template here ...
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>
Second solution (imho the best one)
You can take advantage of ObjectWrapper object, by using an ItemsControl (here I used Extended WPF Toolkit for DateTime and Int controls):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:toolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="600">
<Window.Resources>
<local:ItemTemplateSelector x:Key="ItemTemplateSelector" />
<DataTemplate x:Key="{x:Type sys:String}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
<TextBox Text="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="{x:Type sys:DateTime}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
<toolkit:DateTimePicker Value="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="{x:Type sys:Int32}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Label, Mode=OneWay}" VerticalAlignment="Center" />
<toolkit:IntegerUpDown Value="{Binding Path=Value, Mode=TwoWay}" Margin="5" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<DockPanel>
<TreeView ItemsSource="{Binding Tree}" DockPanel.Dock="Left" Margin="5">
<TreeView.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="{x:Type TreeViewItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<ItemsControl ItemsSource="{Binding Path=Current.PropertyWrappers, Mode=OneWay}"
Margin="5" ItemTemplateSelector="{StaticResource ItemTemplateSelector}" />
</DockPanel>
</Window>
The implementation of the ItemTemplateSelector it is not difficult:
public class ItemTemplateSelector : DataTemplateSelector
{
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
PropertyWrapper propertyWrapper = (PropertyWrapper)item;
FrameworkElement frameworkElement = (FrameworkElement)container;
DataTemplate dataTemplate = (DataTemplate)frameworkElement.TryFindResource(propertyWrapper.PropertyType);
return dataTemplate;
}
}
My answer is quite long, but I wanted to show you both roads you can use.
Of course you can improve the PropertyWrapper by using attributes.
If you're already using MVVM i would sugest something like System.Windows.Interactivity and Prism regions to implement what you need.
For example:
Xaml:
<TreeView Name="testPlanTview" Margin="0,10,283,0">
<TreeViewItem ItemsSource="{Binding Connections}" Header="Test Plan">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction Command="{Binding OpenNewViewCommand}"
CommandParameter="{Binding SelectedItem,ElementName=testPlanTview}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeViewItem>
</TreeView>
<ContentControl prism:RegionManager.RegionName="MyRegion"/>
Viewmodel:
public ICommand OpenNewViewCommand
{
get { return this.selectedCommand; }
}
In the view model constructor you add:
this.selectedCommand = new DelegateCommand<YourModel>(this.SelectedExecute);
And the command:
private void SelectedExecute(YourModel parameter)
{
this.regionManager.RequestNavigate(RegionNames.MyRegion, new Uri("ViewToNavigate", UriKind.Relative), parameters);
}
Please be aware that this is just an example on how to make the navigation possible with prism. For more information on what i'm suggesting you can check the msdn link here
I'm trying to use Caliburn.Micro to bind a view model of a nested ListBox but I'm stuck.
I have a list workspace that is divided in two sections a list of groups containtaining items in a different part of that workspace I want to show the details of either a group or an item in a group depending on what is the SelectedItem. I'm new to Caliburn.Micro and looked at the documentation and samples but don't know how to connect the dots. Specifically I'm trying to model this after the Caliburn.Micro.HelloScreens sample. The code I have so far:
The ViewModel:
public class AnalyzerGroupWorkspaceViewModel : Conductor<AnalyzerGroupWorkspaceViewModel>, IWorkspace
{
private Selected selected = Selected.AnalyzerGroup;
private const string name = "Analyzers";
public AnalyzerGroupWorkspaceViewModel(
IMappingEngine fromMapper,
IRepository<Model.AnalyzerGroup> analyzerGroups)
{
AnalyzerGroups = new ObservableCollection<IAnalyzerGroupViewModel>(analyzerGroups.GetAll().Select(fromMapper.Map<Model.AnalyzerGroup,AnalyzerGroupViewModel>));
}
public ObservableCollection<IAnalyzerGroupViewModel> AnalyzerGroups { get; private set; }
public string Name { get { return name; } }
public Selected Selected
{
get { return selected; }
set
{
if (value == selected) return;
selected = value;
NotifyOfPropertyChange(() => Selected);
}
}
private IConductor Conductor { get { return (IConductor) Parent; } }
public void Show()
{
var haveActive = Parent as IHaveActiveItem;
if (haveActive != null && haveActive.ActiveItem == this)
{
DisplayName = name;
Selected = Selected.AnalyzerGroup;
}
else
{
Conductor.ActivateItem(this);
}
}
}
The view:
<UserControl x:Class="Philips.HHDx.SSW.AnalyzerGroup.AnalyzerGroupWorkspaceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<DockPanel>
<GroupBox Header="AnalyzerGroups" DockPanel.Dock="Top">
<ListBox x:Name="AnalyzerGroups">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}" />
<ListBox x:Name="Analyzers">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id }"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Header="Details">
<ContentControl cal:View.Context="{Binding Selected, Mode=TwoWay}"
cal:View.Model="{Binding}"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"/>
</GroupBox>
</DockPanel>
</UserControl>
Next to that I have two UserControls that display the detailsof a group or item.
My specific question is how can I use the SelectedItem property of the two ListBoxes to modify the Selected property to switch between displaying the AnalyzerGroup details and the Analyzer details?
I've found the solution to the above described problem the solution consists of four parts:
Add a IsSelected property (that notifies changes) to both 'child' ViewModels
Bind the IsSelected property of the ListBox.ItemContainerStyle to the IsSelected property of the respective ViewModels
Attach a Caliburn.Micro Message to the 'outer' ListBox and use the $eventArgs argument
In the ViewModel bound to the entire UserControl implement the method corresponding to the Message and use the AddedItems property of the eventArgs to set the SelectedViewModel property setting the IsSelected property of the previous SelectedViewModel to false
The code then becomes:
The ViewModel:
public class AnalyzerGroupWorkspaceViewModel : PropertyChangedBase, IAnalyzerGroupWorkspaceViewModel
{
private IAnalyzerViewModel selectedViewModel;
private const string WorkspaceName = "Analyzers";
public AnalyzerGroupWorkspaceViewModel(
IMappingEngine fromMapper,
IRepository<Model.AnalyzerGroup> analyzerGroups)
{
AnalyzerGroups = new ObservableCollection<IAnalyzerGroupViewModel>(
analyzerGroups.GetAll().Select(
fromMapper.Map<Model.AnalyzerGroup, AnalyzerGroupViewModel>));
}
public void SelectionChanged(object eventArgs)
{
var typedEventArgs = eventArgs as SelectionChangedEventArgs;
if (typedEventArgs != null)
{
if (typedEventArgs.AddedItems.Count > 0)
{
var item = typedEventArgs.AddedItems[0];
var itemAsGroup = item as IAnalyzerViewModel;
if (itemAsGroup != null)
{
SelectedViewModel = itemAsGroup;
}
}
}
}
public ObservableCollection<IAnalyzerGroupViewModel> AnalyzerGroups { get; private set; }
public string Name { get { return WorkspaceName; } }
public IAnalyzerViewModel SelectedViewModel
{
get { return selectedViewModel; }
set
{
if (Equals(value, selectedViewModel))
{
return;
}
if (SelectedViewModel != null)
{
SelectedViewModel.IsSelected = false;
}
selectedViewModel = value;
NotifyOfPropertyChange(() => SelectedViewModel);
}
}
}
The View:
<UserControl x:Class="Philips.HHDx.SSW.AnalyzerGroup.AnalyzerGroupWorkspaceView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburnproject.org">
<DockPanel>
<GroupBox Header="AnalyzerGroups" DockPanel.Dock="Top">
<ListBox SelectionMode="Single"
x:Name="AnalyzerGroups"
cal:Message.Attach="[Event SelectionChanged] = [Action SelectionChanged($eventArgs)]">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="2" BorderBrush="DarkGray">
<StackPanel Orientation="Vertical" Margin="10">
<TextBlock Text="{Binding Name}" />
<ListBox SelectionMode="Single" ItemsSource="{Binding Analyzers}" >
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="1" BorderBrush="DarkGray">
<StackPanel>
<TextBlock Text="{Binding Name }" Margin="10" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Header="Details">
<ContentControl cal:View.Model="{Binding SelectedViewModel}" />
</GroupBox>
</DockPanel>
</UserControl>
The answer to your specific question is yes, you can.
On the ViewModel of your UserControl. You create a property that is a ViewModel of either of the two details.
public interface IAnalyzerViewModel
{
}
Next, create two ViewModels for the Views of your Analyzer and AnalyzerGroup views.
public class AnalyzerGroupViewModel : IAnalyzerViewModel
{
}
public class AnalyzerViewModel : IAnalyzerViewModel
{
}
Next, create a property in your UserControl's ViewModel that implements INPC or PropertyChangedBase of Caliburn Micro.
public class MainViewModel :
{
private IAnalyzerViewModel _analyzerViewModel;
public IAnalyzerViewModel SelectedViewModel { get { return _analyzerViewModel; } set { _analyzerViewModel = value; OnPropertyChanged(() => SelectedViewModel); }
//Hook up the selected item changed event of your listbox and set the appropriate ViewModel to show, so if you either want to show the AnalyzerGroup or AnalyzerView.
}
And lastly, just update your MainView to
<ContentControl x:Name="SelectedViewModel"
VerticalContentAlignment="Stretch"
HorizontalContentAlignment="Stretch"/>
Caliburn will hook up the appropriate bindings and stuff and will pull the View for the associated ViewModel, and also the Name convention part will automatically map it to any public property of its datacontext as long as the names match.