本文来自:Recommendations and best practices for implementing MVVM and XAML/.NET applications

项目的文件(目录)结构

项目的文件结构

  • App.xaml
  • Controls: 重复使用的 UI(没有 ViewModel)
  • Localization: 用于程序本土化的类代码和资源文件
  • Models: Model and domain classes
  • ViewModels: View models classes
    • MainWindowModel.cs
    • MyViewModel.cs
    • Dialogs
      • SelectItemDialogModel.cs
  • Views: Contains the views
    • MainWindow.xaml
    • MyView.xaml
    • Dialogs
      • SelectItemDialog.xaml

View 的名称体是以它的类型(Window、View、Dialog)结尾的;ViewModel 的名称是 View名称 + Model 后缀(例如:MainWindow – MainWindowModel)。

View – ViewModel 原则

View – ViewModel 是一一对应的关系,只有 View 才能访问它的 ViewModel,别的 View 不能访问其它的 ViewModel。

ViewModel 不能直接访问它的 View,只能通过 messenger (或其它 IoC 机制)实现方法 调用。

如果 View 很大(复杂),应该把它拆分成多个 View。

ViewModel 的实例化和赋值

在 XAML 里以根元素 Resources 方式实例化 ViewModel,然后再分配给顶级子元素的 DataContext,像这样:

 <UserControl x:Class="My.Namespace.MySampleView" ...> 
    <UserControl.Resources> 
        <viewModels:MySampleViewModel x:Key="ViewModel" /> 
    </UserControl.Resources> 

    <Grid DataContext="{StaticResource ViewModel}"> 
        ... 
    </Grid> 
</UserControl> 

如果需要在 View 中访问 ViewModel,那么可以在 .xaml.cs 中这样实现:

 protected MySampleViewModel Model 
{
    get { return (MySampleViewModel)Resources["ViewModel"]; } 
}

这样做的好处是:

  • Visual Studio 的编辑器能识别到 Resource 的类型,这样就可以提供 IntelliSense 功能
  • 其它 UI 需要 ViewModel 的内容时,可以很方便通过 Resource 的 key 来访问。

不要在 View 实例化的地方设置 DataContext 属性, 只有 View 自己才能给它的 DataContext 赋值。 如下做法是错的:

<MySubView DataContext="{Binding MySubViewModel}" />

根元素需要绑定 ViewModel 属性怎么办

因为 ViewModel 只有在 Resources 之后才可用,所以我们就无法直接在根元素上使用属性绑定,需要换种方式,像这样:

 <Window ...> 
    <Window.Resources> 
        <viewModels:MyWindowModel x:Key="ViewModel" /> 
    </Window.Resources> 
    <Window.Title> 
        <Binding Source="{StaticResource ViewModel}" Path="Document.Title" /> 
    </Window.Title> 

    ...  

Command 实现

所有 UI 动作都在 ViewModel 中以 Command 实现 。

使用 Dependency Property 来给 View 传参

Dependency Property 这样实现:

public class MySubView
{
    public static readonly DependencyProperty ProjectProperty = DependencyProperty.Register( 
        "Project", typeof(Project), typeof(MySubView),  
        new PropertyMetadata(default(Project), OnProjectChanged)); 

    public Project Project 
    { 
        get { return (Project)GetValue(ProjectProperty); } 
        set { SetValue(ProjectProperty, value); } 
    } 

    private static void OnProjectChanged(DependencyObject obj,  
        DependencyPropertyChangedEventArgs args) 
    { 
        ((MySubView)obj).Model.Project = (Project)args.NewValue; 
    }  

    public ProjectPropertiesViewModel Model 
    { 
        get { return (ProjectPropertiesViewModel)Resources["ViewModel"]; } 
    } 
}

然后,在 xaml 中可以这样传参:

<MySubView Project="{Binding SelectedProject}" />

ViewModel 的生命周期

View 调用 ViewModel 的相关方法:

public MyView() 
{ 
    InitializeComponent(); 
    Model.Initialize();    
    Loaded += delegate { Model.OnLoaded(); }; 
    Unloaded += delegate { Model.OnUnloaded(); }; 
}

ViewModel 实现:

public class MyViewModel  
{ 
    private bool _isLoaded = false;  

    public MyViewModel()
    {
        // TODO: Add your constructor code here
        // The ctor is always called, initialize view model so that it also works in designer
    }

    public void Initialize()
    {
        // TODO: Add your initialization code here 
        // This method is only called when the application is running
    }

    public void OnLoaded() 
    { 
        if (!_isLoaded) 
        { 
            // TODO: Add your loaded code here 
            _isLoaded = true;  
        } 
    } 

