0

I'm making a simple file renaming program with a WPF UI. I take in a directory, read text into a grid, then apply changes. One of the parameters, however, which is the length of the file path, I would like to have updated on the fly as a user is typing in changes to their filename. Disclaimer: I'm very new to WPF and relatively new to C# (just trying to get back into programming, annoyingly, implementing WPF is more complicated than the C#, but I can't use WinForms for this one). I'm sure this is an INotifyPropertyChanged thing, though, from what I've seen online so far, there are quite a few ways to implement it-- so here I am. Thank you to all.

XAML

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Files;assembly=FileCollection"
        mc:Ignorable="d"
        WindowStyle="SingleBorderWindow" WindowStartupLocation="CenterScreen"
        Title="SLU Renamer" FontFamily="Segoe UI" Height="500" Width="1000" MinHeight="250" MinWidth="250" ResizeMode="CanResize">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Border BorderBrush="LightBlue" BorderThickness="1">
            <StackPanel Grid.Row="0">
                <Grid Height="25">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Button Grid.Column="0" x:Name="BtnBrowse" Content="Browse" Click="BtnBrowse_Click" Background="AliceBlue" FontFamily="Segoe UI" FontSize="13" BorderBrush="LightBlue" BorderThickness="0"/>
                    <Button Grid.Column="3" x:Name="BtnApply" Content="Apply" Click="BtnApply_Click" Background="AliceBlue" FontFamily="Segoe UI" FontSize="13" BorderBrush="LightBlue" BorderThickness="0"/>
                </Grid>
            </StackPanel>

        </Border>
        <DockPanel Grid.Row="2">
            <Grid x:Name="DataGridHolderGrid">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="727*"/>
                    <ColumnDefinition Width="33*"/>
                </Grid.ColumnDefinitions>
                <DataGrid Grid.Column="0" x:Name="DataGridFileNames" AutoGenerateColumns="False" ItemsSource="{Binding}" FontSize="13" FontFamily="Segoe UI Semibold" Background="White" 
                          CanUserReorderColumns="True" CanUserSortColumns="True" CanUserResizeRows="False" CanUserResizeColumns="True" AlternatingRowBackground="LightBlue"
                          VerticalGridLinesBrush="DeepSkyBlue" HorizontalGridLinesBrush="DeepSkyBlue" BorderBrush="DeepSkyBlue" BorderThickness="0"
                          ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto" Grid.ColumnSpan="2"
                          SourceUpdated="DataGridFileNames_SourceUpdated">
                    <DataGrid.Columns>
                        <DataGridTextColumn x:Name="OriginalFileNamesColumn" Header="Original Name" Width=".5*" 
                                            IsReadOnly="True" Binding="{Binding OriginalFileName}" />
                        <DataGridTextColumn x:Name="NewFileNamesColumn" Header="New Name" Width=".5*"
                                            IsReadOnly="False" Binding="{Binding NewFileName, Mode=TwoWay}" />
                        <DataGridTextColumn x:Name="FileExensionColumn" Header="Ext." Width=".125*"
                                            IsReadOnly="True" Binding="{Binding FileExtension}" />
                        <DataGridTextColumn x:Name="FinalFileNamesColumn" Header="File Path" Width=".75*"
                                            IsReadOnly="True" Binding="{Binding NewPath}" />
                        <DataGridTextColumn x:Name="PathLengthColumn" Header="Length" Width=".125*" 
                                            IsReadOnly="True" Binding="{Binding PathLen, UpdateSourceTrigger=PropertyChanged}" />
                    </DataGrid.Columns>
                </DataGrid>
            </Grid>
        </DockPanel>
    </Grid>
</Window>

XAML.cs (Code Behind??)

using System;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using IronXL;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.IO;

