【问题标题】:how to open a other window in C# MVVM?如何在 C# MVVM 中打开另一个窗口?
【发布时间】:2014-07-02 11:09:41
【问题描述】:

我有一个简单的问题: 在我的应用程序中,我有一个“主视图”/主窗口。 用户由此可以控制数据库查询、搜索等所有操作。

但我需要在单击菜单按钮时打开一个属性窗口。

所以我将它绑定到我的 SearchWindowCommand

private ICommand searchwindowcommand;

       public ICommand SearchWindowCommand
       {
           get
           {
               if (searchwindowcommand == null)
               {
                   searchwindowcommand = new RelayCommand(p => ExcecuteSearchwindowcommand());
               }
               return searchwindowcommand;
           }
       }

       public void ExcecuteSearchwindowcommand()
       {


       }

通常我会这样打开它:(我的应用程序工作正常,但我没有使用 mvvm,现在我必须重做应用程序并找出 mvvm :))

 public void auswahl_click(object sender, RoutedEventArgs e)
        {
            Einstellungen suchwindow = new Einstellungen();
            app_config_load(suchwindow);
            suchwindow.Show();

        }

我必须在我的 Executecommand 中写入什么才能显示另一个窗口? 我应该为另一个视图创建一个新的视图模型吗? (我猜是的?)

编辑: 我的启动应用代码:

 public partial class MainWindow : Window

{
    public MainWindow()
    {
        InitializeComponent();
        SetupBindings();
    }

    private void SetupBindings()
    {
        pViewModelList viewModel = new pViewModelList();

        personlistview.DataContext = viewModel;
    }

}

