【问题标题】:How should I move a WPF Window using MVVM?我应该如何使用 MVVM 移动 WPF 窗口?
【发布时间】:2010-08-06 15:54:07
【问题描述】:

这对 MVVM 模式来说可能有点矫枉过正,但对我来说是新的,我有兴趣看看它是否可能。

如果我附加到窗口的 MouseMove 事件并执行 DragMove,我可以移动一个无边框的窗口。我可以通过 MVVM 中的其他方法来实现这一点,还是应该接受将此代码添加到 Window 的代码隐藏中?

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:

    这是纯 UI 逻辑,不属于 ViewModel。你不想把它放在你的代码隐藏中的唯一原因是为了重复使用,这可以通过自定义 Window 派生控件更好地解决。

    【讨论】:

      【解决方案2】:

      我个人认为任何使用 MVVM 的解决方案都不会让这段代码变得更好。此外,这通常与视图相关,与您显示的数据没有任何关系。

      【讨论】:

      • 绝对同意这个!
      【解决方案3】:

      恕我直言,除非这会影响您的数据(也称为模型),否则它是视图代码,应该在视图的代码隐藏中,而不是在模型中。

      【讨论】:

        【解决方案4】:

        我会真正回答你的问题。答案是肯定的。我正在使用Cinch 来协助我进行事件绑定和视图模型创建。该解决方案使用 DragMove,但不作为代码隐藏的一部分(我相信您在问这个问题)。

        XAML 中的事件绑定:

        <Window 
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            xmlns:cinchV2="clr-namespace:Cinch;assembly=Cinch.WPF"
            ...>
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="MouseLeftButtonDown">
                    <cinchV2:EventToCommandTrigger Command="{Binding MouseLeftButtonDown}" />
                </i:EventTrigger>
            </i:Interaction.Triggers>
            <Grid>
                ...
            </Grid>
        </Window>
        

        在 ViewModel 中:

        [ExportViewModel("MainViewModel")]
        [PartCreationPolicy(CreationPolicy.NonShared)]
        internal sealed class MainViewModel : ViewModelBase
        {
            public SimpleCommand<object, EventToCommandArgs> MouseLeftButtonDown { get; private set; }
        
            [ImportingConstructor]
            public MainViewModel(IUIVisualizerService uiVisualizerService)
            {
                ...
                MouseLeftButtonDown = new SimpleCommand<object, EventToCommandArgs>(OnMouseLeftButtonDown);
            }
        
            private static void OnMouseLeftButtonDown(EventToCommandArgs e)
            {
                ((Window)e.Sender).DragMove();
            }
        }
        

        相当简单,对吧?来自 UI 的任何事件都包含作为发送者的视图。因此,在这里,我们只需在 ViewModel 的事件处理程序中调用视图上的方法。

        我正在处理的项目不使用代码隐藏(即使在 MVVM 中不推荐)。

        【讨论】:

          【解决方案5】:

          我知道我问这个问题有点晚了,但这是我已经使用了一段时间了,它就像一个魅力。

          DashboardViewModel viewModel;
          public DashboardView()
          {
              InitializeComponent();
              viewModel = new DashboardViewModel();
              viewModel.RequestClose += (s, e) => Application.Current.Dispatcher.Invoke(this.Close);
              viewModel.RequestMinimize += (s, e) => Application.Current.Dispatcher.Invoke(() => { this.WindowState = WindowState.Minimized; });
              DataContext = viewModel;
          }
          

          在你的 viewModel 中有类似的东西

          #region Public Event Handlers
          public event EventHandler<EventArgs> RequestClose;
          public event EventHandler<EventArgs> RequestMinimize;
          #endregion
          

          使用 ICommand 接口...

          #region ICommand Members
          public ICommand CloseCommand { get; private set; }
          public ICommand MinimizeCommand { get; private set; }
          #endregion
          

          配置命令...

          private void SetupCommands()
          {
              CloseCommand = new RelayCommand(CloseApplication);
              MinimizeCommand = new RelayCommand(MinimizeApplication);
          }
          

          这是 RelayCommand 类。

          public class RelayCommand : ICommand
          {
          #region Private Readonly Properties
          private readonly Action<object> executeCommand;
          private readonly Predicate<object> canExecute;
          #endregion
          
          #region Constructors
          public RelayCommand(Action<object> execute) : this(execute, null)
          {
          
          }
          public RelayCommand(Action<object> execute, Predicate<object> canExecute)
          {
              if (execute == null) 
                  throw new ArgumentNullException("execute");
              this.executeCommand = execute; 
              this.canExecute = canExecute;
          }
          #endregion
          
          #region Public ICommand Members
          public bool CanExecute(object parameter)
          {
              return canExecute == null ? true : canExecute(parameter);
          }
          public event EventHandler CanExecuteChanged
          {
              add { CommandManager.RequerySuggested += value; }
              remove { CommandManager.RequerySuggested -= value; }
          }
          public void Execute(object parameter)
          {
              executeCommand(parameter);
          }
          #endregion
          }
          

          还有一些示例方法...

          private void MinimizeApplication(object obj)
          {
              RequestMinimize(this, new EventArgs());
          }
          private void CloseApplication(object obj)
          {
              RequestClose(this, new EventArgs());
          }
          

          希望这会有所帮助!

          【讨论】:

            【解决方案6】:

            我知道这是一个老问题。但是我准备了另一个简单的实现。使用以下行为使窗口可移动:

            public class WindowMoveBehavior : Behavior<Grid>
            {
                protected override void OnAttached()
                {
                    base.OnAttached();
                    AssociatedObject.MouseLeftButtonDown += AssociatedObject_MouseLeftButtonDown;
                }
            
                protected override void OnDetaching()
                {
                    AssociatedObject.MouseLeftButtonDown -= AssociatedObject_MouseLeftButtonDown;
                    base.OnDetaching();
                }
            
                private void AssociatedObject_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
                {
                    Window.GetWindow(AssociatedObject).DragMove();
                }
            }
            

            Xaml 示例:

            <Style x:Key="CustomWindowStyle" TargetType="{x:Type Window}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Window}">
                            <Grid>
                                <i:Interaction.Behaviors>
                                    <behaviors:WindowMoveBehavior/>
                                </i:Interaction.Behaviors>
                 <!-- different controls and content -->
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
            

            【讨论】:

              【解决方案7】:

              11 年过去了,但也许有人仍然对使用 MVVM 拖动窗口感兴趣。这个棘手的解决方案基于窗口的属性“Tag” - 几乎没有人使用它,但现在是时候了解它的力量了:) 所以你只需要 System.Windows.Interactivity nuget,没有 Cinch 或事件!

              Xaml:

              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
              
              <Window ... Tag="{Binding WindowTag}">
              
              <i:Interaction.Triggers>        
                  <i:EventTrigger EventName="MouseLeftButtonDown">
                      <i:InvokeCommandAction Command="{Binding DragMoveWindowCommand}" />
                  </i:EventTrigger>
              </i:Interaction.Triggers>
              

              让我们找出您当前的窗口并移动它。在 ViewModel 中:

              private object _windowTag;
              public object WindowTag
              {
                  get
                  {
                      return _windowTag;
                  }
                  set
                  {
                      _windowTag = value;
                      OnPropertyChanged("WindowTag");
                  }
              }
              
                  private RelayCommand _dragMoveWindowCommand;
                  public RelayCommand DragMoveWindowCommand
                  {
                      get
                      {
                          return _dragMoveWindowCommand ??
                                 (_dragMoveWindowCommand = new RelayCommand(obj =>
                                 {
                                     try
                                     {
                                         var window = WindowService.GetCurrentWindowByTag(WindowTag = 1);
                                         window.DragMove();
                                     }
                                     catch (Exception ex)
                                     {
              
                                     }
                                 }));
                      }
                  }
              
                  public static Window GetCurrentWindowByTag(object tag)
                  {
                      return (from Window w in App.Current.Windows
                       where w.Tag == tag
                       select w)?.FirstOrDefault();
                  }
              

              享受吧!)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-05-27
                • 1970-01-01
                • 1970-01-01
                • 2010-12-29
                • 2013-05-12
                相关资源
                最近更新 更多