namespace RenamerApp_3._0
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Microsoft.Win32.OpenFileDialog openFileDialog = new Microsoft.Win32.OpenFileDialog();
        Microsoft.Win32.OpenFileDialog openExcelWkbkDialog = new Microsoft.Win32.OpenFileDialog();
        FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
        //Dictionary<string, string> browseOptions = new Dictionary<string, string>();

        public ObservableCollection<FileName> observableFileNames = new ObservableCollection<FileName>();

        public MainWindow()
        {
            InitializeComponent();
            InitDataGrid();
            InitializeOpenFileDialog();
            //DataGridFileNames.HeadersVisibility = ;
            //InitializeComboBox();
        }

        private void BtnBrowse_Click(object sender, RoutedEventArgs e)
        {
            //OpenFileDialog();
            OpenFolderDialog();
            DataGridFileNames.Visibility = Visibility.Visible;
        }
        private void BtnApply_Click(object sender, RoutedEventArgs e)
        {
            foreach (var file in observableFileNames)
            {
                File.Move(file.OriginalPath + @"\" + file.OriginalFileName + file.FileExtension, file.NewPath + @"\" + file.NewFileName + file.FileExtension);
                FileNameInit(file);
                UpdateLen(file);
                DataGridFileNames.Items.Refresh();
            }
        }

        private void InitializeOpenFileDialog()
        {
            this.openFileDialog.Multiselect = true;
        }
        private void OpenFileDialog()
        {
            Nullable<bool> result = openFileDialog.ShowDialog();
            if (result.HasValue && result.Value)
            {
                foreach (string filename in openFileDialog.FileNames)
                {
                    FileName fileName = new FileName(filename);
                    observableFileNames.Add(fileName);
                }
                DataGridFileNames.DataContext = observableFileNames;
            }
        }
        private void OpenFolderDialog()
        {
            DialogResult result = folderBrowserDialog.ShowDialog();
            if (result == System.Windows.Forms.DialogResult.OK)
            {
                string path = folderBrowserDialog.SelectedPath;
                DirectoryInfo directoryInfo = new DirectoryInfo(path);
                FileInfo[] files = directoryInfo.GetFiles();
                foreach (FileInfo file in files)
                {
                    FileName fileName = new FileName(file);
                    observableFileNames.Add(fileName);
                }
                DataGridFileNames.DataContext = observableFileNames;
            }
        }

        private void FileNameInit(FileName file)
        {
            file.OriginalFileName = file.NewFileName;
            file.OriginalPath = file.NewPath;
        }
        private void InitDataGrid()
        {
            DataGridFileNames.Visibility = Visibility.Hidden;
        }

        private void DataGridFileNames_SourceUpdated(object sender, DataTransferEventArgs e)
        {
            UpdateLen(observableFileNames);
            DataGridFileNames.Items.Refresh();
        }

        public void UpdateLen(ObservableCollection<FileName> observableFileNames)
        {
            int currentRowIndex = DataGridFileNames.Items.IndexOf(DataGridFileNames.CurrentItem);
            FileName file = observableFileNames[currentRowIndex - 1];
            file.PathLen = file.NewPath.Length + +@"\".Length + file.NewFileName.Length + file.FileExtension.Length;
        }

        public void UpdateLen(FileName file)
        {
            file.PathLen = file.NewPath.Length + +@"\".Length + file.NewFileName.Length + file.FileExtension.Length;
        }

Data

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using IronXL;

namespace Files
{
    public class FileName : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public FileInfo OriginalFile { get; set; }
        public string FileExtension { get; set; }
        public int PathLen { get; set; }

        public string OriginalFileName { get; set; }
        public string NewFileName { get; set; }

        public string OriginalPath { get; set; }
        public string NewPath { get; set; }

        public FileName()
        {

        }

        public FileName(string filename)
        {
            OriginalFile = new FileInfo(filename);
            OriginalPath = NewPath = Path.GetDirectoryName(filename);
            OriginalFileName = NewFileName = Path.GetFileNameWithoutExtension(filename);
            FileExtension = Path.GetExtension(filename);
            PathLen = filename.Length;
        }

        public FileName(FileInfo file)
        {
            OriginalFile = file;
            OriginalPath = NewPath = OriginalFile.DirectoryName;
            OriginalFileName = NewFileName = Path.GetFileNameWithoutExtension(OriginalFile.Name);
            FileExtension = Path.GetExtension(OriginalFile.Name);
            string path = OriginalFile.ToString();
            PathLen = path.Length;
        }

        public string index { get; set; }
        public FileName(WorkBook workbook, string index)
        {
            WorkSheet sheet = workbook.WorkSheets.First();

            foreach (var cell in sheet[index])
            {
                string path = cell.ToString();
                OriginalFile = new FileInfo(path);
                OriginalPath = NewPath = OriginalFile.DirectoryName;
                OriginalFileName = NewFileName = path;
                PathLen = path.Length;
            }
        }



        //String pathHolder;
        //private void FileName_PropertyChanged(object sender, PropertyChangedEventArgs e)
        //{
        //    pathHolder = NewPath + NewFileName + FileExtension;
        //    PathLen = pathHolder.Length;
        //}

    }
}

