Posted by: Zeeshan Amjad | January 25, 2014

File Dialog ViewModel


During one of my WPF application, I came across a situation to display the File dialog to open and save the file. My first abstraction is to make a ViewModel for File dialog so I can reuse it. Although I can use the OpenFileDialog and SaveFileDialog classes from the FileViewModel, but I want to make the user interface logic separate from the ViewModel. To make the user interface logic separate, I created a small utility or helper class to to wrap the OpenFileDialog and SaveFileDialog functionality. Here is my class.

Code Snippet
public sealed class FileService
{
    public Stream OpenFile(string defaultExtension, string filter)
    {
        OpenFileDialog fd = new OpenFileDialog();
        fd.DefaultExt = defaultExtension;
        fd.Filter = filter;

        bool? result = fd.ShowDialog();

        return result.Value ? fd.OpenFile() : null;
    }

    public Stream SaveFile(string defaultExtension, string filter)
    {
        SaveFileDialog fd = new SaveFileDialog();
        fd.DefaultExt = defaultExtension;
        fd.Filter = filter;

        bool? result = fd.ShowDialog();

        return result.Value ? fd.OpenFile() : null;
    }        
}

 

Now my next step is to FileDialogViewModel. Assuming that I already have a ViewModelBase class that implements the INotifyPropertyChanged interface, here is a simple implementation of FileDialogViewModel.

Code Snippet
public class FileDialogViewModel : ViewModelBase
{
    public FileDialogViewModel()
    {
        this.SaveCommand = new RelayCommand(this.SaveFile);
        this.OpenCommand = new RelayCommand(this.OpenFile);            
    }

    #region Properties

    public Stream Stream
    {
        get;
        set;
    }

    public string Extension
    {
        get;
        set;
    }

    public string Filter
    {
        get;
        set;
    }
    public ICommand OpenCommand
    {
        get;
        set;
    }

    public ICommand SaveCommand
    {
        get;
        set;
    }

    #endregion

    private void OpenFile()
    {
        FileService fileServices = new FileService();
        this.Stream = fileServices.OpenFile(this.Extension, this.Filter);
    }

    private void SaveFile()
    {
        FileService fileServices = new FileService();
        this.Stream = fileServices.SaveFile(this.Extension, this.Filter);
    }
}

 

Because the return type of Execute method in ICommand interface doesn’t return anything, therefore, we created a Stream property to store the stream of file. This small FileDialogViewModel class encapsulate the functionality of opening and saving file without knowing anything about the user interface.

Now comes to our main view model that uses the FileDialogViewModel. Here is a simple implementation of our main view model that uses the FileDialogViewModel to open and save the file to make a simple text editor.

Code Snippet
public class MainViewmodel : ViewModelBase
{
    private string text;

    public MainViewmodel()
    {
        this.SaveCommand = new RelayCommand(this.SaveFile);
        this.OpenCommand = new RelayCommand(this.OpenFile);            
    }

    #region Properties

    public string Text
    {
        get
        {
            return this.text;
        }

        set
        {
            this.text = value;
            this.RaisePropertyChanged("Text");
        }
    }

    public ICommand OpenCommand
    {
        get;
        set;
    }

    public ICommand SaveCommand
    {
        get;
        set;
    }

    #endregion

    private void OpenFile()
    {
        FileDialogViewModel fdvm = new FileDialogViewModel();
        fdvm.Extension = "*.txt";
        fdvm.Filter = "Text documents (.txt)|*.txt";

        fdvm.OpenCommand.Execute(null);

        using (StreamReader sr = new StreamReader(fdvm.Stream, Encoding.ASCII))
        {
            this.Text = sr.ReadToEnd();
        }
    }

    private void SaveFile()
    {
        FileDialogViewModel fdvm = new FileDialogViewModel();
        fdvm.Extension = "*.txt";
        fdvm.Filter = "Text documents (.txt)|*.txt";

        fdvm.SaveCommand.Execute(null);

        using (StreamWriter sw = new StreamWriter(fdvm.Stream, Encoding.ASCII))
        {
            sw.Write(this.Text.ToString(CultureInfo.InvariantCulture));    
        }           
    }
}

 

Here is a complete XAML code of the program.

