将 SelectedItem 传递给 DataTemplate 中 UserControl 的 ViewModel
Pass SelectedItem to the ViewModel of the UserControl inside DataTemplate
我有这个用 Prism 制作的非常简单的 MVVM 代码:
- 2 个模型(个人、公司 - 公共接口 IContact),带有 2 个视图模型 (Prism) 和 2 个视图 (UserControl)
- 1 个 ViewModel(接口 IContact 的集合)和 1 个视图(ListBox 绑定到集合,ContentControl 绑定到 ListBox 的 SelectedItem,DataTemplateSelector returns 基于 SelectedItem 类型的两个 UserControl 之一)
如何将模型对象(Person 或 Company)从 ListBox 的 SelectedItem (IContact) 传递到与 DataTemplateSelector 返回的视图匹配的两个 ViewModel(PersonViewModel 或 CompanyViewModel)之一(PersonView 或 CompanyView)?
谢谢!
代码很多,其实很简单:
我有这些型号 类:
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; }
}
我有这些 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); }
}
}
我有这些视图 类:
<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>
人:
<UserControl x:Class="PersonView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Person.Address}" />
</Grid>
</UserControl>
公司:
<UserControl x:Class="CompanyView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Company.Address}" />
</Grid>
</UserControl>
还有这个:
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;
}
}
}
这里先不要用view(a.k.a.ViewModelLocator
)。先去查看模型。
长版:
使 Contacts
(列表框的项目源)包含视图模型。然后直接绑定SelectedItem
到content控件。
列表框使用一个数据模板来显示联系人,内容控件使用另一个。您甚至不需要选择器,只需在您的数据模板上设置 DataType
。
当您手边已有要显示的项目(即其视图模型)时,只需绑定并显示该项目即可。如果您想导航到应用程序中的屏幕(例如登录对话框),请使用 ViewModelLocator
。它基本上是 not 准备好视图模型的解决方法。
所有功劳都归功于 Haukinger!
这个回答只是因为Sagar Panwala问我是怎么做到的...
最后我并没有完全按照我最初想象的那样去做。
我做的有点不同:
BindableBase
视图模型:
public Dictionary<string, Dictionary<string, PositioningModuleSetting>>? SelectedSettings;
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;
}
}
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;
}
}
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>
我有这个用 Prism 制作的非常简单的 MVVM 代码:
- 2 个模型(个人、公司 - 公共接口 IContact),带有 2 个视图模型 (Prism) 和 2 个视图 (UserControl)
- 1 个 ViewModel(接口 IContact 的集合)和 1 个视图(ListBox 绑定到集合,ContentControl 绑定到 ListBox 的 SelectedItem,DataTemplateSelector returns 基于 SelectedItem 类型的两个 UserControl 之一)
如何将模型对象(Person 或 Company)从 ListBox 的 SelectedItem (IContact) 传递到与 DataTemplateSelector 返回的视图匹配的两个 ViewModel(PersonViewModel 或 CompanyViewModel)之一(PersonView 或 CompanyView)?
谢谢!
代码很多,其实很简单:
我有这些型号 类:
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; }
}
我有这些 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); }
}
}
我有这些视图 类:
<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>
人:
<UserControl x:Class="PersonView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Person.Address}" />
</Grid>
</UserControl>
公司:
<UserControl x:Class="CompanyView"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True" >
<Grid>
<TextBlock Text="{Binding Company.Address}" />
</Grid>
</UserControl>
还有这个:
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;
}
}
}
这里先不要用view(a.k.a.ViewModelLocator
)。先去查看模型。
长版:
使 Contacts
(列表框的项目源)包含视图模型。然后直接绑定SelectedItem
到content控件。
列表框使用一个数据模板来显示联系人,内容控件使用另一个。您甚至不需要选择器,只需在您的数据模板上设置 DataType
。
当您手边已有要显示的项目(即其视图模型)时,只需绑定并显示该项目即可。如果您想导航到应用程序中的屏幕(例如登录对话框),请使用 ViewModelLocator
。它基本上是 not 准备好视图模型的解决方法。
所有功劳都归功于 Haukinger!
这个回答只是因为Sagar Panwala问我是怎么做到的...
最后我并没有完全按照我最初想象的那样去做。
我做的有点不同:
BindableBase
视图模型:
public Dictionary<string, Dictionary<string, PositioningModuleSetting>>? SelectedSettings;
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;
}
}
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;
}
}
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>