【问题讨论】:

    标签: c# wpf mvvm


    【解决方案1】:

    我应该为另一个视图创建一个新的视图模型吗? (我猜是的?)

    是的。

    我必须在我的 Executecommand 中写入什么才能显示另一个 窗户?

    在这种情况下,我通常会在 ViewModel 中引发一个事件。视图为这个事件附加了一个处理程序,实际的ShowDialog()在这个处​​理程序中执行。

    这里有一些伪代码说明了这个想法:

    // ViewModel
    public class MainViewModel
    {
        public event EventHandler<EventArgs<UpdateViewModel>> UpdateRequested;
    
        private void ExecuteUpdate()
        {
            if (this.UpdateRequested != null)
            {
                var childVm = new UpdateViewModel(/* parameters related to the object being updated */);
                this.UpdateRequested(this, new EventArgs<UpdateViewModel>(childVm));
            }
        }
    }
    
    // View
    public class MainView
    {
        public MainView()
        {
            var vm = new MainViewModel();
            this.DataContext = vm;
    
            vm.UpdateRequested += (sender, e) =>
            {
                var updateView = new UpdateView();
                updateView.DataContext = e.Data;    // Gets the instance of the viewModel here
    
                updateView.ShowDialog();
            };
        }
    }
    

    【讨论】:

    • 我同意这个事件,但我认为新的 ViewModel 不是强制性的。这取决于新窗口的用途。
    • 嗨!问题是,我无法引用 startapplikation,因为 startapplikation 已经引用了我的视图模型(对于上下文?!)现在我不知道如何制作另一个窗口类的实例以使用 ShowDialog() 编辑: 目的只是在appconfig中设置一些东西,没什么特别的。
    • @Xaruth 实际上这不是强制性的。但如果视图需要绑定,我个人总是为每个视图创建一个 viewModel。只是个人偏好,因此阅读代码的人更清楚地将视图及其视图模型关联起来
    • @user3793935 MVVM 的主要规则是:视图引用了视图模型,但视图模型不引用视图。我不知道您的 startapplikation (可能在问题中提供更多代码),但无论如何 viewModel 不得引用视图。
    • 呃,对不起,我有点困惑。我在问题中提供了 startapplikation 代码(它只是数据绑定......我想我可以对另一个窗口做同样的事情,然后用我的 ExcecuteCommand 以某种方式调用它)
    【解决方案2】:

    实际上,不必为每个视图创建新的视图模型。但是您应该根据自己的情况创建一个新的。要从您的视图模型中打开一个新窗口,请使用中介模式,例如 MVVM Light 的 Messenger 服务。

    在您的视图模型中:

    Messenger.Default.Send(ViewName.PropertyWindow);
    

    MainWindow.cs

    Messenger.Default.Register<ViewName>(this, ShowPropertyWindow);
    
    private void ShowPropertyWindow(ViewName view)
    {
      if(view == ViewName.PropertyWindow)
      {
        var propertyWindow = new PropertyWindow();
        propertyWindow.DataContext = new PropertyWindowViewModel();
        propertyWindow.Show();
      }
    }
    

    【讨论】:

      【解决方案3】:

      我目前有类似的需求,用户希望在另一个窗口中打开报告。

      我已经通过在 Window 类的实际实例周围使用“包装器”类来完成此操作(在 MVVM 中),重要的是,包装器构造函数被传递给 Window 将要呈现的 ViewModel 的接口。包装器具有用于隐藏实际 Window 实现类的“显示”、“关闭”等方法。

      包装类的实例由服务类创建和跟踪,该服务类知道所有打开的窗口,并且能够在应用关闭或用户选择从另一个 ViewModel 关闭它们时关闭所有窗口。

      希望这是有道理的!

      查看类:

       public partial class TearOffWindow : MetroWindow
          {
              public TearOffWindow()
              {
                  InitializeComponent();
                  SourceInitialized += (s, a) => this.HideMinimizeButtons();
              }
          }
      

      包装器实现:

      public sealed class TearOffWindowWrapper : IWindowWrapper<TearOffWindow>
          {
              private readonly TearOffWindow _window;
      
              public TearOffWindowWrapper(ITearOffViewModel viewModel)
              {
                  _window = new TearOffWindow { DataContext = viewModel };
              }
      
              public event EventHandler Closed;
      
              public bool IsVisible
              {
                  get
                  {
                      return _window.Visibility == Visibility.Visible;
                  }
      
                  set
                  {
                      _window.Visibility = value ? Visibility.Visible : Visibility.Hidden;
                  }
              }
      
              public void Close()
              {
                  _window.Close();
              }
      
              public void Show()
              {
                  if (!_window.IsVisible)
                  {
                      _window.Closed += WindowOnClosed;
      
                      _window.Show();
                      _window.Activate();
                  }
              }
      
              public void Activate()
              {
                  if (_window.IsVisible)
                  {
                      _window.Activate();
                  }
              }
      
              private void WindowOnClosed(object sender, EventArgs eventArgs)
              {
                  _window.Closed -= WindowOnClosed;
      
                  var closed = Closed;
                  if (closed != null)
                  {
                      closed(this, EventArgs.Empty);
                  }
              }
          }
      

      如您所见,我传入了一个名为 ITearOffViewModel 的接口,其中包含窗口的标题和内容,它们以标准 WPF\MVVM 方式绑定到视图的 DataTemplate。

      public interface ITearOffViewModel : IViewModel
          {
              string Title { get; }
      
              IViewModel Content { get; }
          }
      

      【讨论】:

      • 辉,开窗的代码就这么多;(我试试看,希望能成功:)
      【解决方案4】:

      我应该为另一个视图创建一个新的视图模型吗? (我猜是的?)

      这取决于您的 Viewmodel 以及您尝试用它做什么

      假设您显示了一个List&lt;Person&gt;,以便用户可以在哪里选择一个人并编辑此人。当然,您需要一个用于此模型的 VM,但如果它是 List&lt;PersonVM&gt;,则无需创建新的 PersonVM 仅用于编辑,因为您的 VM 应该能够处理此类事情。

      如何显示另一个窗口?

      这只是一个简单的例子,它肯定有点违反损失耦合规则,因为它知道有视图但它不知道 PersonV,因为 PersonV 是一个用户控件

         public void ExcecuteSearchwindowcommand()
         {
             var vm = //if you need to select a VM or create a new VM
      
             var v = new view(vm) // view is a modified Window
      
             v.ShowDialog()
      
            if(vm.Result)// Result will be an property which tells you
            {            // how the user closed the Window e.g. Save,Cancel, ...
            }
            //else or whatever
      
         }
      

      view.cs

          // this are my constructors
      
          // obj is your vm
          public DetailV(object obj)
          {
              InitializeComponent();
      
              DataContext = obj;
              CPresenter.Content = obj;
      
              initializeCostumComponent();
          }
      
          // dataContext is your vm and 
          // content is an Usercontrol
          public DetailV(object dataContext, object content)
          {
              InitializeComponent();
      
              DataContext = dataContext;
              CPresenter.Content = content;
      
              initializeCostumComponent();
          }
      

      view.xaml

      <Window x:Class="view"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              SizeToContent="WidthAndHeight"
              WindowStartupLocation="CenterScreen"
              Top="150"
              DataContext="{Binding ElementName=CPresenter,Path=Content}"
              Title="{Binding DisplayTitle}" Loaded="Window_Loaded" TabIndex="99999999">
          <ScrollViewer Name="viewer"
                        VerticalScrollBarVisibility="Auto"
                        HorizontalScrollBarVisibility="Auto">
              <ScrollContentPresenter Name="CPresenter" />
          </ScrollViewer>
      </Window>
      

      现在告诉我使用 ResourceDictionary 的特定 vm 的默认视图

      <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      
                           xmlns:vm="clr-namespace:NamespaceViewModel"
                           xmlns:vw="clr-namespace:NamespaceView">
          <DataTemplate DataType="{x:Type vm:PersonVM}">
              <vw:PersonV  />
          </DataTemplate>
      </ResourceDictionary>
      

      如果您现在有一个 AdressVM,只需将其添加到您的 ResourceDictionary 即可使用

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多