如何在c# MVVM中打开其他窗口

本文关键字:其他 窗口 MVVM | 更新日期: 2023-09-27 18:11:02

我有一个简单的问题:在我的应用程序中,我有一个"主视图"/主窗口。从这个用户可以控制所有的事情,如数据库查询,搜索等。

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

所以我把它绑定到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();
        }

我要写什么到我的执行命令显示其他窗口?我应该为另一个视图创建一个新的视图模型吗?(i guess yes?)

编辑:我的Startapplikationcode:

 public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        SetupBindings();
    }
    private void SetupBindings()
    {
        pViewModelList viewModel = new pViewModelList();
        personlistview.DataContext = viewModel;
    }
}

如何在c# MVVM中打开其他窗口

我应该为另一个视图创建一个新的视图模型吗?(i guess yes?)

是的。

我要写什么到我的执行命令显示另一个窗户吗?

在这种情况下,我通常做的是,在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();
        };
    }
}

实际上并不需要为每个视图创建新的视图模型。但是您应该在您的案例中创建一个新的。要从视图模型打开一个新窗口,请使用中介模式,如MVVM Light的Messenger服务。

在你的ViewModel中:

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

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

我已经做到了这一点(在MVVM)通过使用一个'包装'类周围的窗口类的实际实例,重要的包装构造器传递一个接口到ViewModel窗口将要渲染。包装器有'Show', 'Close'等方法来隐藏实际的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; }
    }

我应该为另一个视图创建一个新的视图模型吗?(i guess yes?)

这取决于你的视图模型和你想用它做什么

假设您显示了一个List<Person>,因此用户可以在其中选择一个Person并编辑这个Person。当然,你需要一个虚拟机为这个模型,但如果它是一个List<PersonVM>,就不需要创建一个新的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>

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

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

如果你现在有了一个addressvm只要把它添加到你的ResourceDictionary中就可以了