参照WPF

时间:2018-08-30 08:59:55

标签: c# wpf xaml mvvm

序言

在StackOverflow上已经有数十个与此主题类似的模拟问题,我浏览了很多次却没有找到合适的答案,这将适用于我的问题。

任务

我有一个MVVM模式的WPF窗口,其中有很多打开其他窗口的按钮。我想让大多数窗口与按钮相关(我在MainWindow的右上角有一个工具栏,并希望大多数较小的窗口出现在按钮的正下方),或者至少在同一按钮上屏幕作为我的MainWindow。

问题

起初,我认为这没什么大不了的,并且在Google上有很多关于此主题的博客和问题,但它们全都不适用于我的项目。

我正在使用MVVM模式,这意味着:

  • 不能在我的按钮上使用Mouse.GetPosition(ButtonName),因为ViewModel不知道它们的名称
  • 不能在点击事件中使用Mouse.GetPosition(sender),因为大多数按钮都使用控件。
  • 我显然也不能在我的视图后面的代码中使用PointToScreen,因为这会导致异常(此可视对象未连接到\“ PresentationSource \”)
  • 可以在我视图的代码后面的MouseMove-Event上使用Mouse.GetPosition(this),并将其交给我的ViewModel,后者将更新一个Property,我可以在命令中使用创建窗口,但我不喜欢必须永久更新属性的想法。同样,如果没有PointToScreen,就无法设置与屏幕有关的点。
  • 不能使用任何WinForms引用,因为这会导致我当前的项目发生冲突

  • 除了按钮之外,我还在MainWindow中托管了一个带有超链接的UserControl,该控件会打开其他与超链接有关的窗口。

研究

  • 问题here的答案有很多,但没有一个对我有用。

  • 由于我的ViewModel不知道XAML元素,因此我不能像建议的here

  • 那样简单地通过点表示法进行访问
  • 我的ViewModel不知道WorkingArea,因此我什至无法使我的窗口出现在与MainWindow相同的屏幕上,如图here
  • 与大多数其他答案一样,this one似乎在ViewModel中不起作用

问题

我花了很多时间解决一个问题,乍一看似乎已经很微不足道了。由于到目前为止我所看过的大多数问题似乎都针对没有MVVM的窗口,因此在ViewModel中将窗口的位置设置为鼠标坐标或单击按钮坐标的正确方法是什么?

编辑: 注释中要求的MouseDownEvent: Xaml:

<Window x:Class="MySampleProject.MainWindow"
        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:MySampleProject"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525"
        MouseDown="Window_MouseDown">

C#:

private void Window_MouseDown(object sender, MouseEventArgs e)
{
    if (m_oDataContext != null)
    {
        m_oDataContext.MouseTest(Mouse.GetPosition(this));
    }
}

oDataContext是我的ViewModel。我的MouseTest()当前为空。我确实在第一个括号中设置了一个断点。仅当在我的窗口中而不是其托管控件之一中单击鼠标左键时才达到断点。

1 个答案:

答案 0 :(得分:0)

以下是如何在Vm中将参数传递给Command的示例:

窗口类:

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext= new MyVm();
    }

    private void BtnWin1_OnClick(object sender, RoutedEventArgs e)
    {
        var dataContext = DataContext as MyVm;
        var relativePoint = ((Button)sender).TransformToAncestor(this).Transform(new Point(0, 0));
        relativePoint.X += this.Left;
        relativePoint.Y += this.Top;
        dataContext?.OpenWindow1Command.Execute(relativePoint);
    }

    private void BtnWin2_OnClick(object sender, RoutedEventArgs e)
    {
        var dataContext = DataContext as MyVm;
        var relativePoint = ((Button)sender).TransformToAncestor(this).Transform(new Point(0, 0));
        relativePoint.X += this.Left;
        relativePoint.Y += this.Top;
        dataContext?.OpenWindow2Command.Execute(relativePoint);
    }
}

VM类:

public class MyVm : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

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

    public ICommand OpenWindow1Command { get; }
    public ICommand OpenWindow2Command { get; }

    public MyVm()
    {
        OpenWindow1Command = new RelayCommand(OpenWindow1Command_Execute);
        OpenWindow2Command = new RelayCommand(OpenWindow2Command_Execute);
    }

    void OpenWindow1Command_Execute(object parameter)
    {
        var point = (Point)parameter;

        var win1 = new Window1{WindowStartupLocation = WindowStartupLocation.Manual, Left = point.X, Top = point.Y};
        win1.Show();
    }

    void OpenWindow2Command_Execute(object parameter)
    {
        var point = (Point)parameter;

        var win2 = new Window2 { WindowStartupLocation = WindowStartupLocation.Manual, Left = point.X, Top = point.Y };
        win2.Show();
    }
} 

还有中继类(如果尚未实现的话):

 public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<bool> _canExecute;

    public RelayCommand(Action<object> execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute.Invoke();
    }

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

使用这种方法,您将失去Command的CanExecute功能,但可以完成工作。