2

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.

7
  • This is a clear answer: Open File Dialog MVVM Commented Jan 2, 2019 at 15:28
  • 3
    Possible duplicate of Open File Dialog MVVM Commented Jan 2, 2019 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? Commented Jan 2, 2019 at 15:36
  • May be because I'm a c# beginner...help me Commented Jan 2, 2019 at 15:39
  • Will u please share me the CommandImpl Implementation of stackoverflow.com/questions/1043918/open-file-dialog-mvvm @Rekshino Commented Jan 3, 2019 at 6:55

1 Answer 1

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;
    }
}
Sign up to request clarification or add additional context in comments.

10 Comments

Comments are not for extended discussion; this conversation has been moved to chat.
'viewModel.OpenFileCommand' is inaccessible due to its protection level- I'm getting this error ....Please help me @MacPowder
@GJPD I edited the code. Just make the property 'OpenFileCommand' public.
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 I corrected a spelling error in ViewModel.cs: "public ICommand OpenFileCommand { get => new RelayCommand(OpenFile, CanOpenFile); } "
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.