原文:WPF MVVM模式中,通过命令实现窗体拖动、跳转以及显隐控制

在WPF中使用MVVM模式,可以让我们的程序实现界面与功能的分离,方便开发,易于维护。但是,很多初学者会在使用MVVM的过程中遇到一个显而易见且无法回避的问题,那就是不同的窗体之间如何跳转?很多人在介绍MVVM的使用时,都没有明显提到该如何解决这一问题,不知是因为觉得太简单了还是其他原因。

博主根据自己的开发经验,写了一个简单的示例程序,介绍MVVM模式中,如何通过命令来控制窗体的跳转、拖动与显隐控制。

先看效果:

主窗体中只有一个按钮,点击该按钮后,可以打开新的窗。

新窗体可以为自定义样式窗体,鼠标拖动标题框,可以拖动整个窗体,点击关闭按钮,窗体隐藏。

下面是实现操作:

1.定义命令类ActionCommand.

使用MVVM模式的第一步,就是要实现自己的命令类。

public class ActionCommand<T> : ICommand where T : class
{
private Predicate<T> _canExecuteMethod;
private Action<T> _executeMethod; public ActionCommand(Action<T> executeMethod)
{
_canExecuteMethod = null;
_executeMethod = executeMethod;
} public ActionCommand(Action<T> executeMethod, Predicate<T> canExecuteMethod)
{
_canExecuteMethod = canExecuteMethod;
_executeMethod = executeMethod;
} public bool CanExecute(object parameter)
{
return _canExecuteMethod == null ? true : _canExecuteMethod(parameter as T);
} public event EventHandler CanExecuteChanged; public void Execute(object parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter as T);
}
UpdateCanExecute();
} public void UpdateCanExecute()
{
var handls = CanExecuteChanged;
if (handls != null)
{
handls(this, new EventArgs());
}
}
}

2.在App.xaml中定义窗体导航实现代码以及窗体操作命令

/// <summary>
/// App.xaml 的交互逻辑
/// </summary>
public partial class App : Application
{
private static bool _bDebug = true;
public static void MessageBox(string text, string caption)
{
if (_bDebug)
{
System.Windows.MessageBox.Show(text, caption);
} } private static Dictionary<string, Window> _cacheWindow = new Dictionary<string, Window>();
public static Window NavigationToWindow(string wndUri, bool createNew = false, bool cache = true, string cacheKey = null)
{
Window window = null;
string key = string.IsNullOrWhiteSpace(cacheKey) ? wndUri : cacheKey;
if (createNew)
{
window = App.Current.GetType().Assembly.CreateInstance(wndUri) as Window;
if (cache && window != null)
{
if (!_cacheWindow.ContainsKey(key))
{
_cacheWindow.Add(key, window);
}
}
}
else
{
if (_cacheWindow.ContainsKey(key))
{
window = _cacheWindow[key];
}
else
{
window = App.Current.GetType().Assembly.CreateInstance(wndUri) as Window;
if (cache && window != null)
{
_cacheWindow.Add(key, window);
}
}
}
return window;
} /// <summary>
/// 显示窗体命令
/// </summary>
public static ICommand ShowWindowCommand
{
get
{
return new ActionCommand<string>(p =>
{
if (string.IsNullOrWhiteSpace(p))
{
App.MessageBox("参数不能为空!", "[App][ShowWindowCommand]");
return;
}
string[] arrs = p.Split(','); string wndUri = null, cacheKey = null;
bool createNewWnd = false, cacheWnd = true;
try
{
if (arrs.Length > 3)
{
wndUri = arrs[0];
createNewWnd = Convert.ToBoolean(arrs[1]);
cacheWnd = Convert.ToBoolean(arrs[2]);
cacheKey = arrs[3];
}
else if (arrs.Length > 2)
{
wndUri = arrs[0];
createNewWnd = Convert.ToBoolean(arrs[1]);
cacheWnd = Convert.ToBoolean(arrs[2]);
}
else if (arrs.Length > 1)
{
wndUri = arrs[0];
createNewWnd = Convert.ToBoolean(arrs[1]);
}
else
{
wndUri = arrs[0];
}
Window window = NavigationToWindow(wndUri, createNewWnd, cacheWnd, cacheKey);
if (window == null)
{
App.MessageBox("未找到导航窗体" + "[" + wndUri + "]", "[App][ShowWindowCommand]");
return;
}
window.Owner = App.Current.MainWindow;
if (!window.IsVisible)
{
window.Show();
}
else
{
window.Hide();
}
}
catch (Exception ex)
{
App.MessageBox(ex.Message, "[App][ShowWindowCommand]");
} }
);
}
} /// <summary>
/// 隐藏窗体命令
/// </summary>
public static ICommand HideWindowCommand
{
get
{
return new ActionCommand<string>(p =>
{
if (string.IsNullOrWhiteSpace(p))
{
App.MessageBox("参数不能为空!", "[App][HideWindowCommand]");
return;
} Window window = App.NavigationToWindow(p);
if (window != null)
{
window.Hide();
}
}
);
}
} /// <summary>
/// 拖动窗体命令
/// </summary>
public static ICommand DragMoveWindowCommand
{
get
{
return new ActionCommand<string>(p =>
{
if (string.IsNullOrWhiteSpace(p))
{
App.MessageBox("参数不能为空!", "[App][DrawMoveWindowCommand]");
return;
} Window window = App.NavigationToWindow(p);
if (window != null)
{
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
window.DragMove();
}
if (window.WindowState == WindowState.Maximized)
{
window.WindowState = WindowState.Normal;
}
}
}
);
}
}
}

