Define different DataTemplate for same source - c#

I'm trying to show collection of bool with a DataTemplate for ListView.
Here's the code:
In MainWindow.xaml
<Window.Resources>
<DataTemplate x:Key="ListItemCheckBoxTemplate">
<CheckBox IsChecked="{Binding Mode=OneWay}"/>
</DataTemplate>
<DataTemplate x:Key="ListItemRadioButtonTemplate">
<RadioButton IsChecked="{Binding Mode=OneWay}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="ListViewSample" />
<ListBox ItemsSource="{Binding MyData}" ItemTemplate="{StaticResource ListItemCheckBoxTemplate}" Grid.Column="1"/>
</Grid>
In MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
public class ViewModel
{
private List<bool> myBooleanCollection;
public List<bool> MyData
{
get { return myBooleanCollection; }
set { myBooleanCollection = value; }
}
public ViewModel()
{
myBooleanCollection = new List<bool>();
myBooleanCollection.Add(true);
myBooleanCollection.Add(false);
myBooleanCollection.Add(true);
myBooleanCollection.Add(true);
myBooleanCollection.Add(false);
}
}
Instead of ItemTemplate ListItemCheckBoxTemplate I want to apply ListItemRadioButtonTemplate with Radio button. How I can specify the use of ItemTemplate for the same source in xaml. Do I need to change the DataSource for the same or
do I have some way to specify the DataTemplate in xaml based on condition.

You did not specify what is the condition on which you want to change template but you can add another property to your view model, say AllowMultiple:
public class ViewModel: INotifyPropertyChanged
{
private List<bool> myBooleanCollection;
public List<bool> MyData
{
get { return myBooleanCollection; }
set { myBooleanCollection = value; }
}
private bool _allowMultiple;
public bool AllowMultiple
{
get { return _allowMultiple; }
set
{
if (_allowMultiple != value)
{
_allowMultiple = value;
OnPropertyChanged("AllowMultiple");
}
}
}
}
and then change ListBox.Style to:
<ListBox ItemsSource="{Binding MyData}">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate" Value="{StaticResource ListItemCheckBoxTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=AllowMultiple}" Value="False">
<Setter Property="ItemTemplate" Value="{StaticResource ListItemRadioButtonTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
assuming that ViewModel implements INotifyPropertyChanged you can then change ItemTemplate buy changing AllowMultiple against view model

You can specify DataTemplate for each item with the followign attribute:
ItemTemplate="{StaticResource ListItemCheckBoxTemplate}
Change this to:
ItemTemplate="{StaticResource ListItemRadioButtonTemplate}
If you need multiple item templates in the same ListBox then you can provide custom template selection logic via the DataTemplateSelector Class
If you want to do it in Xaml, you will need to expose property which you can bind to as a selector. Have a look at A Data Template Selector in Xaml

You need create DataTemplateSelector and using this for ItemTemplateSelector. You also need to determine the condition under which you will return the DataTemplate. Example:
XAML
<Window x:Class="YourNamespace.MainWindow" ...
xmlns:this="clr-namespace:YourNamespace"
<Window.Resources>
<DataTemplate x:Key="OneTemplate">
<CheckBox IsChecked="{Binding Mode=OneWay}" />
</DataTemplate>
<DataTemplate x:Key="SecondTemplate">
<RadioButton IsChecked="{Binding Mode=OneWay}" />
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding MyData}">
<ListBox.ItemTemplateSelector>
<this:MyItemTemplateSelector OneTemplate="{StaticResource OneTemplate}"
SecondTemplate="{StaticResource SecondTemplate}" />
</ListBox.ItemTemplateSelector>
</ListBox>
Code-behind
public class MyItemTemplateSelector : DataTemplateSelector
{
public DataTemplate OneTemplate { get; set; }
public DataTemplate SecondTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
bool myItem = (bool)item;
if (myItem == true)
{
return OneTemplate;
}
return SecondTemplate;
}
}

Related

C# Caliburn.Micro Multiple selection

