Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/268.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 设置窗口顶部+;相对于WPF中的按钮位置向左 序言_C#_Wpf_Xaml_Mvvm - Fatal编程技术网

C# 设置窗口顶部+;相对于WPF中的按钮位置向左 序言

C# 设置窗口顶部+;相对于WPF中的按钮位置向左 序言,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,关于StackOverflow这个主题,已经有几十个类似的问题,我浏览了很多次,没有找到合适的答案,这将适用于我的问题 任务 我有一个MVVM模式的WPF窗口,它有很多打开其他窗口的按钮。我希望我的大多数窗口都与我的按钮相关(我在主窗口的右上角有一个工具栏,希望大多数较小的窗口都显示在按钮的正下方),或者至少与主窗口在同一屏幕上 问题 起初,我认为这没什么大不了的,谷歌上有很多关于这个话题的博客和问题,但它们都不适合我的项目 我使用的是MVVM模式,这意味着: 我不能在我的按钮上使用鼠标.Ge

关于StackOverflow这个主题,已经有几十个类似的问题,我浏览了很多次,没有找到合适的答案,这将适用于我的问题

任务 我有一个MVVM模式的WPF窗口,它有很多打开其他窗口的按钮。我希望我的大多数窗口都与我的按钮相关(我在主窗口的右上角有一个工具栏,希望大多数较小的窗口都显示在按钮的正下方),或者至少与主窗口在同一屏幕上

问题 起初,我认为这没什么大不了的,谷歌上有很多关于这个话题的博客和问题,但它们都不适合我的项目

我使用的是MVVM模式,这意味着:

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

  • 除了按钮之外,我还在我的主窗口中托管了一个带有超链接的UserControl,它可以打开其他窗口,这些窗口应该与超链接相关
研究
  • 一个问题有很多不同的答案,但没有一个对我有用

  • 因为我的ViewModel不知道XAML元素,所以我不能像建议的那样简单地通过点符号进行访问

  • 我的ViewModel不知道工作区域,因此我甚至无法使我的窗口显示在与演示的主窗口相同的屏幕上
  • 与大多数其他答案一样,它似乎在ViewModel中不起作用
问题 我已经在一个问题上花了相当长的时间,这个问题一开始看起来很琐碎。由于到目前为止我看到的大多数问题似乎都针对没有MVVM的窗口,在ViewModel中,将窗口位置设置为鼠标坐标或单击按钮坐标的正确方法是什么

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


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

下面是一个示例,说明如何将参数传递给Vm中的命令:

窗口类:

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);
    }
}
虚拟机类:

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);
    }
}
公共类RelayCommand:ICommand
{
私有只读操作\u执行;
私有只读功能可执行;
公共RelayCommand(执行操作,Func canExecute=null)
{
_execute=execute??抛出新的ArgumentNullException(nameof(execute));
_canExecute=canExecute;
}
公共布尔CanExecute(对象参数)
{
return _canExecute==null | | u canExecute.Invoke();
}
公共事件事件处理程序CanExecuteChanged
{
add=>CommandManager.RequerySuggested+=值;
remove=>CommandManager.RequerySuggested-=值;
}
public void Execute(对象参数)
{
_执行(参数);
}
}

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

即使在鼠标按下事件中处理鼠标按下并将参数传递给命令,也没有问题。MVVM仍然是有效的。我已经尝试过MouseDown事件,但它要么覆盖我控件的MouseDown事件,要么正好相反。然而,这场比赛有一半的时间没有成功。如何避免?请显示您在鼠标按下时所做的操作的代码,即使我添加了所需的代码。我的控件没有MouseDown事件,但它们可能包含一些可能使用它的第三方UI元素。10次中有9次你不需要按钮作为控件,如果你保持它们应有的状态,那么处理单击事件就会容易得多。为什么需要将它们设置为
控件
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);
    }
}