Установить верхний и левый края окна относительно положения кнопки в WPF - PullRequest
0 голосов
/ 30 августа 2018

Преамбула

В StackOverflow уже есть десятки похожих вопросов на эту тему, и я много раз просматривал, не найдя подходящего ответа, который подходит для моей проблемы.

Задача

У меня есть окно WPF в шаблоне MVVM, в котором довольно много кнопок, которые открывают другие окна. Я хотел бы, чтобы большинство моих окон отображалось относительно моих кнопок (у меня есть панель инструментов в верхнем правом углу моего главного окна и я хочу, чтобы большинство меньших окон появлялись прямо под моими кнопками), или, по крайней мере, на тех же самых экран как мое главное окно.

Проблема

Сначала я подумал, что это не так уж важно, и в Google было много блогов и вопросов по этой теме, но все они не будут работать в моем проекте.

Я использую шаблон MVVM, что означает:

  • Я не могу использовать Mouse.GetPosition(ButtonName) на моих кнопках, поскольку ViewModel не знает их имен
  • Я не могу использовать Mouse.GetPosition(sender) в Click-Event, так как большинство кнопок используют команды.
  • Я также, очевидно, не могу использовать PointToScreen в коде моего представления, так как это вызовет исключение (этот визуальный объект не связан с \ "PresentationSource \")
  • Я мог бы использовать Mouse.GetPosition(this) для события MouseMove в коде моего представления позади и передать его в мою модель представления, которая обновит свойство, которое я могу использовать в своих командах при создании окна , но мне не нравится идея постоянного обновления свойства. Также без PointToScreen я не могу установить точку относительно моего экрана.
  • Я не могу использовать какие-либо ссылки на WinForms, так как это вызовет конфликты в моем текущем проекте

  • Дополнительно к кнопкам, я также размещаю UserControl с гиперссылками в моем главном окне, которое открывает дополнительные окна, которые должны быть связаны с гиперссылками.

Исследования

  • есть довольно много разных ответов на вопрос здесь , но ни один из них не помог мне.

  • Поскольку моя ViewModel не знает элементов XAML, я не могу просто получить доступ к точечной нотации, как это предлагается здесь

  • Моя ViewModel не знает WorkingArea, поэтому я даже не смог отобразить свое окно на том же экране, что и мое MainWindow, как показано здесь
  • Как и большинство других ответов, этот кажется, что он не будет работать в 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 голосов
/ 30 августа 2018

Вот пример того, как вы можете передать параметр в Command в вашем 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);
    }
}

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

При таком подходе вы потеряете функциональность CanExecute команды, но сделаете это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...