I have two button commands. Let's call them BrowseButton1
and LoadButton1
. The first command browses a file path (MVVM object) and sets this to a local path of my machine. Then the second ICommand (a.k.a LoadButton1
) uses that file path (same MVVM object) to load some data to an SQL table.
My problem is that I cannot use the value of the file path in the second ICommand, because it is returned null
XAML code
<Window x:Class="TestEnvironment.MainWIndowTestStackOverflow"
x:Name="MainWindowTestStackOverflow"
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:TestEnvironment"
mc:Ignorable="d"
Height="720"
Width="1145"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
BorderBrush="Black"
BorderThickness="1.5,1.5,1.5,1.5"
WindowStyle="None">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid x:Name="GridMain"
Width="1145"
Background="White"
HorizontalAlignment="Center"
ShowGridLines="False"
Grid.Row="1">
<!--Grid Columns-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0"/>
<ColumnDefinition Width="195"/>
<ColumnDefinition Width="295"/>
<ColumnDefinition Width="650"/>
<ColumnDefinition Width="0"/>
</Grid.ColumnDefinitions>
<!--Grid Rows-->
<Grid.RowDefinitions>
<RowDefinition Height="0"/>
<RowDefinition Height="45"/>
<RowDefinition Height="45"/>
<RowDefinition Height="45"/>
<RowDefinition Height="45"/>
<RowDefinition Height="52"/>
<RowDefinition Height="400"/>
</Grid.RowDefinitions>
<TextBox
Name="FileNameTextBox"
Text="{Binding Path=FilesFilePath}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="5,0,0,0"
IsReadOnly="True"
FontStyle="Italic"
FontFamily="Arial"
FontSize="9"
BorderThickness="0"
Grid.Column="2"
Grid.Row="1"/>
<!--Apply ICommand to browse file 1st time-->
<Button
x:Name="BrowseButton1"
Content="Browse"
Command="{Binding Path=BrowseButtonCommand}"
IsEnabled="{Binding Path=EnableFilesBrowseButton, Converter={StaticResource BooleanToVisibilityConverter}}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="80"
Height="25"
Margin="40,0,0,0"
Padding="0"
FontWeight="Light"
FontSize="10"
Grid.Column="3"
Grid.Row="1"
Cursor="Hand">
<Button.CommandParameter>
<MultiBinding>
<MultiBinding.Converter>
<local:BrowseButtonConverter/>
</MultiBinding.Converter>
<Binding Path="FilesFilePath"/> //this is the value I want to exchange between the two ICommands
<Binding Path="EnableFilesBrowseButton"/>
<Binding Path="EnableFilesLoadButton"/>
<Binding Path="EnableFilesViewButton"/>
<Binding Path="FilesPanelVisibility"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
<Button
x:Name="LoadButton1"
Content="Load"
Command="{Binding Path=LoadButtonCommand}"
IsEnabled="{Binding Path=EnableFilesLoadButton, Converter={StaticResource BooleanToVisibilityConverter}}"
Focusable="False"
Width="80"
Height="25"
Margin="135,0,0,0"
FontSize="10"
FontWeight="Light"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Grid.Column="3"
Grid.Row="1"
Cursor="Hand">
<Button.CommandParameter>
<MultiBinding>
<MultiBinding.Converter>
<local:LoadButtonConverter/>
</MultiBinding.Converter>
<Binding Path="FilesFilePath"/> //this returns null even though browse button return the FilesFilePath
</MultiBinding>
</Button.CommandParameter>
</Button>
</Grid>
</Window>
.cs file
namespace TestEnvironment
{
//Command parameters -Browse Button
public class BrowseButtonCommandParameters
{
public string FilePathSelected { get; set; } //parameter 1
public bool EnableBrowseButton { get; set; } //parameter 2
public bool EnableLoadButton { get; set; } //parameter 3
}
//Browse - MultiValueConverter
class BrowseButtonConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Error handling omitted for brevity
// Casting omitted because question's code has insufficient context
return new BrowseButtonCommandParameters
{
FilePathSelected = (string)values[0],
EnableBrowseButton = (bool)values[1],
EnableLoadButton = (bool)values[2],
};
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("to-source binding mode not supported");
}
private static BrowseButtonConverter _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null) _converter = new BrowseButtonConverter();
return _converter;
}
public BrowseButtonConverter()
: base()
{
}
}
//Command parameters -Load Button
public class LoadButtonCommandParameters
{
public string FilePathSelected { get; set; } //parameter 1
}
//Load - MultiValueConverter
class LoadButtonConverter : MarkupExtension, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Error handling omitted for brevity
// Casting omitted because question's code has insufficient context
return new LoadButtonCommandParameters
{
FilePathSelected = (string)values[0], //this is actually the FilePath defined from Browse Command.
};
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("to-source binding mode not supported");
}
private static LoadButtonConverter _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null) _converter = new LoadButtonConverter();
return _converter;
}
public LoadButtonConverter()
: base()
{
}
}
//----------------------------------------------------------------------------------------------------
public class MainWindowViewModel : INotifyPropertyChanged
{
//MVVM objects bound to XAML objects and connected to ICommand parameters
//Used by BrowseButton1
//1.
private bool _enableFilesLoadButton;
public bool EnableFilesLoadButton
{
get
{
return _enableFilesLoadButton;
}
set
{
_enableFilesLoadButton = value;
OnPropertyChanged("EnableFilesLoadButton");
}
}
//2.
private bool _enableFilesBrowseButton;
public bool EnableFilesBrowseButton
{
get
{
return _enableFilesBrowseButton;
}
set
{
_enableFilesBrowseButton = value;
OnPropertyChanged("EnableFilesBrowseButton");
}
}
//3.
private string _FilesFilePath;
public string FilesFilePath
{
get
{
return _FilesFilePath;
}
set
{
_FilesFilePath = value;
OnPropertyChanged("FilesFilePath");
}
}
//----------------------------------------------------------------------------------------------------
//ICommand: BrowseButtonCommand
public ICommand BrowseButtonCommand
{
get { return new DelegateCommand<object>(FuncBrowseCommand); }
}
public void FuncBrowseCommand(object parameters)
{
var param = (BrowseButtonCommandParameters)parameters;
Nullable<bool> browse_result = BrowseFile(param.FilePathSelected); //command parameter 1, FilesFilePath
Debug.WriteLine("FilesFilePath " + FilesFilePath);
//Load button gets instantly disabled when every time the user clicks the Browse Button
param.EnableLoadButton = false; //command parameter 2
Debug.WriteLine("EnableLoadButton: " + EnableFilesLoadButton);
//Browse file
if (browse_result == true)
{
param.EnableLoadButton = true; //command parameter 2
Debug.WriteLine("EnableLoadButton: " + EnableFilesLoadButton);
param.EnableBrowseButton = true; //command parameter 3
Debug.WriteLine("EnableBrowseButton: " + EnableFilesBrowseButton);
}
else
{
return;
}
}
public void FuncLoadButton(object parameters)
{
var param = (LoadButtonCommandParameters)parameters;
Debug.Writeline("FilePath: "+ param.FilePathSelected); //this returns null
}
//Browse function used in Browse ICommand
public bool BrowseFile(string filepathselected)
{
// Create OpenFileDialog
OpenFileDialog openFileDlg = new OpenFileDialog();
// Launch OpenFileDialog by calling ShowDialog method
Nullable<bool> result = openFileDlg.ShowDialog();
Debug.WriteLine("1. browse window result: " + result);
// Set filter for file extension and default file extension
openFileDlg.DefaultExt = ".csv";
openFileDlg.Filter = "All files (*.*)|*.*|CSV file (*.csv)|*.csv|Text files (*.txt)|*.txt";
// Set initial directory
openFileDlg.InitialDirectory = @"C:\Documents\";
openFileDlg.Title = "Browse Files";
openFileDlg.CheckFileExists = true;
openFileDlg.CheckPathExists = true;
openFileDlg.RestoreDirectory = true;
// Multiple selection with all file types
openFileDlg.Multiselect = true;
// Get the selected file name and display in a TextBox.
// Load content of file in a TextBlock
if (result == true)
{
filepathselected = openFileDlg.FileName;
Debug.WriteLine("2. File Path: " + filepathselected);
}
return (bool)result;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
}
Both code snippets will help you to reproduce my problem. The thing is that neither FilesFilePath
command parameter can pass its value to Load Command, nor any of those changes in the DataContext can be viewed in MainWindow UI. Even though the Browse button gives value to parameter FilesFilePath
, the UI cannot show this text. Recall that property .Text
of FileNameTextBox
is bound to FilesFilePath
. So I want the UI to show that text also.
Summarizing I have two goals to achieve based on the snippet provided
- Pass the value of
FilesFilePath
(string) between the two ICommands. - View the value of
FilesFilePath
(string) in UI after using the Browse ICommand.
I am more than willing to provide in the comments any additional info if something was unclear to you.