3.在主窗体中使用ShowCommand命令来实现窗体导航。

<Window x:Class="WpfMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfMVVM"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Width="100" Height="40" Content="打开新窗体" Command="{x:Static local:App.ShowWindowCommand}"
CommandParameter="WpfMVVM.View.CustomWindow"/>
</Grid>
</Window>

4.在自定义窗体CustomWindow.xaml中使用命令来实现窗体拖动和显隐控制。

为了使得Grid中的MouseMove事件能够响应命令绑定操作,导入blend中的类库:System.Windows.Interactivity.dll。并在页面中导入xml命名空间:xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"。这样就可以实现WPF中任意事件的命令响应。

<Window x:Class="WpfMVVM.View.CustomWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfMVVM"
Title="CustomWindow" Height="300" Width="300"
WindowStyle="None" AllowsTransparency="True"
ShowInTaskbar="False"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions> <Grid Grid.Row="0" Background="{StaticResource BoardHead}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Text="标题" Style="{StaticResource BoardTitle}"/>
<Button Grid.Column="1" Style="{StaticResource CloseBtn}"
Command="{x:Static local:App.HideWindowCommand}"
CommandParameter="WpfMVVM.View.CustomWindow"/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseMove">
<i:InvokeCommandAction Command="{x:Static local:App.DragMoveWindowCommand}"
CommandParameter="WpfMVVM.View.CustomWindow"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid> <Grid Grid.Row="1" Background="{StaticResource BoardBody}">
<Image Source="/Resource/sun.png" Stretch="Uniform"/>
</Grid> </Grid>
</Window>

通过以上步骤操作,我们便可以实现窗体之间的导航以及自定义窗体的拖动控制以及显隐控制。

完整代码下载:http://download.csdn.net/detail/tianwenxue/9078205

本文原创,转载请注明出处。