I am using Caliburn.Micro in my C# WPF project and I have sucessfully used single selection binding in ListBox. How to use multiple selection in this scenario?
Xaml:
<ListBox x:Name="Items">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Time}"/>
<Label Content="{Binding Desc}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code:
public BindableCollection<MyObject> Items
{
get
{
var collection = new BindableCollection<MyObject>(_MyObject);
return collection;
}
}
public MyObject SelectedItem
{
get; set;
}
Add IsSelected property to your item:
public class MyObject : PropertyChangedBase
{
public DateTime Time { get; set; }
public String Desc { get; set; }
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
NotifyOfPropertyChange();
}
}
}
Then add binding to this property to your ListBox:
<ListBox x:Name="Items" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Time}"/>
<Label Content="{Binding Desc}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
After that you may refer to selected items in your View Model:
BindableCollection<MyObject> _items = new BindableCollection<MyObject>();
public BindableCollection<MyObject> Items
{
get
{
return _items;
}
}
public BindableCollection<MyObject> SelectedItems
{
get
{
_selectedItems.Clear();
_selectedItems.AddRange(Items.Where(mo => mo.IsSelected));
return _selectedItems;
}
}

Add textbox in Checkboxlist for particular item checked WPF

I am creating a application using WPF. I am able to bind Checkboxlist by using the below code:
XAML:
<DataTemplate x:Key="defaultTemplate">
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Checked}" Value="true">
<Setter TargetName="lstcheckbox" Property="ContentTemplate" Value="{DynamicResource Template1}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate x:Key="Template1">
<TextBox Width="100"></TextBox>
</DataTemplate>
<local:SomeTemplateSelector x:Key="SomeTemplateSelector" DefaultTemplate="{StaticResource defaultTemplate}"
Template1="{StaticResource Template1}">
</local:SomeTemplateSelector>
</Window.Resources>
<Grid>
<dxd:DockLayoutManager x:Name="dockManager" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Visible" ScrollViewer.VerticalScrollBarVisibility="Visible">
<dxd:LayoutGroup Caption="Review Checklist" Orientation="Vertical" ShowCaption="True" GroupBorderStyle="Group" CaptionAlignMode="AlignInGroup">
<dxd:LayoutControlItem>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" Margin="8,5,5,5">Safety Equipment</TextBlock>
<!--<ListView x:Name="lstSafEquip" Grid.Column="0" Grid.Row="1" BorderThickness="0" Margin="5,5,5,5" SelectionMode="Multiple" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource SomeTemplateSelector}" ItemTemplate="{StaticResource ItemDataTemplate}" IsSynchronizedWithCurrentItem="true" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="200" />-->
<ListView x:Name="lstSafEquip" Grid.Column="0" Grid.Row="1" BorderThickness="0" Margin="5,5,5,5" ItemsSource="{Binding}" ItemTemplateSelector="{StaticResource SomeTemplateSelector}" IsSynchronizedWithCurrentItem="true" ScrollViewer.VerticalScrollBarVisibility="Auto" MaxHeight="200" />
</Grid>
</dxd:LayoutControlItem>
</dxd:LayoutGroup>
</dxd:DockLayoutManager>
</Grid>
CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
BindCheckBoxs(lstSafEquip);
}
private void BindCheckBoxs(ListView lst)
{
try
{
ObservableCollection<Data> Source = new ObservableCollection<Data> { new Data(1, "Apple"), new Data(2, "Mango"), new Data(3, "Others"), new Data(3, "Banana"), new Data(3, "Grapes") };
lst.ItemsSource = Source;
}
catch (Exception ex)
{
throw new Exception(ex.Message.ToString());
}
}
}
public class SomeTemplateSelector : DataTemplateSelector
{
public DataTemplate Template1 { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
Data _Item = (Data)item;
if (_Item.Name == "Others")
{
return Template1;
}
return DefaultTemplate;
}
}
DATA.CS:
public class Data
{
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
public Data(int _ID, string _Name)
{
ID = _ID;
Name = _Name;
Checked = false;
}
}
PROBLEM:
I want to add textbox just below the checkboxlist item Others when user will checked this item.
By using the above code trigger enter textbox for each item when i checked.
please let me know where i am going wrong. I will try my best to provide more info if needed.
From what i understood from your post and comments:
Add a TextBox as Barptad suggested but use a BooleanToVisibilityCollapsed Converter as stated below:
BooleanToVisibilityCollapsedConverter.cs:
public class BooleanToVisibilityCollapsedConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
return ((bool) value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture) {
//never used
return null;
}
}
XAML:
<DataTemplate x:Key="defaultTemplate">
<StackPanel>
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<TextBox Visibility="{Binding IsChecked, ElementName=lstcheckbox}"/>
</StackPanel>
</DataTemplate>
If you use Visibility.Hidden the textbox is not visible but still takes place.
After merging all suggestions and comments. I reached on the solution.
XAML:
Added textbox in default template
<DataTemplate x:Key="defaultTemplate">
<StackPanel>
<dxe:CheckEdit x:Name="lstcheckbox" Checked="lstcheckbox_Checked_1" Unchecked="lstcheckbox_Checked_1" Content="{Binding Name}" IsChecked="{Binding Path=Checked}" />
<TextBox Name="txtOther" Visibility="{Binding Path=Visible}" Text="test" Width="100"></TextBox>
</StackPanel>
</DataTemplate>
XAML.CS:
private void lstcheckbox_Checked_1(object sender, RoutedEventArgs e)
{
Data some = (sender as CheckEdit).DataContext as Data;
if (some.Name.Contains("Others"))
{
some.Visible = ToggleVisibility(some.Visible);
}
}
private Visibility ToggleVisibility(Visibility visibility)
{
return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
DATA.CS:
Added visible property and inherited with INotifyPropertyChanged to track change in property in Data.cs
public class Data : INotifyPropertyChanged
{
private Visibility _visible;
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
//public Visibility Visible { get; set; }
public Visibility Visible
{
get { return this._visible; }
set
{
this._visible = value;
RaisePropertyChanged("Visible");
}
}
public Data(int _ID, string _Name,Visibility _visible)
{
ID = _ID;
Name = _Name;
Visible = _visible;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
SomeTemplateSelector.CS
public class SomeTemplateSelector : DataTemplateSelector
{
public DataTemplate OthersTemplate { get; set; }
public DataTemplate DefaultTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return DefaultTemplate;
}
}
Hope this will help others.
Correct Me If I am worng!
U want to show textbox only under "Others" item when it is checked.
For the scenario mentioned above u need to have two different templates that changes on the value of item.
For e.g if the item is "Others" it should have template that have a text box just below check box which is only shown when checkbox is checked otherwise collapsed.
for the second type of items it should have different template even if it is checked no textbox should be displayed under it.
(Step 1) Create a Template Selector
public class ValueDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DefaultTemplate { get; set; }
public DataTemplate OthersTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
DataTemplate template = DefaultTemplate;
CheckList data = item as CheckList;
if (data.Description.Contains("Others"))
{
template = OthersTemplate;
}
return template;
}
}
Reference assembly in your file.
(Step 3) Create two templates as given below
<Window.Resources>
<DataTemplate x:Key="DefaultTemplate">
<StackPanel>
<CheckBox IsChecked="{Binding Path=IsChecked}" Content="{Binding Path=Description}"></CheckBox>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="OthersTemplate">
<DataTemplate.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsChecked}" Value="false">
<Setter Property="Visibility" Value="Collapsed"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataTemplate.Resources>
<StackPanel>
<CheckBox IsChecked="{Binding Path=IsChecked}" Content="{Binding Path=Description}"></CheckBox>
<TextBox x:Name="othersTextBox">
</TextBox>
</StackPanel>
</DataTemplate>
<local:ValueDataTemplateSelector x:Key="CheckListTemplateSelector"
DefaultTemplate="{StaticResource DefaultTemplate}"
OthersTemplate="{StaticResource OthersTemplate}"/>
</Window.Resources>

