在C#中使用MVVM(Model-View-ViewModel)的WPF OpenFileDialog

时间:2019-01-02 15:23:37

标签: c# wpf mvvm

如何在c#中使用MVVM(模型-视图-视图模型)编写WPF OpenFileDialog? 我已经访问了一些有关此OpenFileDialog的网站,但是我对此不清楚。这是我的代码

View.xaml 中:

<Window....  xmlns:VM="clr-namespace:myproject.myViewModel"
  ...  >
<Window.DataContext><VM:myViewModel/>

</Window.DataContext>
 <ItemsControl ItemsSource="{Binding mygroup}" >
        <ItemsControl.ItemTemplate >
        <DataTemplate>

                 <Grid >....

                       <TextBlock Text="Color"  Margin="20" VerticalAlignment="Center"/>
                        <ComboBox   KeyboardNavigation.TabIndex="0" Grid.Column="1" Margin="45,10,10,10" Height="30" Width="200" ItemsSource="{Binding Color}"   />
                        <TextBlock Text="Shapes" Grid.Row="1"  VerticalAlignment="Center"  />

                        <ComboBox KeyboardNavigation.TabIndex="3" Grid.Column="1" Grid.Row="1" Height="20" Width="150" SelectedIndex="0"   HorizontalContentAlignment="Right" 
                      VerticalAlignment="Center"  ItemsSource="{Binding Shapes}">
                                                            </ComboBox>

<TextBlock Text="Open Files "  VerticalAlignment="Center"      Grid.Row="0"  Grid.Column="2" Margin="10"    />
                                <TextBox  Grid.Column="3" Text="" Height="30" Grid.Row="0"   IsReadOnly="True" 
                        TextAlignment="Right" VerticalContentAlignment="Center" Width="200" />                                    <Button  Grid.Column="4"  Content="Browse"    Height="30" VerticalAlignment="Bottom"   MinWidth="41" />
                   </Grid>

 </Window>  

Model.cs 中:

namespace Myproject.Models
{
  public class ProjectModel : INotifyPropertyChanged
  {
    private ObservableCollection<string> color;
    private ObservableCollection<string> shapes;
    public ObservableCollection<string> Color
{
  get { return color; }
  set
  {
    color = value;
    NotifyPropertyChanged("Color");
  }
}

public ObservableCollection<string> Shapes
{
  get { return shapes; }
  set
  {
    shapes = value;
    NotifyPropertyChanged("Shapes");
  }
}


#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion

#region Private Helpers

private void NotifyPropertyChanged(string propertyName)
{
  if (PropertyChanged != null)
  {
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  }
}

#endregion


 }
}

ViewModel.cs 中:

namespace MyProject.ViewModels    
{
  public class myProjectViewModel : INotifyPropertyChanged
  {
    private ObservableCollection<myprojectmodel> mygroup;

public ObservableCollection<myprojectmodel> Mygroup
{
  get => this.mygroup;
  set
  {
    if (Equals(value, this.mygroup)) return;
    this.mygroup = value;
    OnPropertyChanged();
  }
}

public ProjectViewModel()
{
  Mygroup = new ObservableCollection<myprojectmodel>();
  List<string> lstColor = new List<string>();
  lstCity = new List<string> {"Pink", "Blue", "Red"};
  List<string> lstShapes = new List<string>();
  lstTemperature = new List<string> {"Rectangle", "Triangle", "Circle"};
   Mygroup.Add(
    new ProjectModel
    {
      Color= new ObservableCollection<string>(lstColor),
      Shapes = new ObservableCollection<string>(lstShapes),
      });
}

public event PropertyChangedEventHandler PropertyChanged;   

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
  this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}


 }
}

我应该如何编写代码以获取OpenFileDialog。 我已经看到了此链接WPF OpenFileDialog with the MVVM pattern?,但是我不知道如何为上面的代码编写它。  请有人通过编辑代码获取OpenFileDailog来帮助我。

1 个答案:

答案 0 :(得分:0)

我认为这种事情不属于ViewModel。这是查看特定的逻辑。 View独自处理用户输入,然后将其发送到ViewModel。 ViewModel从不要求View做某事。这将反转依赖关系链,并将ViewModel耦合到View。依赖关系必须是这样的: 查看-> ViewModel->模型。 ViewModel甚至不知道视图的类型,甚至根本不知道一个视图。

您必须从视图中打开对话框,然后将结果发送到视图模型。 为此,您可以在代码背后创建一个简单的事件处理程序,并将其附加到按钮的click事件上。您获取选择的文件并使用ICommand调用例如打开文件操作。那就是MVVM(或MVP)。将视图的关注点与模型分开。

MainWindow.xaml:

