Reputation: 2683
I have this really simple MVVM code made with Prism:
How can I pass the Model object (either Person or Company) from the ListBox's SelectedItem (IContact) to one of the two ViewModels (either PersonViewModel or CompanyViewModel) that match the View returned by the DataTemplateSelector (either PersonView or CompanyView)?
Thank you!
There is a lot of code, but it is really simple:
I have these Model classes:
public interface IContact
{
string Address { get; set; }
}
public class Person : IContact
{
public string Address { get; set; }
}
public class Company : IContact
{
public string Address { get; set; }
}
I have these ViewModel classes:
public class ContactViewModel : Prism.Mvvm.BindableBase
{
private ObservableCollection<IContact> _contacts = new ObservableCollection<IContact>();
public ObservableCollection<IContact> Contacts
{
get { return _contacts; }
set { SetProperty(ref _contacts, value); }
}
}
public class PersonViewModel : Prism.Mvvm.BindableBase
{
private Person _person; // I want to set this from the ListBox's SelectedItem
public Person Person
{
get { return _person; }
set { SetProperty(ref _person, value); }
}
}
public class CompanyViewModel : Prism.Mvvm.BindableBase
{
private Company _company; // I want to set this from the ListBox's SelectedItem
public Company Company
{
get { return _company; }
set { SetProperty(ref _company, value); }
}
}
I have these View classes:
<UserControl x:Class="ContactView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<UserControl.Resources>
<DataTemplate x:Key="PersonDataTemplate">
<local:PersonView>
// How can I pass the SelectedItem to the ViewModel of this UserControl?
</local:PersonView>
</DataTemplate>
<DataTemplate x:Key="CompanyDataTemplate">
<local:CompanyView>
// How can I pass the SelectedItem to the ViewModel of this UserControl?
</local:CompanyView>
</DataTemplate>
<dataTemplateSelectors:contactDataTemplateSelector x:Key="templateSelector"
PersonDataTemplate="{StaticResource PersonDataTemplate}"
CompanyDataTemplate="{StaticResource CompanyDataTemplate}"/>
</UserControl.Resources>
<Grid>
// RowDefinitions here
<ListBox ItemsSource="{Binding Contacts}" x:Name="myListBox">
// ItemTemplate here
</ListBox>
<ContentControl Grid.Row="1"
Content="{Binding ElementName=myListBox, Path=SelectedItem}"
ContentTemplateSelector="{StaticResource templateSelector}" />
</Grid>
</UserControl>
Person:
<UserControl x:Class="PersonView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Person.Address}" />
</Grid>
</UserControl>
Company:
<UserControl x:Class="CompanyView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Company.Address}" />
</Grid>
</UserControl>
And this:
public class ContactDataTemplateSelector : DataTemplateSelector
{
public DataTemplate PersonDataTemplate { get; set; }
public DataTemplate CompanyDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is Person)
{
return PersonDataTemplate;
}
if (item is Company)
{
return CompanyDataTemplate;
}
}
}
Upvotes: 1
Views: 751
Reputation: 2683
This answer is just because Sagar Panwala asked how I did it...
In the end I did not do it exactly the way I imagined at first.
I did it a little bit different:
The BindableBase
ViewModel:
public Dictionary<string, Dictionary<string, PositioningModuleSetting>>? SelectedSettings;
The PositioningModuleSetting
class:
public class PositioningModuleSetting
{
public string Section { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public dynamic value = null!;
public string description = string.Empty;
public PositioningModuleRestart restart;
public Action<PositioningModuleSetting>? OnSettingChanged { get; set; }
public bool BoolValue
{
get { return value; }
set { this.value = value; OnSettingChanged?.Invoke(this); }
}
public double DoubleValue
{
get { return value; }
set { this.value = value; OnSettingChanged?.Invoke(this); }
}
public long LongValue
{
get { return value; }
set { this.value = value; OnSettingChanged?.Invoke(this); }
}
public string StringValue
{
get { return value; }
set { this.value = value; OnSettingChanged?.Invoke(this); }
}
public object ObjectValue
{
get { return value; }
set { this.value = value; OnSettingChanged?.Invoke(this); }
}
public void Initialize(string section, string name, Action<PositioningModuleSetting> onSettingChanged)
{
Section = section;
Name = name;
OnSettingChanged = onSettingChanged;
}
}
The DataTemplateSelector
class:
public class SettingsDataTemplateSelector : DataTemplateSelector
{
public DataTemplate? DefaultDataTemplate { get; set; }
public DataTemplate? BoolDataTemplate { get; set; }
public DataTemplate? DoubleDataTemplate { get; set; }
public DataTemplate? LongDataTemplate { get; set; }
public DataTemplate? StringDataTemplate { get; set; }
public override DataTemplate? SelectTemplate(object item, DependencyObject container)
{
if (item is KeyValuePair<string, PositioningModuleSetting> pair)
{
return pair.Value.value switch
{
bool _ => BoolDataTemplate,
double _ => DoubleDataTemplate,
long _ => LongDataTemplate,
string _ => StringDataTemplate,
_ => DefaultDataTemplate
};
}
return DefaultDataTemplate;
}
}
The UserControl
View:
<UserControl.Resources>
<DataTemplate x:Key="DefaultDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock MinWidth="180" Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" FontSize="{StaticResource SmallestFontSize}" />
<TextBox MinWidth="240" Text="{Binding Path=Value.ObjectValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="{StaticResource SmallestFontSize}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="BoolDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock MinWidth="180" Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" FontSize="{StaticResource SmallestFontSize}" />
<CheckBox IsChecked="{Binding Path=Value.BoolValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="DoubleDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock MinWidth="180" Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" FontSize="{StaticResource SmallestFontSize}" />
<TextBox MinWidth="240" Text="{Binding Path=Value.DoubleValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="{StaticResource SmallestFontSize}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="LongDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock MinWidth="180" Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" FontSize="{StaticResource SmallestFontSize}" />
<TextBox MinWidth="240" Text="{Binding Path=Value.LongValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="{StaticResource SmallestFontSize}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="StringDataTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock MinWidth="180" Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" FontSize="{StaticResource SmallestFontSize}" />
<TextBox MinWidth="240" Text="{Binding Path=Value.StringValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontSize="{StaticResource SmallestFontSize}" />
</StackPanel>
</DataTemplate>
<dataTemplateSelectors:SettingsDataTemplateSelector x:Key="templateSelector"
DefaultDataTemplate="{StaticResource DefaultDataTemplate}"
BoolDataTemplate="{StaticResource BoolDataTemplate}"
DoubleDataTemplate="{StaticResource DoubleDataTemplate}"
LongDataTemplate="{StaticResource LongDataTemplate}"
StringDataTemplate="{StaticResource StringDataTemplate}" />
</UserControl.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=SelectedSettings}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Key}" Style="{StaticResource LabelTextBlock}" />
<ItemsControl ItemsSource="{Binding Path=Value}" ItemTemplateSelector="{StaticResource templateSelector}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Upvotes: 0
Reputation: 10873
Do not use view first (a.k.a. ViewModelLocator
) here. Go view model first.
Long version:
Make Contacts
(the item source of the list box) contain view models. Then directly bind SelectedItem
to the content control.
The listbox uses one data template to show the contacts, the content control uses another. You don't even need the selector, just set DataType
on your data templates.
When you already have the item at hand that you want to show (i.e. its view model), just bind and show that one. If you want to navigate to a screen in your app (e.g. login dialog), use the ViewModelLocator
. It basically is a workaround for not having the view model ready.
Upvotes: 2