欢迎使用StackOverflow!我会尽力回答您的第一个问题:D
从我读到的内容来看,我正急着要告诉您键盘:“是的,要摔死了”,但我决定为您制作一个很小的示例,说明MVVM模式可以带来哪些好处你。
就我个人而言,我之前有点像您一样,怀疑它会带来的好处和额外的工作。但让我告诉您,这确实值得。直到最近我才采用这种模式,并且我不停地思考为什么我以前没有做过呢?如此节省时间,使关注点分离并且非常简单。
所以这是例子!
现在是MVVM模式
在这里,组件之间的通信方式图,请注意这是一条链:
查看<-> ViewModel <->模型
(您很快就会从中受益)
模型:,它是您的应用程序执行操作的核心,在此示例中,这是加密数据的组件。这是处理加密过程的低级类,它不必了解ViewModel或View。它唯一关心的是加密输入数据并输出它,仅此而已!
public class EncryptorModel
{
public string Cipher(string text)
{
char[] enumerable = text.Select(s => ++s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
}
ViewModel(模型的 View ):现在变得很有趣,虽然乍一看该组件的好处并不明显,但它们确实有效,我将尝试将其出售给您:D
将ViewModel视为View和Model之间的网关,它的工作是通过在Model上执行操作并将Model发送的结果返回给View来满足来自View(用户)的请求。
正如您在下面看到的:该主机托管此加密示例的属性;将数据从 View 传递到模型或从模型传递(自动归功于WPF数据绑定(bind))。最后,它托管 View 触发的命令。
public class EncryptorViewModel : ViewModelBase
{
private RelayCommand _cipher;
private string _inputText;
private string _outputText;
public EncryptorViewModel()
{
Model = new EncryptorModel();
}
private EncryptorModel Model { get; set; }
#region Public properties
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
public string OutputText
{
get { return _outputText; }
set
{
_outputText = value;
RaisePropertyChanged();
}
}
#endregion
#region Commands
public RelayCommand Cipher
{
get { return _cipher ?? (_cipher = new RelayCommand(CipherExecute, CipherCanExecute)); }
}
private void CipherExecute()
{
OutputText = Model.Cipher(InputText);
}
private bool CipherCanExecute()
{
return !string.IsNullOrWhiteSpace(InputText);
}
#endregion
}
View :除了要介绍您的应用程序并在ViewModel中调用命令之外,没有什么好说的。
<Window x:Class="WpfApplication1.EncryptorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="EncryptorView"
Width="165"
Height="188">
<Window.Resources>
<wpfApplication1:EncryptorViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModel}}">
<StackPanel>
<TextBlock Text="Input text" />
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Output text" />
<TextBox Text="{Binding OutputText}" />
<Button Command="{Binding Cipher}" Content="Cipher" />
</StackPanel>
</Grid>
</Window>
结论
我本可以并且已经给出了更长的答案,但是为了保持简单而取消了,这是您现在应该保留的内容:
View 仅绑定(bind)到用户需要查看/操作的属性,并绑定(bind)到用户需要在Model 上执行的命令
ViewModel表示模型的简化 View ,它仅显示 View 需要的内容并在模型上执行命令该模型严格来说是您的专业领域,它的工作是加密/解密,仅此而已! 最终结论:使用MVVM可以使的关注点保持分离,使用此模式的应用程序易于维护。除非我在MVVM上呆了3天,否则这对我来说并不明显,所以我只能鼓励您这样做,您的编程项目显然将从中受益。
使用的环境:我用过Galasoft MVVM Light:
http://www.mvvmlight.net/installing/nuget/(仅限MVVM Light库软件包)
我自愿省略了服务定位器部分,您可以在Visual Studio项目模板中找到它:
http://www.mvvmlight.net/installing编辑
以下是一些可以升级“初始”方案的方案。
多个ViewModel:规则是每个 View (或用户界面)有一个ViewModel,因此对于您的应用程序。有2个窗口,那么您将有2个ViewModel。
在2个或更多ViewModel之间共享模型:例如,如果您可以证明两个窗口都可以在同一模型上工作,那么您只能拥有1个模型,而应在
App.xaml
中声明它:
App.xaml:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
StartupUri="EncryptorView.xaml">
<Application.Resources>
<wpfApplication1:EncryptorModel x:Key="Model1"/>
</Application.Resources>
</Application>
EncryptorViewModel1:
public class EncryptorViewModel1 : ViewModelBase
{
//private EncryptorModel Model { get; set; }
public EncryptorViewMode1l()
{
// Model = new EncryptorModel();
// Now you retrieve the model in App.xaml instead of declaring a private one above
var model =(EncryptorModel) Application.Current.FindResource("Model1");
}
}
方案:在同一 View 中使用多个加密这是另一个小示例,向您展示如何让用户在同一 View 中选择一种加密方法。
我们采用相同的ViewModel,
我们添加AvailableEncryptors
和CurrentEncryptor
属性我们修改CipherCanExecute
以便它考虑CurrentEncryptor
,仅当设置了InputText
并选择了加密器时,用户才能够加密也会CipherExecute
有所改变,EncryptorModel
根据指定的字符串和加密器进行密码
更新的ViewModel:
public class EncryptorViewModel : ViewModelBase
{
private RelayCommand _cipher;
private IEncryptor _currentEncryptor;
private string _inputText;
private string _outputText;
public EncryptorViewModel()
{
Model = new EncryptorModel();
}
private EncryptorModel Model { get; set; }
public IEnumerable<IEncryptor> AvailableEncryptors
{
get
{
Type type = typeof (IEncryptor);
IEnumerable<IEncryptor> encryptors =
Assembly
.GetExecutingAssembly()
.GetTypes()
.Where(p => type.IsAssignableFrom(p) && !p.IsInterface && !p.IsAbstract)
.Select(s => (IEncryptor) Activator.CreateInstance(s));
return encryptors;
}
}
public IEncryptor CurrentEncryptor
{
get { return _currentEncryptor; }
set
{
_currentEncryptor = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
#region Public properties
public string InputText
{
get { return _inputText; }
set
{
_inputText = value;
RaisePropertyChanged();
Cipher.RaiseCanExecuteChanged();
}
}
public string OutputText
{
get { return _outputText; }
set
{
_outputText = value;
RaisePropertyChanged();
}
}
#endregion
#region Commands
public RelayCommand Cipher
{
get { return _cipher ?? (_cipher = new RelayCommand(CipherExecute, CipherCanExecute)); }
}
private void CipherExecute()
{
OutputText = Model.Cipher(CurrentEncryptor, InputText);
}
private bool CipherCanExecute()
{
return CurrentEncryptor != null && !string.IsNullOrWhiteSpace(InputText);
}
#endregion
}
注意:,您可能会想完全删除Model并在ViewModel内执行所有操作,但是不会这样做,即使ViewModel只是充当网关,您也不应借此机会在Model中实现与加密相关的逻辑而不是ViewModel。
如果您将各个部分分开,那么将来可能会节省大量时间,例如,如果您需要应用程序的命令行版本,则只需要使用模型即可,因为所有必要的逻辑都在那里并且不会分散它和ViewModel。 (请参阅将ViewModel绑定(bind)到特定的UI框架(例如WPF))
然后,我更新了模型,以便在我们希望它加密某些内容时调用加密器:
public class EncryptorModel
{
public string Cipher(IEncryptor encryptor, string text)
{
return encryptor.Cipher(text);
}
}
最后,我实现了加密器:
public interface IEncryptor
{
string Description { get; }
string Cipher(string text);
}
public class Encryptor1 : IEncryptor
{
#region IEncryptor Members
public string Description
{
get { return "Encryptor 1"; }
}
public string Cipher(string text)
{
char[] enumerable = text.Select(s => ++s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
#endregion
}
public class Encryptor2 : IEncryptor
{
#region IEncryptor Members
public string Description
{
get { return "Encryptor 2"; }
}
public string Cipher(string text)
{
char[] enumerable = text.Select(s => --s).ToArray();
var cipher = new string(enumerable);
return cipher;
}
#endregion
}
以及更新后的 View :
<Window x:Class="WpfApplication1.EncryptorView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="EncryptorView"
Width="165"
Height="188">
<Window.Resources>
<wpfApplication1:EncryptorViewModel x:Key="ModelView" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ModelView}}">
<StackPanel>
<TextBlock Text="Select an encryptor" />
<ComboBox ItemsSource="{Binding AvailableEncryptors}" SelectedItem="{Binding CurrentEncryptor}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="wpfApplication1:IEncryptor">
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="Input text" />
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Output text" />
<TextBox Text="{Binding OutputText}" />
<Button Command="{Binding Cipher}" Content="Cipher" />
</StackPanel>
</Grid>
</Window>
结论正如您所看到的,我在实现新类型方面有些挣扎,但确实有返回,每种加密方法都是独立的,加密器也很不错。毕竟,加密器不是加密方法,因此最好分开使用。