<Window x:Class="WpfOpenDialogExample.OpenFileDialogSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="OpenFileDialogSample" Height="300" Width="300">
    <Window.DataContext>
        <ViewModel />
    </Window.DataContext>

    <Grid>
        <Button Name="ShowFilePickerButton" Click="ShowFilePicker_OnClick" Content="Open file" />
    </Grid>
</Window>

MainWindow.xaml.cs:

using System;
using System.IO;
using System.Windows;
using Microsoft.Win32;

namespace WpfOpenDialogExample
{
    public partial class OpenFileDialogSample : Window
    {
        public OpenFileDialogSample()
        {
            InitializeComponent();
        }

        private void ShowFilePicker_OnClick(object sender, RoutedEventArgs e)
        {
            var viewModel = this.DataContext as ViewModel;
            OpenFileDialog openFileDialog = new OpenFileDialog();
            if(openFileDialog.ShowDialog() == true && viewModel.OpenFileCommand.CanExecute(openFileDialog.FileName))
            {
               viewModel.OpenFileCommand.Execute(openFileDialog.FileName);
            }
        }

        private void ShowFolderPicker_OnClick(object sender, RoutedEventArgs e)
        {
            var viewModel = this.DataContext as ViewModel;
            FolderBrowserDialog openFolderDialog = new FolderBrowserDialog();
            if(openFolderDialog.ShowDialog() == DialogResul.Ok && viewModel.OpenFolderCommand.CanExecute(openFolderDialog.SelectedPath ))
            {
               viewModel.OpenFolderCommand.Execute(openFolderDialog.SelectedPath );
            }
        }
    }
}

ViewModel.cs:

public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); }  

private void OpenFile(string filePath)
{
   ...
}

private bool CanOpenFile(string filePath)
{
   return File.Exists(filePath);
}

public ICommand OpenFolderCommand { get => new RelayCommand(OpenFolder, CanOpenFolder); }

private void OpenFolder(string folderPath)
{
   ...
}

private bool CanOpenFolder(string folderPath)
{
   return Directory.Exists(filePath);
}

RelayCommand.cs:

using System;
using System.Windows.Input;

namespace WpfOpenDialogExample
{
  /// <summary>
  /// An implementation independent ICommand implementation.
  /// Enables instant creation of an ICommand without implementing the ICommand interface for each command.
  /// The individual Execute() an CanExecute() members are suplied via delegates.
  /// <seealso cref="System.Windows.Input.ICommand"/>
  /// </summary>
  /// <remarks>The type of <c>RelaisCommand</c> actually is a <see cref="System.Windows.Input.ICommand"/></remarks>
    public class RelayCommand : ICommand
    {
      /// <summary>
      /// Default constructor to declare the concrete implementation of Execute(object):void and CanExecute(object) : bool
      /// </summary>
      /// <param name="executeDelegate">Delegate referencing the execution context method. 
      /// Delegate signature: delegate(object):void</param>
      /// <param name="canExecuteDelegate">Delegate referencing the canExecute context method.
      /// Delegate signature: delegate(object):bool</param>
      public RelayCommand(Action<object> executeDelegate , Predicate<object> canExecuteDelegate)
      {
        this.executeDelegate = executeDelegate;
        this.canExecuteDelegate = canExecuteDelegate;
      }

      /// <summary>
      /// Invokes the custom <c>canExecuteDelegate</c> which should check wether the command can be executed.
      /// </summary>
      /// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
      /// <returns>Expected to return tue, when the preconditions meet the requirements and therefore the command stored in <c>executeDelegate</c> can execute.
      /// Expected to return fals when command execution is not possible.</returns>
      public bool CanExecute(object parameter)
      {
        if (this.canExecuteDelegate != null)
        {
          return this.canExecuteDelegate(parameter);
        }
        return false;
      }

      /// <summary>
      /// Invokes the custom <c>executeDelegate</c>, which references the command to execute.
      /// </summary>
      /// <param name="parameter">Optional parameter of type <see cref="System.Object"/></param>
      public void Execute(object parameter)
      {
        if (this.executeDelegate != null)
          this.executeDelegate(parameter);
      }

      /// <summary>
      /// The event is triggered every time the conditions regarding the command have changed. This occures when <c>InvalidateRequerySuggested()</c> gets explicitly or implicitly called.
      /// Triggering this event usually results in an invokation of <c>CanExecute(object):bool</c> to check if the occured change has made command execution possible.
      /// The <see cref="System.Windows.Input.CommandManager"/> holds a weakrefernce to the observer.
      /// </summary>
      public event EventHandler CanExecuteChanged
      {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
      }

      private readonly Action<object> executeDelegate;
      private readonly Predicate<object> canExecuteDelegate;
    }
}