Question

All, Binded complete Model, values are getting displayed in controls but not able to get button click working... any suggestion? what I m missing or doing wrong ? Thanks

<Window x:Class="test" Title="test" Height="350" Width="525">
    <StackPanel Name="abc" Orientation="Vertical" DataContext="{Binding Path=EMP, Mode=TwoWay}" Margin="4" Height="153">
        <Label Content="Last Name:" Margin="0,0,4,0"/>
        <TextBox Width="250" Text="{Binding Path=LastName}" Height="20"/>
        <Button Grid.Row="2" Margin="0,0,4,0" Height="40" Width="40" 
                 Command="{Binding Path=SaveCommand}" />
    </StackPanel>
</Window>

class EmployeeVM: ViewModelBase
{
    private bool _Execute = true;
    public EmployeeVM()
    {
        emp = new Model.Employee { FirstName = "abc", LastName = "xyz" };
    }
    private string sFirstName;
    private string sLastName;
    private Model.Employee emp;

    public Model.Employee EMP
    {
        get{return emp;}
        set{emp = value;
            OnPropertyChanged("EMP");}
    }
    public string LastName
    {
        get { return sLastName; }
        set 
        { 
            sLastName = value;
            OnPropertyChanged("LastName");
        }
    }

    #region Commands
    private ICommand _SaveCommand;
    public ICommand SaveCommand
    {
        get
        {
            return _SaveCommand = new CommandHandler(Save, _Execute);
        }
    }
    #endregion

    private void Save(object param)
    {
        ObservableCollection<Model.Employee> newIM = new ObservableCollection<Model.Employee>();
        foreach(Model.Employee e in newIM)
        {
            string a = e.FirstName;
            string b = e.LastName;
        }
    }
}


public class CommandHandler : ICommand
{
    Action<object> _act;
    bool _canExecute;

    public CommandHandler(Action<object> act, bool canExecute)
    {
        _act = act;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _act(parameter);
    }
}
Was it helpful?

Solution

You can write your own command.

Here is the baseclass I use for my commands.

It has some very basic things that make life easier.

  • the Execute method accepts an object, so you will be able to pass arrays
  • a viewmodel can be easily passed in, that is the one you will work with in your commands (this is most of the time the case, swipe it out if you do not need that)
  • the changed handler leverages the CommandManager. This is really very helpful

Perhaps you want to change some things. All I have added is in there because it is very helpful. (especially the viewmodel)

public abstract class CommandBase : ICommand
{
    public abstract bool CanExecute(object o);
    public abstract void Execute(object o);

    public PropertyChangedBase ViewModel { get; set; }

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

You would have implementations like

public class ExampleCommand : CommandBase
{
    public ExampleCommand (PropertyChangedBase viewModel)
    {
        this.ViewModel = viewModel;
    }

    public override void Execute(object o)
    {
        // something like
        var settings = UnityContainer.Resolve<ISettings>();
        settings.MagicValue = (this.ViewModel as ConcreteViewModel).MagicValue;
    }

    public override bool CanExecute(object o)
    {
        return true;
    }
}

in your ViewModel, you expose the command to the view by having a property:

public class ExampleViewModel : PropertyChangedBase
{
    public ExampleViewModel ()
    {
        this.DoThisAndThatCommand = new ExampleCommand(this);
    }

