创建MVVM WPF应用程序时的正确方法

创建MVVM WPF应用程序时的正确方法,wpf,xaml,mvvm,Wpf,Xaml,Mvvm,我想问你如何创建合适的MVVM应用程序WPF。昨天我试图理解PRISM和MVVM模型。这很好,但我想知道的是,如何在大型应用程序中使用MVVM。我有一个主窗口,然后是3种类型的TabControls(单独的UserControls-针对不同的用户组-Admin、EE、SPOC-带有代码隐藏和命令/方法)。我应该为每个TabControl类库创建吗?我应该在ViewModel和View中添加什么?如何在启动时从MainWindow调用LoginDialog?如果我理解正确,不应将代码作为代码添加到

我想问你如何创建合适的MVVM应用程序WPF。昨天我试图理解PRISM和MVVM模型。这很好,但我想知道的是,如何在大型应用程序中使用MVVM。我有一个主窗口,然后是3种类型的TabControls(单独的UserControls-针对不同的用户组-Admin、EE、SPOC-带有代码隐藏和命令/方法)。我应该为每个TabControl类库创建吗?我应该在ViewModel和View中添加什么?如何在启动时从MainWindow调用LoginDialog?如果我理解正确,不应将代码作为代码添加到MainWindow/View中?

LOB应用程序?这也是我的领域。我推荐galasofts MVVM Light

可悲的是,关于MVVM有很多异端邪说,甚至提供了我的Microsoft

我在WPF/SL工作了4.5年。有时候,看一段视频而不是在谷歌上搜索是可以的。当我学会正确的MVVM时,我看了视频

虽然已经过时,但它仍然非常相关(ooold),不过如果我没记错的话,他使用DO作为viewmodels,这是一个坏主意

您的视图(.xaml.cs)应该始终为空,才能成为狂热者;)

但你的回答是:

视图-应仅包含绑定和表示层。没有暗箱操作

视图模型-应包含所有相关代码,且独立于视图。这样,您可以为不同的视图或主题重用代码

Datamodel/Entity(这里有很多混淆)-只包含数据和简单的接口,如INotifyPropertyChanged、IErrorInfo等

是的,在大型应用程序中,您应该使用mvvm

对于您的登录问题,我通常使用转换器(),如果用户未授权自己,则显示登录屏幕,否则显示常规内容。通常使用BooleanToVisibilityConverter和InvertedBooleanToVisibilityConverter绑定到viewmodel上的“IsLoggedIn”属性:)

希望它在正确的方向上对您有所帮助,我将在这里为您提供一个简单的小示例

XAML:

就我个人而言,我喜欢使用页面,并且会在这种情况下使用它。但这只是一个例子

干杯


斯蒂安

远离棱镜。首先学习基本的MVVM。您不需要类库

三个视图中的每一个都应具有相应的viewModel。所有逻辑(命令等)都应该在VM中。该视图通常只是一个UserControl,其中包含绑定到VM各种属性的XAML。这只是你虚拟机上的一层薄薄的“皮肤”


代码隐藏是好的,只要它是特定的用户界面本身(如驾驶动画等)。一般来说,你会有很少的代码隐藏,因为任何显示逻辑都应该在虚拟机中(例如“如果按下此按钮,则其他内容会更新”)

