StackOverflow上有很多关于这个主题的类似问题,我浏览了很多而没有找到合适的答案,这将适用于我的问题。
我有一个MVVM模式的WPF窗口,它有很多按钮打开其他窗口。我希望我的大多数窗口都与我的按钮相关(我的MainWindow右上角有一个工具栏,希望大多数小窗口出现在我的按钮下方),或者至少在同一个窗口下方屏幕为我的MainWindow。
起初,我认为这不是一个大问题,谷歌有很多关于这个主题的博客和问题,但所有这些都不适用于我的项目。
我正在使用MVVM模式,这意味着:
Mouse.GetPosition(ButtonName)
,因为ViewModel不知道他们的名字Mouse.GetPosition(sender)
,因为大多数Buttons都使用控件。Mouse.GetPosition(this)
并将其交给我的ViewModel,它将更新一个Property,我可以在创建窗口时在我的命令中使用它,但我不喜欢有这个想法永久更新房产。也没有PointToScreen我无法设置与我的屏幕相关的点。我花了很长时间在一个问题上,起初看起来似乎微不足道。由于我目前看到的大多数问题似乎都是针对没有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()目前是空的。我确实在第一个括号设置了一个断点。只有在我的窗口中左键单击时才会到达断点,而不是在其中一个托管控件中。
下面是一个如何在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();
}
}
和Relay类,如果你还没有实现:
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功能,但将完成工作。