[英]Pass SelectedItem to the ViewModel of the UserControl inside DataTemplate
I have this really simple MVVM code made with Prism:我有这个用 Prism 制作的非常简单的 MVVM 代码:
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)?如何将 Model 对象(Person 或 Company)从 ListBox 的 SelectedItem (IContact) 传递到与 DataTemplateSelector(PersonView 或 CompanyView)返回的视图匹配的两个 ViewModel(PersonViewModel 或 CompanyViewModel)之一?
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:我有这些 ViewModel 类:
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;
}
}
}
Do not use view first (aka ViewModelLocator
) here.不要在这里首先使用视图(又名
ViewModelLocator
)。 Go view model first.先去查看模型。
Long version:长版:
Make Contacts
(the item source of the list box) contain view models.使
Contacts
(列表框的项目源)包含视图模型。 Then directly bind SelectedItem
to the content control.然后直接将
SelectedItem
绑定到内容控件上。
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.您甚至不需要选择器,只需在数据模板上设置
DataType
。
When you already have the item at hand that you want to show (ie its view model), just bind and show that one.当您手头已有要显示的项目(即其视图模型)时,只需绑定并显示该项目。 If you want to navigate to a screen in your app (eg login dialog), use the
ViewModelLocator
.如果要导航到应用程序中的屏幕(例如登录对话框),请使用
ViewModelLocator
。 It basically is a workaround for not having the view model ready.它基本上是没有准备好视图模型的解决方法。
This answer is just because Sagar Panwala asked how I did it...这个答案只是因为 Sagar Panwala 问我是怎么做到的...
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: BindableBase
视图模型:
public Dictionary<string, Dictionary<string, PositioningModuleSetting>>? SelectedSettings;
The PositioningModuleSetting
class: PositioningModuleSetting
类:
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: DataTemplateSelector
类:
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
视图:
<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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.