【发布时间】:2010-08-06 15:54:07
【问题描述】:
这对 MVVM 模式来说可能有点矫枉过正,但对我来说是新的,我有兴趣看看它是否可能。
如果我附加到窗口的 MouseMove 事件并执行 DragMove,我可以移动一个无边框的窗口。我可以通过 MVVM 中的其他方法来实现这一点,还是应该接受将此代码添加到 Window 的代码隐藏中?
【问题讨论】:
这对 MVVM 模式来说可能有点矫枉过正,但对我来说是新的,我有兴趣看看它是否可能。
如果我附加到窗口的 MouseMove 事件并执行 DragMove,我可以移动一个无边框的窗口。我可以通过 MVVM 中的其他方法来实现这一点,还是应该接受将此代码添加到 Window 的代码隐藏中?
【问题讨论】:
这是纯 UI 逻辑,不属于 ViewModel。你不想把它放在你的代码隐藏中的唯一原因是为了重复使用,这可以通过自定义 Window 派生控件更好地解决。
【讨论】:
我个人认为任何使用 MVVM 的解决方案都不会让这段代码变得更好。此外,这通常与视图相关,与您显示的数据没有任何关系。
【讨论】:
恕我直言,除非这会影响您的数据(也称为模型),否则它是视图代码,应该在视图的代码隐藏中,而不是在模型中。
【讨论】:
我会真正回答你的问题。答案是肯定的。我正在使用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 中不推荐)。
【讨论】:
我知道我问这个问题有点晚了,但这是我已经使用了一段时间了,它就像一个魅力。
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());
}
希望这会有所帮助!
【讨论】:
我知道这是一个老问题。但是我准备了另一个简单的实现。使用以下行为使窗口可移动:
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>
【讨论】:
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();
}
享受吧!)
【讨论】: