3

How to write WPF OpenFileDialog using MVVM (Model-View-ViewModel) in c#? I have visited some websites regarding this OpenFileDialog ,but I didn't get clear idea about it. This is my code

In 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>  

In 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


 }
}

In 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));
}


 }
}

How should I write code to get OpenFileDialog. I have seen this link WPF OpenFileDialog with the MVVM pattern? but I don't know how to write it for my above code. please someone help me by editing my code to get OpenFileDailog.

GJPD
  • 89
  • 1
  • 9
  • This is a clear answer: [Open File Dialog MVVM](https://stackoverflow.com/a/43756154/7713750) – Rekshino Jan 02 '19 at 15:28
  • 3
    Possible duplicate of [Open File Dialog MVVM](https://stackoverflow.com/questions/1043918/open-file-dialog-mvvm) – Rekshino Jan 02 '19 at 15:32
  • yeah....I didn't say that is not a clear answer. I really don't know how to write it for my code....Will u help me? – GJPD Jan 02 '19 at 15:36
  • May be because I'm a c# beginner...help me – GJPD Jan 02 '19 at 15:39
  • Will u please share me the CommandImpl Implementation of https://stackoverflow.com/questions/1043918/open-file-dialog-mvvm @Rekshino – GJPD Jan 03 '19 at 06:55
  • [relaycommand](https://stackoverflow.com/questions/22285866/why-relaycommand). CommandManager I have just omitted. – Rekshino Jan 03 '19 at 07:16
  • " The type or namespace name 'CommandImpl" could not be found(are you missing a using directive or an assembly reference)" . This is an error which i'm getting ,how to rectify this?...Please help me @Rekshino – GJPD Jan 03 '19 at 08:47

1 Answers1

7

In my opinion this kind of things doesn't belong in to the ViewModel. It's View specific logic. The View alone handles user input and then sends it to the ViewModel. ViewModel never asks the View to do something. This would invert the dependency chain and couple the ViewModel to the View. The dependencies have to be like this: View --> ViewModel --> Model. The ViewModel doesn't even know about the type of views nor that there is a View at all.

You have to open the dialog from your view and then send the result to the view model. For this you could create a simple event handler in your code-behind and attach it to a button's click event. You take the picked file and use an ICommand to invoke e.g. an open file action. That's MVVM (or MVP). Separate the concerns of the views from your models.

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;
    }
}
  • 1
    Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/186073/discussion-on-answer-by-macpowder-wpf-openfiledialog-using-mvvm-model-view-view). – Samuel Liew Jan 02 '19 at 23:07
  • 'viewModel.OpenFileCommand' is inaccessible due to its protection level- I'm getting this error ....Please help me @MacPowder – GJPD Jan 03 '19 at 10:20
  • @GJPD I edited the code. Just make the property 'OpenFileCommand' public. –  Jan 03 '19 at 10:44
  • Operator '&&' cannot be applied to operands of type 'bool?' and 'bool' - This is an error which I'm getting in if condition line of MainWindow.xaml.cs @MacPowder – GJPD Jan 03 '19 at 10:57
  • @GJPD I corrected a spelling error in ViewModel.cs: "public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); } " –  Jan 03 '19 at 11:05
  • @GJPD I edited the anser and added folder dialog. But you deleted your question or last comment I see right now. –  Jan 04 '19 at 08:53
  • yeah ...It should not extend as a chat session. Thats y I deleted some of my comments.sorry – GJPD Jan 04 '19 at 09:01
  • Yeah. sure...I ll mark it as answer once I got the output for folder dialog @MacPowder – GJPD Jan 04 '19 at 10:53
  • But once I click the folder from FolderDialog Box, I didn't get the selected folder path...How to achieve that @MacPowder – GJPD Jan 08 '19 at 04:22
  • It's working. Are you sure you didn't changed something? –  Jan 09 '19 at 20:29