这里有一个完整的MVVM介绍:-只需在谷歌上搜索一下。虽然我从未使用过PRISM,但我可以强烈推荐使用其他框架和Caliburn Micro——它是基于配置的约定,所以大多数时候你不需要做任何事情就可以让它正常工作。非常感谢Charleh!没有代码隐藏不是MVVM的目标,只要代码隐藏文件只与UI相关,并且不是您想要测试的东西,就允许使用尽可能多的代码。MVVM的目标是松耦合和可测试性。视图模型的目标同样是可测试性,而不是重用。如果你想成为一个狂热分子。。热衷于可测试和松散耦合的干净代码。。不是文件背后空代码的狂热者。@BenjaminPaul最重要的是vm是可单元测试的,代码是松散耦合的。但我不得不不同意什么属于codebehind,什么不属于codebehind。但狂热主义从来都不是好的,无论是宗教还是准则;)与视图相关的关注点可以愉快地生活在代码隐藏、ViewController(iOS)或Activity(Droid)中。DerekBeattie同意,@Stian我很抱歉,但你陷入了一个陷阱,认为MVVM规则意味着没有代码隐藏,这是对自己不公平的。。。这完全是错误的。业务逻辑不应该出现在我们大家都同意的观点中,但是建议某人永远不要在他们的观点中放置代码就像说总是将代码放在那里一样糟糕。这是一种误导。
<Window x:Class="custtest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:custtest="clr-namespace:custtest"
            xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
            xmlns:command="http://www.galasoft.ch/mvvmlight"
            Title="MainWindow" Height="350" Width="525">

    <Window.DataContext>
        <custtest:ViewModelTest/>
    </Window.DataContext>
    <Window.Resources>
        <custtest:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
        <custtest:InvertedBooleanToVisibilityConverter x:Key="InvertedBooleanToVisibilityConverter"/>
    </Window.Resources>


    <Grid>
  <!-- Really simple login ui, should use passwordbox for pwd etc -->
        <Grid Visibility="{Binding Path=IsLoggedIn, Converter={StaticResource InvertedBooleanToVisibilityConverter}}">

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" MinWidth="100"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <TextBlock Text="Login"></TextBlock>
            <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding UserName}"></TextBox>
            <TextBlock Grid.Row="1" Grid.Column="0" Text="Password"></TextBlock>
            <!-- Triggs command when user presses enter, should use pwdbox -->
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Password}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="KeyUp">
                        <command:EventToCommand Command="{Binding LoginCommand}" PassEventArgsToCommand="True"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </TextBox>

            <!-- Redundant button bad interaction design -->
            <Button Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" MaxWidth="200" MaxHeight="60"  Command="{Binding LoginClickCommand}" Content="Login"></Button>
        </Grid>


        <Grid Visibility="{Binding Path=IsLoggedIn, Converter={StaticResource BooleanToVisibilityConverter}}">
            <Button Content="This is your main content"></Button>
        </Grid>

    </Grid>
</Window>
public class ViewModelTest : INotifyPropertyChanged // Use a common baseclass
{
    private bool isLoggedIn;
    private string loginErrorText;
    private string password;
    private string userName;

    public ViewModelTest()
    {
        Initialize(); // Bleh
    }

    public bool IsLoggedIn
    {
        get { return isLoggedIn; }
        set
        {
            if (value.Equals(isLoggedIn)) return;
            isLoggedIn = value;
            OnPropertyChanged();
        }
    }
    public String LoginErrorText
    {
        get { return loginErrorText; }
        set
        {
            if (value == loginErrorText) return;
            loginErrorText = value;
            OnPropertyChanged();
        }
    }
    public String UserName
    {
        get { return userName; }
        set
        {
            if (value == userName) return;
            userName = value;
            OnPropertyChanged();
        }
    }
    public String Password
    {
        get { return password; }
        set
        {
            if (value == password) return;
            password = value;
            OnPropertyChanged();
        }
    }

    public ICommand LoginCommand { get; set; }
    public ICommand LoginClickCommand { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;

    protected void Initialize()
    {
        LoginCommand = new RelayCommand<KeyEventArgs>(IdentifyUser);
        LoginClickCommand = new RelayCommand(IdentifyUserClick);
    }

    private void IdentifyUserClick()
    {
        CheckUserCredentials();
    }

    private void CheckUserCredentials()
    {
        // Check your credentials here and change IsLoggedIn accordingly            
    }

    private void IdentifyUser(KeyEventArgs obj)
    {
        if (obj.Key == Key.Enter)
            CheckUserCredentials();
    }


    [NotifyPropertyChangedInvocator] // comment out if you dont have resharper
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}
public class BooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool && ((bool)value))
            return Visibility.Visible;
        else
            return Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}


public class InvertedBooleanToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool && ((bool)value))
            return Visibility.Collapsed;
        else
            return Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}