开发者

How should I move a WPF Window using MVVM?

This is probably overkill on the MVVM pattern but it's new to me and I'm interested to see if it is possible.

If I attach to the MouseMove event for a Window and do DragMove, I can move a bordeless window. Can I achieve this by some 开发者_如何学运维other method in MVVM or should I just accept adding this code to the Window's codebehind?


This is pure UI logic and doesn't belong in a ViewModel. The only reason you wouldn't want to put this in your code-behind would be for re-use and that is better solved with a custom Window derived control.


Personally I think any solution using MVVM would not make this code any better. Also, this is typically something that's view related and hasn't got anything to do with the data you're displaying.


IMHO, unless this is something that effects your data (aka the Model) then it is View code and should be in the View's code-behind and not in the Model.


I'm going to actually answer your question. The answer is yes. I'm using Cinch to assist me in the event binding and view model creation. The solution uses DragMove, but not as part of the code-behind (which is what I believe you are asking).

Event binding in the 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>

In the 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();
    }
}

Fairly simple, right? Any events that come from the UI contain the View as the sender. So, here, we simply call the method on the view within the event handler in the ViewModel.

The project I'm working on uses no code-behind (even if it is not recommended in MVVM).


I know that I am a little late to the question, but this is what I have been using for sometime now and it works like a charm.

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

and something like this in your viewModel

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

Using the ICommand interface...

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

Configure the commands...

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

Here is the RelayCommand class.

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
}

And some example methods...

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

Hope this helps!


I know it's an old question. However I prepared another simple implementation. Use following behavior to make window moveable:

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 example:

<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>


11 years passed, but maybe someone is still interested in case to drag window using MVVM. This tricky solution is based on window's property "Tag" - almost no one use it but it's time to find out it's strength :) So all you need is System.Windows.Interactivity nuget, no Cinch or events!

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>

Let's find out your current window and move it. In 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();
    }

Enjoy!)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