Binding from a property of each item inside an ItemsControl to a property of an object outside the ItemsControl

I have an ItemsControl, and a Button outside the ItemsControl. Each item inside the ItemsControl has a dependency property called "MyProperty" (defined in the code-behind).
I would like to set the IsEnabled property of the Button to false when at least one of the items in the ItemsControl has the MyProperty property set to 5. (of course this is just a stupid example of a more complicated situation)
I tried by means of a data trigger, but with no luck:
XAML:
<Window x:Class="cancellami24.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="{x:Type ContentPresenter}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=MyProperty}" Value="5">
<Setter Property="IsEnabled" TargetName="MyButton" Value="False" /><!--error on TargetName-->
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ItemsControl x:Name="MyListBox" Grid.Row="0" ItemContainerStyle="{StaticResource MyStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=MyProperty, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="MyButton" Grid.Row="1" Click="MyButton_Click"/>
</Grid>
</Window>
Code-behind:
using System.Collections.ObjectModel;
using System.Windows;
namespace cancellami24
{
public partial class MainWindow : Window
{
private readonly ObservableCollection<MyItem> myCollection = new ObservableCollection<MyItem>();
public MainWindow()
{
InitializeComponent();
myCollection.Add(new MyItem(1));
myCollection.Add(new MyItem(2));
myCollection.Add(new MyItem(3));
MyListBox.ItemsSource = myCollection;
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
myCollection[2].SetValue(MyItem.MyPropertyProperty, 5);
}
}
public class MyItem : DependencyObject
{
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyProperty", typeof(int), typeof(MyItem));
public MyItem(int propertyValue)
{
SetValue(MyPropertyProperty, propertyValue);
}
}
}
You need custom converter to solve it
public class MyConverter : IValueConverter
{
bool flag = false;
var collection = value as ObservableCollection<MyItem>();
if(collection==null) return flag;
foreach (var item in collection)
{
if (item.MyProperty==5)
{
flag = true;
break;
}
}
return flag;
}
Add MyConverter to your App.xaml
<local:MyConverter x:key="MyConverter"/>
Xaml:
<Button x:Name="MyButton" IsEnabled="{Binding ElementName=MyListBox, Path=ItemsSource, Converter={StaticResource MyConverter}}"/>

