如何在WPF中实例化一个子窗口,该窗口从主窗口获取参数,但尊重MVVM

时间:2018-02-14 12:59:11

标签: c# wpf xaml mvvm

我已经实现了违反MVVM模式的东西,我想知道是否有MVVM方法可以做到这一点。

我有一个Window MainWindow,它的DataContext绑定到一个名为ViewModel的类,它实现了INotifyPropertyChanged

我还实现了一个窗口ChildWindow,当使用RelayCommand单击按钮时,该窗口以“对话框”样式显示。 ChildWindow的DataContext也绑定到ViewModel。此窗口用于填充新列表项的详细信息。我将视图作为CommandParameter传递给ViewModel,以便ChildWindowMainWindow相比可以居中。这不是MVVM,我想改变它。

首先,我以非MVVM的方式实现了这个: 以下是MainWindow中按钮的XAML打开ChildWindow

 <Button Name="BtnInsert" Width="50" Margin="10" Command="{Binding OpenChildWindowCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">Add</Button>

这是我对ChildWindow的简化XAML:

<Window x:Class="HWE_Einteilen_Prototype.View.ListItemWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HWE_Einteilen_Prototype.View"
        mc:Ignorable="d"
        Title="test" Height="400" Width="400">
    <TextBox Width="50" Text="{Binding CurrentListItem.Id}" ></TextBox>
</Window>

这是我的(简化)ViewModel类:

public class ViewModel : INotifyPropertyChanged
{
    private DataContext _ctx;
    private ListItem _currentListItem;
    private ObservableCollection<listItem> _listItems;

    private ListItemWindow _listItemWindow;
    private ICommand _openListItemWindowCommand;

    public event PropertyChangedEventHandler PropertyChanged;

    public ObservableCollection<ListItem> ListItems
    {
        get { return _listItems; }
        set
        {
            _listItems = value;
            OnPropertyChanged();
        }
    }

    public ListItem CurrentListItem
    {
        get { return _currentListItem; }
        set
        {
            _currentListItem = value;
            OnPropertyChanged();
        }
    }

    public ICommand OpenListItemWindowCommand
    {
        get { return _openListItemWindowCommand; }
        set
        {
            _openListItemWindowCommand = value;
            OnPropertyChanged();
        }
    }

    public ViewModel()
    {
        OpenListItemWindowCommand = new RelayCommand(this.OpenNewListItemWindow, this.CanOpenListItemWindow);
    }

    private void OpenNewListItemWindow(object parameter) 
    {
        CurrentListItem = new listItem(){Id = "testId"};
        _listItemWindow = new StListItemWindow(){DataContext = this};     
        _listItemWindow.Owner = (MainWindow)parameter;                   
        _listItemWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;
        _listItemWindow.Closing += OnStListItemWindowClosing;    
        _listItemWindow.Show();
    }

    private bool CanOpenListItemWindow(object parameter) 
    {
        return true;
    }


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

我尝试了什么: 我已经尝试为打开子窗口的按钮实现一个Behavior(来自system.windows.interactivity),这样它就会创建一个新窗口并完成所有居中和所有者的东西,并且只在命令方法中保留CurrentListItem = new listItem(){Id = "testId"}; 。但是,在这种情况下,绑定到ChildWindow中的CurrentListItem会引发异常。

MainWindow按钮的XAML代码:

    <Button Name="BtnInsert" Width="50" Margin="10" Command="{Binding OpenListItemWindowCommand}" Content="Add">
        <i:Interaction.Behaviors>
            <behaviors:BehButtonNewWindow></behaviors:BehButtonNewWindow>
        </i:Interaction.Behaviors>
    </Button>

行为代码:

class BehButtonNewWindow : Behavior<Button>
{
    private StListItemWindow _ListItemWindow;
    protected override void OnAttached()
    {
        AssociatedObject.Click += OnClickHandler;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.Click -= OnClickHandler;
    }
    private void OnClickHandler(object sender, RoutedEventArgs routedEventArgs)
    {
        if (sender is Button button)
        {
            var win = Window.GetWindow(button);

            if (win != null)
            {
                _ListItemWindow = new ListItemWindow
                {
                    DataContext = win.DataContext,
                    Owner = win,
                    WindowStartupLocation = WindowStartupLocation.CenterOwner
                };

                _ListItemWindow.Show();
            }

        }
    }
}

ViewModel的命令执行方法:

    private void OpenNewStListItemWindow(object parameter) 
    {
        CurrentListItem = new ListItem(){Id = "testId"}; 
}

我做错了什么?

1 个答案:

答案 0 :(得分:0)

这个答案归功于威尔(见评论)

处理窗口时:

  

打开一个窗口是一个UI问题。只需处理代码隐藏中的按钮单击,构建一个新窗口并将当前VM粘贴在其中。 MVVM!=没有代码隐藏。

关于处理vm代码:

  

[...]如果您的意思是底部的最后一点代码,请将其公开并让窗口在打开新窗口之前调用它。知道您的视图模型,UI非常精细。它们旨在显示其状态并绑定到其属性。

感谢您的帮助!