Updated Data Class

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using IronXL;

namespace Files
{
    public class FileName : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public FileInfo OriginalFile { get; set; }
        public string FileExtension { get; set; }

        public string OriginalFileName { get; set; }
        public string NewFileName { get; set; }

        public string OriginalPath { get; set; }
        private string _newPath;
        public string NewPath
        {
            get { return _newPath; }
            set
            {
                _newPath = NewPath;
                PathLen = NewPath.Length + @"\".Length + NewFileName.Length + FileExtension.Length;
                OnPropertyChanged();
            }
        }
        private int _pathLen;
        public int PathLen
        {
            get { return _pathLen; }
            set
            {
                _pathLen = value;
                OnPropertyChanged();
            }
        }
        //public int PathLen { get; set; }

        public FileName()
        {

        }

        public FileName(string filename)
        {
            OriginalFile = new FileInfo(filename);
            OriginalPath = NewPath = Path.GetDirectoryName(filename);
            OriginalFileName = NewFileName = Path.GetFileNameWithoutExtension(filename);
            FileExtension = Path.GetExtension(filename);
            PathLen = filename.Length;
        }

        public FileName(FileInfo file)
        {
            OriginalFile = file;
            OriginalPath = NewPath = OriginalFile.DirectoryName;
            OriginalFileName = NewFileName = Path.GetFileNameWithoutExtension(OriginalFile.Name);
            FileExtension = Path.GetExtension(OriginalFile.Name);
            string path = OriginalFile.ToString();
            PathLen = path.Length;
        }

        public string index { get; set; }
        public FileName(WorkBook workbook, string index)
        {
            WorkSheet sheet = workbook.WorkSheets.First();

            foreach (var cell in sheet[index])
            {
                string path = cell.ToString();
                OriginalFile = new FileInfo(path);
                OriginalPath = NewPath = OriginalFile.DirectoryName;
                OriginalFileName = NewFileName = path;
                PathLen = path.Length;
            }
        }

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  • What's the problem you're facing? – Sach Nov 22 '19 at 18:57
  • Also in `FileName` class, you have the `PropertyChangedEventHandler`, but you need to attach a method to it. https://stackoverflow.com/a/1316417/302248 – Sach Nov 22 '19 at 19:00
  • It's seemed like a proper implementation of INotifyPropertyChanged would lead to the immediate updates I'm looking for. So, the problem for me, being an overwhelmed newb, is how to do so-- if that's really the way to go for automativ updates to the data class and the UI. – icantseecolors Nov 22 '19 at 20:33

1 Answers1

1

When you want to use an INotifyPropertyChanged, you have to do something like this:

public class FileName : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public FileInfo OriginalFile { get; set; }
    public string FileExtension { get; set; }