Binding SelectedItem in nested ListBox using Caliburn.Micro

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.

Why does ComboBox not initially show the ComboBoxItem with IsSelected==true?

In the following code, why doesn't the item with property IsSelected set to true become selected in the ComboBox just as it does in the ListBox after clicking the Button?
Once I click on the ComboBox, the selected item becomes selected, but not before.
xaml:
<Window x:Class="WpfApplication1.Desktop.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<StackPanel>
<ListBox ItemsSource="{Binding Items}">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Txt}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ComboBox ItemsSource="{Binding Items}">
<ComboBox.Resources>
<Style TargetType="ComboBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected, Mode=TwoWay}" />
</Style>
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Txt}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Select second item" Click="Button_Click" />
</StackPanel>
</Window>
xaml.cs:
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Windows;
using Microsoft.Practices.Prism.ViewModel;
namespace WpfApplication1.Desktop
{
[Export]
public partial class Shell : Window
{
public class Foo : NotificationObject
{
static int _seq = 0;
string _txt = "Item " + (++_seq).ToString();
public string Txt { get { return _txt; } }
bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
_isSelected = value;
RaisePropertyChanged(() => IsSelected);
}
}
}
public ObservableCollection<Foo> Items { get; set; }
public Shell()
{
Items = new ObservableCollection<Foo>();
for (int i = 0; i < 5; i++)
Items.Add(new Foo());
DataContext = this;
InitializeComponent();
}
void Button_Click(object sender, RoutedEventArgs e)
{
Items[1].IsSelected = true;
}
}
}
It's because the ItemContainerStyle is applied only when the ComboBoxItems are generated (i.e. when you open the dropdown).
To work around this, you create another property called SelectedItem and bind the Combobox's SelectedValue to it.
Long explanation and example here
Because the binding is set on UpdateSourceTrigger=LostFocus by default, you would have to change it to PropertyChanged to get the result you want.
Like this:
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"Value="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</Style>
When using a Model as the DataContext for a WPF Window, the Controls may not initially behave as you'd expect. Essentially, some properties/events never get set/called until after the Window been initialized. The work-around in this case is to setup the binding in the Window's Loaded event.
Disclaimer: I have not tested this with the OP's specific scenario, but this is the behavior and work-around I've encountered in the past.

Categories