    public CommandBase DoThisAndThatCommand { get; set; }
}


// and in XAML, you can use it like
<Button x:Name="Ok"
            Command="{Binding DoThisAndThatCommand }" />

(Given you have connected the ViewModel and the View correctly by setting the DataContext of the View)

Now, whenever the Button is clicked, the Execute Method of the Command will get called.

You have your ViewModel right in the Command, so you can easily work with it.

It is very unusual to have a button inside a command or inside a ViewModel. The trick about MVVM is to separate the View from the ViewModel and to not have UIElements in the ViewModel.

If you do not have the PropertyChangedBase (this one comes with Caliburn.Micro) then I would suggest to use some easy INotifyPropertyChanged implementation.

I found this one here, should be german though

public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged { #region < INotifyPropertyChanged > Members

    /// <summary>
    /// Is connected to a method which handle changes to a property (located in the WPF Data Binding Engine)
    /// </summary>

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raise the [PropertyChanged] event
    /// </summary>
    /// <param name="propertyName">The name of the property</param>

    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    #endregion

    private Dictionary<string, object> propertyValueStorage;


    #region Constructor

    public NotifyPropertyChangedBase()
    {
        this.propertyValueStorage = new Dictionary<string, object>();
    }


    #endregion



    /// <summary>
    /// Set the value of the property and raise the [PropertyChanged] event
    /// (only if the saved value and the new value are not equal)
    /// </summary>

    /// <typeparam name="T">The property type</typeparam>
    /// <param name="property">The property as a lambda expression</param>
    /// <param name="value">The new value of the property</param>

    protected void SetValue<T>(Expression<Func<T>> property, T value)
    {
        LambdaExpression lambdaExpression = property as LambdaExpression;

        if (lambdaExpression == null)
        {
            throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
        }

        string propertyName = this.getPropertyName(lambdaExpression);

        T storedValue = this.getValue<T>(propertyName);

        if (!object.Equals(storedValue, value))
        {
            this.propertyValueStorage[propertyName] = value;

            this.OnPropertyChanged(propertyName);
        }
    }


    /// <summary> Get the value of the property </summary>
    /// <typeparam name="T">The property type</typeparam>
    /// <param name="property">The property as a lambda expression</param>
    /// <returns>The value of the given property (or the default value)</returns>
    protected T GetValue<T>(Expression<Func<T>> property)
    {
        LambdaExpression lambdaExpression = property as LambdaExpression;

        if (lambdaExpression == null)
        {
            throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
        }

        string propertyName = this.getPropertyName(lambdaExpression);
        return getValue<T>(propertyName);
    }

    /// <summary>
    /// Try to get the value from the internal dictionary of the given property name
    /// </summary>
    /// <typeparam name="T">The property type</typeparam>
    /// <param name="propertyName">The name of the property</param>
    /// <returns>Retrieve the value from the internal dictionary</returns>

    private T getValue<T>(string propertyName)
    {
        object value;

        if (propertyValueStorage.TryGetValue(propertyName, out value))
        {
            return (T)value;
        }
        else
        {
            return default(T);
        }
    }


    /// <summary>
    /// Extract the property name from a lambda expression
    /// </summary>
    /// <param name="lambdaExpression">The lambda expression with the property</param>
    /// <returns>The extracted property name</returns>
    private string getPropertyName(LambdaExpression lambdaExpression)
    {
        MemberExpression memberExpression;

        if (lambdaExpression.Body is UnaryExpression)
        {
            var unaryExpression = lambdaExpression.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambdaExpression.Body as MemberExpression;
        }

        return memberExpression.Member.Name;
    }
} 

It is very easy to use!

In your ViewModel you have to provide public properties for Binding (this is VERY important) and fire a change notification.

Here is an example of how to use that basic implementation of INPC (INotifyPropertyChanged)

public class LoginViewModel : NotifyPropertyChangedBase
{
    public string UserName { get;set; }
}

This INPC implementation makes the NotifyOfPropertyChange call for you, you do not have to care for it! But you will have to inspect what fits your case best.

In your Question you already have a ViewModelBase. Perhaps you want to use this one instead of the above.

OTHER TIPS

Please try to write in English, because I was confused what you wrote(such as "If u c above", "b/c", and so on :P..)

Anyway, as for your problem, this should fix it:

<UserControl.Resources>
        <C:MultiValueConverter x:Key="MultiParamConverter"></C:MultiValueConverter>
    </UserControl.Resources>

    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <Button Name="Expander" Content="+" Width="25" Margin="4,0,4,0" Command="{Binding ExpanderCommand}">
                <Button.CommandParameter>
                    <MultiBinding Converter="{StaticResource MultiParamConverter}">
                        <Binding ElementName="Content"/>
                        <Binding ElementName="Expander"/>
                    </MultiBinding>
                </Button.CommandParameter>
            </Button>
            <Label FontWeight="Bold">GENERAL INFORMATION</Label>
        </StackPanel>
        <StackPanel Name="Content" Orientation="Vertical" Visibility="Collapsed">
            <Label>Test</Label>
        </StackPanel>
    </StackPanel>

Command:

public ICommand ExpanderCommand
        {
            get
            {
                return new RelayCommand(delegate(object param)
                    {
                        var args = (object[])param;
                        var content = (UIElement)args[0];
                        var button = (Button)args[1];
                        content.Visibility = (content.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
                        button.Content = (content.Visibility == Visibility.Visible) ? "-" : "+";
                    });
            }
        }

and the converter:

public class MultiValueConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return values.ToArray();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException("No two way conversion, one way binding only.");
        }
    }

First of all, your view model class should either be a DependencyObject, or implementing INotifyPropertyChanged interface. You can always find a suitable MVVM library out there and use their base view model class.

Judging from your XAML that your CheckBox can bind to the same context of your button. So, while you are binding your buttonGetDetails into the ClickCommand you can also bind your chkDuplicates into a view model property, lets say CheckDuplicates. Therefore you won't need this as a parameter to your command, because the property will already be inside your view model. Such as:

class TestViewModel : ViewModelBase
{
    bool checkDuplicates;
    public bool CheckDuplicates
    {
        get { return checkDuplicates; }
        set 
        {
            if(checkDuplicates != value)
            {
                checkDuplicates = value;
                OnPropertyChanged("CheckDuplicates");
            }
         }
    }

    //Everything else is same as before
    //  except the action
    public void AnyAction(object param)
    {
        //no need for param anymore
        //var parmValues = (Object)param;
        bool test = this.CheckDuplicates;
    }        
}

Since this is supposed to model your view, you can get rid of any parameters of your command binding and make them part of your view model.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top