【问题标题】:Adding to XAML WPF from ViewModel (MVVM)从 ViewModel (MVVM) 添加到 XAML WPF
【发布时间】:2018-08-28 03:10:02
【问题描述】:

首次尝试 MVVM 和 WPF(陡峭的学习曲线)。 在我的 ViewModel 中,我想运行以下代码以将作为 AvalonDock 布局的“layoutDocument”添加到我的 Mainform UI 中。

ViewModel 类:

LayoutDocument layoutDocument = new LayoutDocument { Title = "Plan Layout" };

Window mainWindow = Application.Current.Windows.OfType<Window>().Where(x => x.Name == "MainWindow").FirstOrDefault();
if (mainWindow != null)
{
    mainWindow.mainPanel.Children.Add(layoutDocument);
}

上面的代码给了我以下错误:

“‘Window’不包含‘mainPanel’的定义和‘mainPanel’的扩展方法”。

请注意,在下面的 XAML 中,“LayoutDocumentPane”确实包含名称“mainPanel”。

我尝试将上述代码直接添加到我的 MainForm 视图类中(不包括 Application.Current.Windows.OfType 和 If 语句位),并且只包括: mainPanel.Children.Add(layoutDocument); 它工作正常(当我单击按钮时,在我的 MainForm 中创建了一个新布局)。

但是,由于我想坚持使用 MVVM,这不是一个合适的解决方案。

如何将“layoutDocument”从 ViewModel 添加到 MainWindow?提前致谢。

我的 XAML 的摘录如下所示:

<Window x:Class="LiveExplorer.MainWindow"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LiveExplorer"
             xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
             xmlns:xcad="http://schemas.xceed.com/wpf/xaml/avalondock"
             xmlns:s="clr-namespace:System;assembly=mscorlib"
             xmlns:vm="clr-namespace:WpfApp1.ViewModel">

<Grid> etc etc etc here---

                <xcad:LayoutDocumentPaneGroup>
                    <xcad:LayoutDocumentPane x:Name="mainPanel">
                        <xcad:LayoutDocument ContentId="document1" Title="Document 1" >
                            <Button Content="Document 1 Content" HorizontalAlignment="Center" VerticalAlignment="Center" 
                                    Command="{Binding NewPlanCommand, Source={StaticResource viewModel}}" 
                                    />
                        </xcad:LayoutDocument>
                        <xcad:LayoutDocument ContentId="document2" Title="Document 2">
                            <TextBox Text="Document 2 Content" AcceptsReturn="True"/>
                        </xcad:LayoutDocument>
                    </xcad:LayoutDocumentPane>
                </xcad:LayoutDocumentPaneGroup >

编辑:

虽然接受的答案没有根据 MMVM 回答问题,但它确实纠正了编码错误。

【问题讨论】:

  • 从视图模型向 UI 组件添加子控件不是 MVVM 模式。视图模型不应该知道视图的组件。如果要添加多个视图模型或视图模型的属性,您应该在 xaml 中将数据绑定到集合。

标签: c# wpf xaml mvvm


【解决方案1】:

您尝试实现的内容不遵循 MVVM 模式。您需要注意 3 件事才能开始:

  • 视图模型
  • 初始化绑定到窗口的 ViewModel
  • 在 XAML 中将 ViewModel 绑定到 UI

视图模型:

创建一个将绑定到您的 MainWindow 的视图模型,并在该 MainWindowViewModel 中创建一个可观察的集合,其中包含一个对象列表,其中包含可在 UI 中使用的数据:

public ObservableCollection<LayoutDocumentViewModel> LayoutDocument {get;set;}

确保 MainWindowViewModel 和 LayoutDocumentViewModel 都继承自 INotifyPropertyChanged(Implement Property Change Notification),或者如果您使用 ViewModelBase 中的 MVVMLight(或类似)。

LayoutDocumentViewModel 只是一个 ViewModel,用于存储有关您的布局文档的信息,并且可以绑定到 UI。

public LayoutDocumentViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    [NotifyPropertyChangedInvocator]

    protected virtual void OnPropertyChanged([CallerMemberName] 
        string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private string _name;

    public string Name
    {
      get { return _name; }
      set
      {
          _name = value;
          // Call OnPropertyChanged whenever the property is updated
          OnPropertyChanged("Name");
      }
    }

}

我强烈建议您使用 MVVMLight(或类似的)或将 INotifyPropertyChange 代码放入基类,例如 ViewModelBase。

为了简单起见,在本示例中,我正在初始化 observable 集合并直接在 MainWindowViewModel 中创建几个文档布局对象,但您需要进一步研究并找出适合您初始化的位置和/或创建这些。

公共 MainPageViewModel() { DocumentLayouts = new ObservableCollection(); DocumentLayouts.Add(new DocumentLayout {Name="Layout1"}); DocumentLayouts.Add(new DocumentLayout {Name="Layout2"}); }

以上内容负责创建您的 MainWindowViewModel 和布局文档。

初始化 MainViewModel(并绑定到 MainWindow.xaml)。请注意,这是一种快速而肮脏的入门方式,您应该真正了解 IoC 容器。

<Window.DataContext>
    <local:MainWindowViewModel/>
</Window.DataContext>

最后,绑定你的 ViewModel 和 UI

XAML:

<Grid>
    <ItemsControl ItemsSource="{Binding LayoutDocuments}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <Label Content="{Binding Name}"/>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

注意:只需用您的 LayoutDocument 控件替换 Label 并将其绑定到您在 LayoutDocumentViewModel 中声明的相关元素属性。

希望这有助于您入门。

【讨论】:

  • 感谢您的出色回答。你所说的一切都是有道理的,毫无疑问是正确的方法。老实说,尽管经历了几个示例,但数据绑定和 INotifyChange 等还没有为我点击,所以现在我将使用“mm8”答案,以便我可以继续前进。谢谢
  • 不客气!确实需要一点时间来适应并且说实话,仍然有很多场景我今天仍然在挣扎,但这就是 SO 的用武之地:) - 祝你好运!
【解决方案2】:

这与 MVVM 无关,但为了能够访问 mainPanel,您需要将返回的 Window 转换为 MainWindow

MainWindow mainWindow = Application.Current.Windows.OfType<MainWindow>().FirstOrDefault();
if (mainWindow != null)
{
    mainWindow.mainPanel.Children.Add(layoutDocument);
}

视图模型不应该直接访问任何窗口。这打破了 MVVM 模式。

【讨论】:

    猜你喜欢
    • 2010-11-10
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 2010-12-01
    • 2013-01-02
    • 1970-01-01
    相关资源
    最近更新 更多