将SelectedItem传递到DataTemplate内UserControl的ViewModel

时间:2018-11-27 12:10:42

标签: c# wpf xaml mvvm prism

我有使用Prism制作的非常简单的MVVM代码:

  • 具有2个ViewModels(Prism)和2个Views(UserControl)的2个模型(人员,公司-通用接口IContact)
  • 带有1个视图的1个ViewModel(接口IContact的集合)(绑定到列表的ListBox和绑定到ListBox的SelectedItem的ContentControl,DataTemplateSelector会根据SelectedItem的类型返回两个UserControl之一)

如何将模型对象(人或公司)从列表框的SelectedItem(IContact)传递到与DataTemplateSelector返回的视图相匹配的两个视图模型(人视图或公司视图模型)之一(人视图或CompanyView)?

谢谢!


有很多代码,但是非常简单:

我有以下Model类:

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); }
    }
}

我有以下View类:

<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;
        }
    }
}

2 个答案:

答案 0 :(得分:1)

请勿在此处先使用视图(也称为ViewModelLocator)。首先查看模型。

长版:

使Contacts(列表框的项目来源)包含视图模型。然后直接将SelectedItem绑定到内容控件。

列表框使用一个数据模板显示联系人,内容控件使用另一个。您甚至不需要选择器,只需在数据模板上设置DataType

当您已经准备好要显示的项目(即其视图模型)时,只需绑定并显示该项目即可。如果您想导航到应用程序中的屏幕(例如登录对话框),请使用ViewModelLocator。基本上,这是没有准备好视图模型的解决方法。

答案 1 :(得分:0)

所有功劳归Haukinger!

这个答案仅仅是因为Sagar Panwala问我是怎么做到的...


最后,我并没有完全按照我最初的想象去做。

我做了一些不同:

BindableBase ViewModel:

    public Dictionary<string, Dictionary<string, PositioningModuleSetting>>? SelectedSettings;

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;
    }
}

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;
    }
}

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>