    public void OnUnloaded() 
    { 
        if (_isLoaded) 
        { 
            // TODO: Add your cleanup/unloaded code here 
            _isLoaded = false; 
        } 
    }
    ... 

注意,我们在 xaml 中实例化了 ViewModel,这样 ViewModel 的构造函数在 Visual Studio 编辑器中就会被执行,没必要让代码在设计阶段就执行,所以我们独立出了 Initialize 方法。

XAML 的绑定一定要声明类型

这一条很重要:XAML 里的所有绑定(Binding)都要声明类型,通常在 Resources 会声明类型的,但是如果有其它的绑定方式,一定要手动声明绑定的类型:

<Window xmlns:MyNamespace="clr-namespace:MyNamespace"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        ...>
    ... 
    <StackPanel d:DataContext="{d:DesignInstance Type=MyNamespace:MyType}"> 
        ... 
    </StackPanel> 

如果使用 WordPress multi site 功能的话,你会发现 administrator 账号是缺少安装插件的权限的,必须要 super admin 才行。通过数据库可以直接把账号提升到 super admin 权限,需要操作 *sitemeta 表,给 meta_key=site_admin 的 meta_value 加入账号名即可。

先看下这个问题

有两个文件 a.txt 和 b.txt
a.txt:

b.txt:

你需要能输出 b.txt 里没有在 a.txt 中的行:

最简单的方法是使用 Windows 的 findstr 命令

是不是超级简单的,参数请参考这里

下面这个语句可以随机生成过去一年(31536000秒)内的 datetime:

主要利用的是 FROM_UNIXTIME 和 UNIX_TIMESTAMP

打乱行是处理文本文件经常碰到的操作,但是当遇到超大文件时常规做法就显得力不从心了。分享一个可以处理超大文件乱行操作的 Python 脚本,非常值得收藏。

VBScript里你是怎么调用sub(子程序)的呢?
当然这样啊:

MySub1(MyArg)

可是,当别外一个sub有多个参数时呢?

MySub2(MyArg1,MyArg2)

它竟然出错!cannot use parentheses!不能使用括号!这是个什么鬼!
先说结论:如果你以一般方式调用sub时,参数是不能使用括号包着的。
只能这样:

MySub2 MyArg1,MyArg2

骗人,明明可以啊!……好吧……其实:

MySub1(MyArg)

程序的真正理解是:

MySub1 (MyArg)

注意中间有个空格!而(MyArg)的意思是:以ByVal的方式传递参数!
VBScript里调用函数(function)或子程序(sub)时,(定义function和sub时如果你没有声明ByVal)默认是以ByRef的方式传递参数的;如果function或sub已经定义了参数是ByRef的,你还可以强制以ByVal的方式传递参数,那就是给参数加上括号。所以:

MySub1 (MyArg)

的真正意思是:调用MySub1并且以ByVal的方式传递参数MyArg。
先记着结论:如果你以一般方式调用sub时,参数是不能使用括号包着的。所以:

MySub2(MyArg1,MyArg2)

会直接出错,正确的写法是:

MySub2 MyArg1,MyArg2

呃……一直在说以一般方式调用sub,那么不一般的方式是什么啊?
就是用Call来调用sub,并且用Call的时候参数必须用括号了:

MySub2(MyArg1,MyArg2)

总的来说:
括号在VB中的几个意思:

  1. 对括号内的表达式求值,例如:Average = (First + Last) / 2
  2. 引用数组的第几个元素,例如:Item = MyArray(Index)
  3. 调用函数(function)或子程序(sub),例如:Limit = UBound(MyArray)
    • 如果函数调用被赋值,那么它的参数必须使用括号:Result = MyFunc(MyArg)
    • 使用Call调用函数或子程序时它的参数必须使用括号:Call MySub(MyArg)
    • 如果不是以上两种情况,则不能使用括号。
  4. 强制以byval的方式传递参数:Result = MyFunction(Arg1, (Arg2)) ‘Arg1是byref,Arg2是byval

通过python可以很方便的检测一个网址的存活(是不是404),即检测状态码,这种方式不必等待整个网址下载完整。

def check_url(url:str):
p = urlparse(url)
conn = HTTPConnection(p.netloc)
conn.request('HEAD', p.path)
resp = conn.getresponse()
return resp.status < 400

使用起来也非常简单:

# True
check_url("http://www.baidu.com")
# False
check_url("http://www.baidu.com/does-not-exists.html")

虽然Firefox的占有率越来越低了,但毕竟适合自己才是最好的,Firefox仍然是我的首先浏览器,这上面有vimperator、adblock、flashinspector……

需要的话还可以自己写些扩展(现在叫附加组件)。

我的工作需要经常测试网站在不同来路下的表现,open with referer就是一个适合这样的小工具。安装后,你会在右上角看到一个图标,点击这个图标,网页就会以带有http referer的方式打开(设置请求消息头的referer)。

打开菜单-附加组件-open with referee,先设置你要使用的来路


点击右上角open with referee的图标


open with referee会设置消息头的referee字段