    private int _pathLen;

    public int PathLen 
    {
       get { return _pathLen; }
       set 
       { 
          _pathLen = value;
          OnPropertyChanged();
       }
    }

    public string OriginalFileName { get; set; }
    public string NewFileName { get; set; }

    public string OriginalPath { get; set; }
    public string NewPath { get; set; }

    public FileName()
    {

    }

    public FileName(string filename)
    {
        OriginalFile = new FileInfo(filename);
        OriginalPath = NewPath = Path.GetDirectoryName(filename);
        OriginalFileName = NewFileName = Path.GetFileNameWithoutExtension(filename);
        FileExtension = Path.GetExtension(filename);
        PathLen = filename.Length;
    }

    public FileName(FileInfo file)
    {
        OriginalFile = file;
        OriginalPath = NewPath = OriginalFile.DirectoryName;
        OriginalFileName = NewFileName = 
           Path.GetFileNameWithoutExtension(OriginalFile.Name);
        FileExtension = Path.GetExtension(OriginalFile.Name);
        string path = OriginalFile.ToString();
        PathLen = path.Length;
    }

    public string index { get; set; }
    public FileName(WorkBook workbook, string index)
    {
        WorkSheet sheet = workbook.WorkSheets.First();

        foreach (var cell in sheet[index])
        {
            string path = cell.ToString();
            OriginalFile = new FileInfo(path);
            OriginalPath = NewPath = OriginalFile.DirectoryName;
            OriginalFileName = NewFileName = path;
            PathLen = path.Length;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
  • Thank you very much. Is this something I should add to all of the properties in this file class? Wiring this up-- so to speak-- to work with WPF is still a lot of blindfolded trying things out for me. I haven't found any reading material that really steps one through the "what"s and "how"'s of EventHandlers, etc. It seems like a very simple thing, so it's hard to tell if most of these older posts are just a lot of extra code or an actual fix (the link provided by @Sach illustrates my frustrations. Looking forward to the days where I'm not cluttering this site with trivial stuff such as this. – icantseecolors Nov 22 '19 at 21:23
  • No, you have to add this only for properties that you, for example, using on view and you want to refresh it after a change or you want to do something when this property has been changed. EventHandlers is a specific concept when I'm was learning a WPF this was very strange for me also. – pkantorowicz Nov 22 '19 at 21:41
  • I greatly appreciate it. In the instance you've provided, does that OnPropertyChanged method actually connect to anything yet? I'd provide updated code to show where I've landed with that info to illustrate where a lot of the disconnects currently are for me, but i'm not sure if I do that by simply editing my question or providing info that is far to many chars here. @pkantorowicz – icantseecolors Nov 22 '19 at 23:04
  • Maybe you can edit your question and paste the code below the previous version. – pkantorowicz Nov 23 '19 at 00:01
  • Done! @pkantorowicz And, I guess as a side question, to you, how did you dive into WPF and event handlers in your own experience? – icantseecolors Nov 23 '19 at 00:51
  • How would I call OnPropertyChanged in xaml.cs? @pkantorowicz – icantseecolors Nov 25 '19 at 20:36
  • @icantseecolors I didn't understand the first question about disconnects. For the second question, the answer is as much as I'm needed, PropertyChanged() it's WPF concept I'm didn't see WPF application without it, you can write your own event handlers when you have to get a notification about changes. Why you want use PropertyChanged() in XAML ? This example shows how to do that - https://stackoverflow.com/questions/3505716/how-to-use-inotifypropertychanged-correctly-in-wpf-xaml – pkantorowicz Nov 26 '19 at 09:19
  • Basically, I see the code, I just don't see it doing anything and still don't know how to proceed forward as there still seems to be a billion convoluted ways to bind data, or just handle it with event handlers. Again, new to eventhandlers.. they almost feel like dealing with pointers. – icantseecolors Dec 03 '19 at 00:01