how do I do that?
It's not clear what it is you are actually asking. Do you want to know how to copy files? Or do you want to know how to implement the user commands in the context of WPF?
Given that you put the wpf tag on your post, I am going to assume the latter.
The code you posted so far has a couple of serious problems, in the context of WPF, both related to the lack of the use of WPF's binding features:
- You are using the UI elements, such as
txtSource
and txtTarget
, to store the results of the user interaction.
- You are handling the
Click
event directly.
Both of these problems may not prevent the program from being able to work, but they do constrain the implementation unnecessarily. You can improve your efficiency and the quality of the program by isolating the UI aspects from the underlying implementation of the program. That means:
- Declaring a view model to hold all of the non-UI program state.
- Declaring the layout of your UI in XAML.
- Declaring
ICommand
implementations for handling user commands
- Connecting all of the above via
"{Binding}"
markup elements in the XAML
Based on your requirements, here's a view model that would implement your program.:
class ViewModel : NotifyPropertyChangedBase
{
private string _sourceDirectory;
public string SourceDirectory
{
get => _sourceDirectory;
set => _UpdateField(ref _sourceDirectory, value);
}
private string _targetDirectory;
public string TargetDirectory
{
get => _targetDirectory;
set => _UpdateField(ref _targetDirectory, value);
}
public ICommand SelectSourceCommand { get; }
public ICommand SelectTargetCommand { get; }
public ICommand CopyFilesCommand { get; }
public ViewModel()
{
SelectSourceCommand = new DelegateCommand(() => _SelectDirectory(SourceDirectory, s => SourceDirectory = s));
SelectTargetCommand = new DelegateCommand(() => _SelectDirectory(TargetDirectory, s => TargetDirectory = s));
CopyFilesCommand = new DelegateCommand(() => _CopyFiles(SourceDirectory, TargetDirectory));
}
private void _SelectDirectory(string initial, Action<string> selected)
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
dialog.SelectedPath = initial;
if (dialog.ShowDialog() == DialogResult.OK)
{
selected(dialog.SelectedPath);
}
}
private void _CopyFiles(string source, string target)
{
Directory.CreateDirectory(target);
foreach (string file in Directory.GetFiles(source))
{
File.Copy(file, Path.Combine(target, Path.GetFileName(file)));
}
}
}
Note that this view model doesn't have anything in it that's specific to the UI. In fact, you can often write the entire view model before any of the UI has been written. When done correctly, you can use the same view model type in other programs, regardless of the user interface API that they use (e.g. Winforms, console program, web frameworks like Blazor or React, you can even port it to JavaScript with relatively little effort).
With the view model in hand, then you just write up the UI in XAML, using whatever layout you prefer:
<Window x:Class="TestSO64787525FileCopyButton.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="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:l="clr-namespace:TestSO64787525FileCopyButton"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<l:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.Resources>
<p:Style TargetType="Button">
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Margin" Value="2 0"/>
</p:Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Source directory: " Grid.Row="0" Grid.Column="0"/>
<TextBlock Text="Target directory: " Grid.Row="1" Grid.Column="0"/>
<TextBlock Text="{Binding SourceDirectory}" Grid.Row="0" Grid.Column="1"/>
<TextBlock Text="{Binding TargetDirectory}" Grid.Row="1" Grid.Column="1"/>
<Grid Grid.Row="2" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="Select Source Directory" Command="{Binding SelectSourceCommand}"/>
<Button Grid.Row="0" Grid.Column="1" Content="Select Target Directory" Command="{Binding SelectTargetCommand}"/>
<Button Grid.Row="0" Grid.Column="2" Content="Copy Directory" Command="{Binding CopyFilesCommand}"/>
</Grid>
</Grid>
</Window>
Note that in the above, the view model properties are bound to UI elements using the "{Binding}"
syntax. The two string
values get bound to TextBlock
elements for display of that text, while the ICommand
values get bound to the Command
property of the buttons, so that the defined commands are executed when the appropriate button is clicked.
Notes:
- I didn't want to bother installing the Windows API Code Pack library, so I switched your code to use the older
FolderBrowserDialog
type…the basic concepts are identical though.
- I didn't bother showing the "move" operation. That's simply a matter of calling
File.Move()
instead of File.Copy()
, so I assume it's trivial for you to extend on your own the example above to include that as well.
Finally, I will note that the above strategy is a lot easier when you rely on some base classes to handle the boilerplate stuff, like implementing INotifyPropertyChanged
and ICommand
. Here are the classes I used for that in the above example:
class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void _UpdateField<T>(ref T field, T newValue,
Action<T> onChangedCallback = null,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, newValue))
{
return;
}
T oldValue = field;
field = newValue;
onChangedCallback?.Invoke(oldValue);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class DelegateCommand<T> : ICommand
{
private readonly Action<T> _execute;
protected readonly Func<T, bool> _canExecute;
public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public virtual bool CanExecute(object parameter) => _canExecute != null ? (parameter is T t ? _canExecute(t) : false) : true;
public void Execute(object parameter) => _execute((T)parameter);
}
class DelegateCommand : DelegateCommand<object>
{
public DelegateCommand(Action execute, Func<bool> canExecute = null)
: base(o => execute(), canExecute != null ? (Func<object, bool>)(o => canExecute()) : null)
{ }
public override bool CanExecute(object parameter) => _canExecute?.Invoke(null) ?? true;
}