WPF MVVM模式中,通过命令实现窗体拖动、跳转以及显隐控制的更多相关文章

  1. WPF自学入门(十一)WPF MVVM模式Command命令 WPF自学入门(十)WPF MVVM简单介绍

    WPF自学入门(十一)WPF MVVM模式Command命令   在WPF自学入门(十)WPF MVVM简单介绍中的示例似乎运行起来没有什么问题,也可以进行更新.但是这并不是我们使用MVVM的正确方式 ...

  2. WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参

    原文:WPF ContextMenu 在MVVM模式中绑定 Command及使用CommandParameter传参 ContextMenu无论定义在.cs或.xaml文件中,都不继承父级的DataC ...

  3. WPF MVVM模式的一些理解

    /*本文转自 http://www.cnblogs.com/sirkevin/archive/2012/11/28/2793471.html */ 使用WPF+Mvvm开发一年多,期间由于对Mvvm模 ...

  4. WPF 在事件中绑定命令(不可以在模版中绑定命令)

    其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实现将命令绑定到事件中. 上一篇中我们介绍了MVVMLight中的命令的用法,那么仅仅知道命令是 ...

  5. WPF 在事件中绑定命令

    导航:MVVMLight系列文章目录:<关于 MVVMLight 设计模式系列> 其实这也不属于MVVMLight系列中的东东了,没兴趣的朋友可以跳过这篇文章,本文主要介绍如何在WPF中实 ...

  6. 在MVVM模式中,按钮Click事件的绑定方法

    在MVVM模式中,我们将Button的方法写到ViewModel中,然后绑定到前端界面.通常的做法是写一个类,继承ICommand接口,然而如果按钮比较多的话,就需要写很多的类,对于后期维护造成很大的 ...

  7. silverlighter下MVVM模式中利用Behavior和TargetedTriggerAction实现文本框的一些特效

    在silverlight一般开发模式中,给文本框添加一些事件是轻而易举的,然而MVVM开发模式中,想要给文本框添加一些事件并非那么容易,因为MVVM模式中,只有ICommand接口,而且也只有Butt ...

  8. “Win10 UAP 开发系列”之 在MVVM模式中控制ListView滚动位置

    这个扩展属性从WP8.1就开始用了,主要是为了解决MVVM模式中无法直接控制ListView滚动位置的问题.比如在VM中刷新了数据,需要将View中的ListView滚动到顶部,ListView只有一 ...

  9. windows下命令行模式中cd命令无效的原因

    当我们执行cmd 想切换当前工作目录时,会发现windows下命令行模式中cd命令没有生效,到底是什么原因呢? 例如: 当我们想切换到 D:\MySql\mysql-5.7.19-winx64\bin ...

随机推荐

  1. mysql 存相同内容:utb8mb4 会比 utf8 占用更多的内存吗,utf8mb4 浪费内存吗?utf8 utf8mb4 区别

    原文:mysql 存相同内容:utb8mb4 会比 utf8 占用更多的内存吗,utf8mb4 浪费内存吗?utf8 utf8mb4 区别 参考:http://www.fengyunxiao.cn u ...

  2. 【u249】新斯诺克

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 斯诺克又称英式台球,是一种流行的台球运动.在球桌上,台面四角以及两长边中心位置各有一个球洞,使用的球分 ...

  3. 使用UIDataDetectorTypes自己主动检測电话、网址和邮箱

    支付宝公布最新版本号9.0.再一次引发一场撕逼大战.微信说支付宝抄袭了它.支付宝说微信一直都在抄袭自己.在我看来.微信和支付宝都抄袭了对方.对于大佬们的抄袭.我们也是司空见惯了. 支付宝这一次更新,真 ...

  4. (十三)RabbitMQ消息队列-VirtualHost与权限管理

    原文:(十三)RabbitMQ消息队列-VirtualHost与权限管理 VirtualHost 像mysql有数据库的概念并且可以指定用户对库和表等操作的权限.那RabbitMQ呢?RabbitMQ ...

  5. SpringBoot 使用 @Value 从 YAML文件读取属性(转)

    在 YAML中有如下配置 paypal: mode:live 在类中,通过 @Value属性读取 @Value("${paypal.mode}") private String m ...

  6. boost::any的一般使用方法

    01.#include <iostream>    02.#include <list>    03.#include <boost/any.hpp>    04. ...

  7. 一个自己主动依据xcode中的objective-c代码生成类关系图的神器

    https://github.com/kimsungwhee/KSHObjcUML 安装方法: 1.下载项目 2.执行 3.会又一次开启一个新的xcode 4.选择一个项目,点击 Objc-UML 会 ...

  8. ImageView一例 分类: H1_ANDROID 2013-10-30 23:02 1812人阅读 评论(0) 收藏

    参考自<疯狂android讲义>2.4节 效果如下: 当点击图上某点时,将之附近放大至下图. 布局文件: <LinearLayout xmlns:android="http ...

  9. Sift算子特征点提取、描述及匹配全流程解析

    Sift之前的江湖 在Sift横空出世之前,特征点检测与匹配江湖上占据霸主地位的是角点检测家族.先来探究一下角点家族不为人知的恩怨情仇. 角点家族的族长是Moravec在1977年提出的Moravec ...

  10. todo bitnami

    https://bitnami.com/stack/dokuwiki https://bitnami.com/stack/jenkins/installer