-1

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.

DelusionX
  • 79
  • 8
  • I don't understand the question. The `FuncBrowseCommand()` doesn't seem to do anything that would attempt to modify the `FilesFilePath` property. You also seem confused about how the command parameter works; you are changing the property values for the `param` object, but this has no useful effect. The object being modified was created by the converter for input to the command, nothing else. There's no other code that references that object, so modifying its properties doesn't do anything for any other code. Some other concerns: there's way too much code here; please provide a [mcve], both ... – Peter Duniho Nov 09 '20 at 20:05
  • ... **minimal** and **complete**. The above appears to possible be complete, but it's definitely not minimal. There seems to be a lot of stuff there not strictly part of the question. Finally, this comment in your post is both unnecessary and misdirected: _"I am more than willing to provide in the comments any additional info if something was unclear to you"_. The community takes as granted that authors of questions will provide clarifications when needed; but ... – Peter Duniho Nov 09 '20 at 20:05
  • ... those clarifications must _not_ be in the comments. We use the comments to request clarification, but the actual improvements must be made in the question post itself. – Peter Duniho Nov 09 '20 at 20:05
  • @PeterDuniho The ```FuncBrowseCommand``` does not affect the ```FilesFilePath``` text indeed. Although the ```BrowseFile()``` method does modify that property. I have included that method in the last part of my code snippet. Indeed the code is a bit long. I wanted to make a reproducible example. I am indeed a bit confused with the use of param.property because it does seem to affect the UI. – DelusionX Nov 09 '20 at 20:19

1 Answers1

1

The reason why your code doesn't work is that you're never setting the FilesFilePath property.

In your BrowseFile method, you should set FilesFilePath if the result is true:

        //Browse function used in Browse ICommand
        public bool BrowseFile(string filepathselected)
        {
            ...

            // Get the selected file name and display in a TextBox.
            // Load content of file in a TextBlock
            if (result == true)
            {
                filepathselected = openFileDlg.FileName;

                // Set the FilesFilePath property here!
                FilesFilePath = filepathselected;

                Debug.WriteLine("2. File Path: " + filepathselected);
            }
            return (bool)result;
        }
Keithernet
  • 2,349
  • 1
  • 10
  • 16
  • Keithernet I rechecked your solution and I have one question. Since I define the value of ```FilesFilePath``` in the multivalue converter `````` and I give it as value in ```BrowseFile()``` function. Why my approach does not work and yours will work? We do basically the same thing. But I provide the value of the MVVM object outside of the function. While you provide it inside. I believe your approach is no different from mine. Because I want to use ```IMultiValueConverter``` to bound a command.parameter to my MVVM object. – DelusionX Nov 10 '20 at 07:26
  • In a multi value converter the bindings are one way. Essentially they are just parameters for the converter and nothing else. – Keithernet Nov 10 '20 at 14:45
  • Keithernet so is there any way to affect the value of the ```FilesFilePath``` when is give as a command.parameter in ```MultiValueConverter?``` I think this summarizes my question – DelusionX Nov 10 '20 at 15:11
  • In short no. But I think you need to ask yourself why you're trying to do this? A converter takes one or more values in, converts, and outputs a new value. It isn't designed to update state in your view model. – Keithernet Nov 10 '20 at 17:29
  • Keithernet based on my question I need to find a workaround. Because I apply the ICommand three times. And I don't want to create three I Commands ('''BrowseButtonCommand'''). But instead have a single ICommand that will take as parameter a string connected to an MVVM object and would assign it's time a different value. It's imperative to be able to change the value of the MVVM object through the ICommand. – DelusionX Nov 10 '20 at 18:45