Code Snippet
<Window x:Class="WpfFileDialog.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
        Title="FileViewModel" Height="350" Width="525">
    <Grid DataContext="{Binding}">
        <Grid.RowDefinitions>
            <RowDefinition Height="4*"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <TextBox Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="10"
                 TextWrapping="Wrap" AcceptsReturn="True"
                 HorizontalScrollBarVisibility="Auto"
                 VerticalScrollBarVisibility="Auto"
                 Text="{Binding Text}"/>
        <Button Grid.Column="0" Grid.Row="2" Margin="10"
                Command="{Binding OpenCommand}">
            Open
        </Button>
        <Button Grid.Column="1" Grid.Row="2" Margin="10"
                Command="{Binding SaveCommand}">
            Save
        </Button>
    </Grid>
</Window>

And here is a complete C# code of this simple text editor using FileDialogViewModel

Code Snippet
namespace WpfFileDialog
{
    using System;
    using System.ComponentModel;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Windows;
    using System.Windows.Input;
    using Microsoft.Win32;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainViewmodel mvm = new MainViewmodel();

        public MainWindow()
        {
            InitializeComponent();

            DataContext = mvm;
        }
    }

    public class MainViewmodel : ViewModelBase
    {
        private string text;

        public MainViewmodel()
        {
            this.SaveCommand = new RelayCommand(this.SaveFile);
            this.OpenCommand = new RelayCommand(this.OpenFile);            
        }

        #region Properties

        public string Text
        {
            get
            {
                return this.text;
            }

            set
            {
                this.text = value;
                this.RaisePropertyChanged("Text");
            }
        }

        public ICommand OpenCommand
        {
            get;
            set;
        }

        public ICommand SaveCommand
        {
            get;
            set;
        }

        #endregion

        private void OpenFile()
        {
            FileDialogViewModel fdvm = new FileDialogViewModel();
            fdvm.Extension = "*.txt";
            fdvm.Filter = "Text documents (.txt)|*.txt";

            fdvm.OpenCommand.Execute(null);

            if (fdvm.Stream == null)
                return;

            using (StreamReader sr = new StreamReader(fdvm.Stream, Encoding.ASCII))
            {
                this.Text = sr.ReadToEnd();
            }
        }

        private void SaveFile()
        {
            FileDialogViewModel fdvm = new FileDialogViewModel();
            fdvm.Extension = "*.txt";
            fdvm.Filter = "Text documents (.txt)|*.txt";

            fdvm.SaveCommand.Execute(null);

            if (fdvm.Stream == null)
                return;

            using (StreamWriter sw = new StreamWriter(fdvm.Stream, Encoding.ASCII))
            {
                sw.Write(this.Text.ToString(CultureInfo.InvariantCulture));    
            }           
        }
    }

    public class FileDialogViewModel : ViewModelBase
    {
        public FileDialogViewModel()
        {
            this.SaveCommand = new RelayCommand(this.SaveFile);
            this.OpenCommand = new RelayCommand(this.OpenFile);            
        }

        #region Properties

        public Stream Stream
        {
            get;
            set;
        }

        public string Extension
        {
            get;
            set;
        }

        public string Filter
        {
            get;
            set;
        }
        public ICommand OpenCommand
        {
            get;
            set;
        }

        public ICommand SaveCommand
        {
            get;
            set;
        }

        #endregion

        private void OpenFile()
        {
            FileService fileServices = new FileService();
            this.Stream = fileServices.OpenFile(this.Extension, this.Filter);
        }

        private void SaveFile()
        {
            FileService fileServices = new FileService();
            this.Stream = fileServices.SaveFile(this.Extension, this.Filter);
        }
    }

    public sealed class FileService
    {
        public Stream OpenFile(string defaultExtension, string filter)
        {
            OpenFileDialog fd = new OpenFileDialog();
            fd.DefaultExt = defaultExtension;
            fd.Filter = filter;

            bool? result = fd.ShowDialog();

            return result.Value ? fd.OpenFile() : null;
        }

        public Stream SaveFile(string defaultExtension, string filter)
        {
            SaveFileDialog fd = new SaveFileDialog();
            fd.DefaultExt = defaultExtension;
            fd.Filter = filter;

            bool? result = fd.ShowDialog();

            return result.Value ? fd.OpenFile() : null;
        }        
    }

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        #region NotifyPropertyChanged Methods

        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;

            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public sealed class RelayCommand : ICommand
    {
        private Action function;

        public RelayCommand(Action function)
        {
            this.function = function;
        }

        public bool CanExecute(object parameter)
        {
            return this.function != null;
        }

        public void Execute(object parameter)
        {
            if (this.function != null)
            {
                this.function();
            }
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    }
}

 

Here is an output of the program.

FileDialogViewModel

Advertisements

Responses

  1. […] saw the first very simple implementation of FileDialogViewModel here. There is still a room of improvement in this class. The first obvious improvement is to get the […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: