【问题标题】:Multiple Views for a single ViewModel单个 ViewModel 的多个视图
【发布时间】:2016-02-26 09:26:54
【问题描述】:

问题


我的WPF Window 中有许多按钮,单击它们时需要更改Window 上的视图,但保持不变ViewModel。昨天我尝试为此使用ControlTemplate,但人们提到我最好使用DataTemplate.

我需要通过ViewModel 进行绑定,并且我需要检查用户是否可以访问视图。

代码


这是我开始写的一些代码,但我觉得它不正确。

这是我在Window.Resources 的视图中定义的DataTemplate

    <DataTemplate x:Key="panel1">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="7*"/>
                <ColumnDefinition Width="110*"/>
                <ColumnDefinition Width="190*"/>
                <ColumnDefinition Width="110*"/>
                <ColumnDefinition Width="202*"/>
                <ColumnDefinition Width="109*"/>
                <ColumnDefinition Width="7*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="74*"/>
                <RowDefinition Height="50*"/>
                <RowDefinition Height="12*"/>
                <RowDefinition Height="39*"/>
                <RowDefinition Height="11*"/>
                <RowDefinition Height="38*"/>
                <RowDefinition Height="5*"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Column="2" Grid.ColumnSpan="3" Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
                <Label Content="Video Set:" Foreground="#e37e6e" Grid.Column="2" Grid.ColumnSpan="3" VerticalAlignment="Center" FontSize="22" HorizontalAlignment="Center"/>
                <Image Source="{Binding VideoSet}" Height="25" Width="25" HorizontalAlignment="Center" VerticalAlignment="Center"/>
            </StackPanel>

            <TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
            <Button Style="{StaticResource loginButton}" Command="{Binding ScreenBack}" Foreground="White" Content="Add Video" Grid.Column="3" HorizontalAlignment="Stretch" Grid.Row="3" VerticalAlignment="Stretch" Grid.ColumnSpan="1"></Button>

        </Grid>
    </DataTemplate>

然后我尝试使用ContentPresenter 并绑定到DataTemplate

<ContentPresenter Content="{Binding}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>

现在我希望能够通过ViewModel 将不同的DataTemplates 绑定到ContentPresenter,有人可以帮我解决这个问题吗?

编辑:

我可以通过如下静态资源将ContentPresenter绑定到DataTemplate

<ContentPresenter ContentTemplate="{StaticResource panel1}" Content="{StaticResource panel1}" Grid.Row="3" Grid.RowSpan="1" Grid.ColumnSpan="5"/>

DataTemplate 如下所示:

<DataTemplate x:Key="panel1">

</DataTemplate>

但是我怎样才能从ViewModel 更改ControlPresenter 绑定?

编辑:

这是我的代码周期:

所以这里有两个数据模板:

<DataTemplate DataType="{x:Type local:ViewModelA}">
     <TextBlock Foreground="#e37e6e" FontSize="12" Text="You currently do not have a video set. Please click the button below to add a video. Please note you will not be able to create an On Demand presentation without a media file selected. " Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>

<DataTemplate DataType="{x:Type local:ViewModelB}">
     <TextBlock Foreground="#e37e6e" FontSize="12" Text="NEWWWWWWWWWWYou" Grid.Row="1" Grid.Column="2" TextWrapping="WrapWithOverflow" TextAlignment="Center" Grid.ColumnSpan="3" />
</DataTemplate>

我的 ContentControl:

<ContentControl Content="{Binding SelectedViewModel}" />

我在后面的代码中定义了我的 DataContext:

WizardViewModel _wizardViewModel = new WizardViewModel();
this.DataContext = _wizardViewModel;

在我拥有的 WizardViewModel 中:

namespace Podia2016.ViewModels
{
    public class WizardViewModel : INotifyPropertyChanged
    {
        public object SelectedViewModel { get; set; }

        ViewModelA s = new ViewModelA();
        ViewModelB d = new ViewModelB();

        public WizardViewModel()
        {
            SelectedViewModel = s;
            OnPropertyChanged("SelectedViewModel"); 

        }

        //BC - BINDS TO CHANGE LECTURE.
        public ICommand Next
        {
            get { return new DelegateCommand<object>(Next_Click); }
        }

        private void Next_Click(object obj)
        {
            SelectedViewModel = d;
            OnPropertyChanged("SelectedViewModel");  
        }
    }

public class ViewModelA : INotifyPropertyChanged
{
    //BC - DEFAULT ONPROPERTYCHANGED EVENT.
    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModelA()
    {

    }

    /// <summary>
    /// This is the standard OnPropertyChanged Event Method 
    /// </summary>
    /// <param name="name"></param>
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

public class ViewModelB : INotifyPropertyChanged
{
    //BC - DEFAULT ONPROPERTYCHANGED EVENT.
    public event PropertyChangedEventHandler PropertyChanged;

    public ViewModelB()
    {

    }

    /// <summary>
    /// This is the standard OnPropertyChanged Event Method 
    /// </summary>
    /// <param name="name"></param>
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}
}

【问题讨论】:

  • 您的预期结果是什么?当您尝试使用 ContentPresenter 绑定到 DataTemplate 时发生了什么?
  • @Tyress 我只是在 ContentPresenter 中收到一条消息,说“System.Windows.Controls.ControlTemplate”,但这不是通过 ViewModel,因为我不确定该放什么。
  • @BenClarke 看看我的回答
  • @BenClarke 以及如何将 ViewModel 设置为 DataContext?
  • @Tyress 在后面的代码中,我只是像这样设置 DataContext this.DataContext = _wizardViewModel;

标签: c# wpf xaml mvvm datatemplate


【解决方案1】:

数据模板使用起来要简单得多(与您的尝试相比):

  • 创建子视图模型,为您想要的每个子视图创建一个

// could have base class, may have to implement INotifyPropertyChanged, etc.
public class ViewModelA { }
public class ViewModelB
{
    public string SomeProperty { get; }
}
...

public object SelectedViewModel { get; set; }
  • 定义数据模板

<SomeContainer.Resources>
    <!-- using user control -->
    <DataTemplate DataType="{x:Type local:ViewModelA}">
        <local:UserControlA />
    </DataTemplate>
    <!-- or like this -->
    <DataTemplate DataType="{x:Type local:ViewModelB}">
        <Grid>
            <TextBlock Text="{Binding SomeProperty}" />
            <Button .../>
            ...
        </Grid>
    </DataTemplate>
  • 将内容控件绑定到选择子视图模型的属性

<ContentControl Content="{Binding SelectedViewModel}" />
  • 通过更改选定的子视图模型来控制要显示的内容:

var a = new ViewModelA();
var b = new ViewModelB() { SomeProperty = "Test" };

// display a - will display UserControlA content in ContentControl
SelectedViewModel = a;
OnPropertyChanged(nameof(SelectedViewModel));

// display b - will display text and button in ContentControl
SelectedViewModel = b;
OnPropertyChanged(nameof(SelectedViewModel));

【讨论】:

  • 感谢您的回答。在您的第二个代码 sn-p 中,我收到错误“未定义本地”
  • 您应该在窗口/用户控件 xaml 中自动定义 local。如果你不这样做,那么只需添加它,例如`
  • 所以代码运行但DataTemplate's 没有显示。 ViewModelAViewModelB类需要实现INotifyPropertyChanged吗?
  • 我添加了 PropertyChanged 仍然没有显示任何内容?
  • 通常他们必须实现INotifyPropertyChanged,但是可能有一种情况,当您只是显示静态内容(无绑定)时,则此视图模型不必实现(可以,但不是然后使用)。
【解决方案2】:

DataTemplate 存储为ViewModel 的属性。从 ResourceDictionary 访问 DataTemplate 以存储在您的属性中。

绑定&lt;ContentPresenter Content="{Binding}" ContentTemplate="{Binding template1}" .../&gt;

如何从代码中访问 ResourceDictionary:

如果您的 WPF 项目中有一个用于定义资源的 ResourceDictionary,您可以使用如下代码创建它的实例:

ResourceDictionary res = Application.LoadComponent(
 new Uri("/WpfApplication1;component/MyDataTemplateCollection.xaml", 
 UriKind.RelativeOrAbsolute)) as ResourceDictionary;

其中 WpfApplication1 是您的程序集的名称,而 MyDataTemplateCollection.xaml 是您的 ResourceDictionary 的名称。

另一种方法是使用资源字典的代码隐藏。

将 x:Class 添加到您的 ResourceDictionary:

添加类 MyDataTemplateCollection.xaml.cs 作为 ResourceDictionary 的代码隐藏。

类后面的代码如下所示:

partial class MyDataTemplateCollection: ResourceDictionary
{
   public MyDataTemplateCollection()
   {
      InitializeComponent();
   }     
}

用法:

ResourceDictionary res = new MyDataTemplateCollection();

【讨论】:

  • 最好只存储 DataTemplate 键并将转换器从名称写入 DataTemplate
  • @AnjumSkhan 我以前从未这样做过,并且不确定如何实施您的两个建议,即将 DataTemplate 存储为 ViewModel 的属性等...
  • @BenClarke 您的 ViewModel 类不能包含 DataTemplate,那么您将如何加载它?
  • @AnjumSKhan 所以 ViewModel 不能绑定到 DataTemplate?如果是这样,这甚至可能吗?
  • 100% 可能。在 VM 中创建类型为 DataTemplate 的属性。那么这个DataTemplate 需要有一些价值。通过从 ResourceDictionary 加载该值。
猜你喜欢
  • 2012-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-17
  • 2020-01-14
  • 1970-01-01
  • 1970-01-01
  • 2023-03-11
相关资源
最近更新 更多