将模型绑定到View但按钮单击工作

时间:2013-05-20 17:11:08

标签: c# mvvm icommand

全部, Binded complete Model,值将显示在控件中,但无法让按钮单击工作...任何建议?我错过了什么或做错了什么?感谢

<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);
    }
}

3 个答案:

答案 0 :(得分:0)

您可以编写自己的命令。

这是我用于命令的基类。

它有一些非常基本的东西可以让生活更轻松。

  • Execute方法接受一个对象,因此您将能够传递数组
  • 一个viewmodel可以很容易地传入,也就是你将在你的命令中使用的那个(这种情况大多数情况下,如果你不需要那么就把它擦掉)
  • 更改的处理程序利用CommandManager。这真的很有帮助

也许你想改变一些事情。我添加的所有内容都在那里,因为它非常有用。 (尤其是视图模型)

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; }
    }
}

你会有像

这样的实现
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;
    }
}
在ViewModel中,通过使用属性

将命令公开给视图
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 }" />

(通过设置ViewModel的{​​{1}},您已正确关联ViewDataContext

现在,只要单击Button,就会调用Command的Execute方法。

您的ViewModel位于View,因此您可以轻松使用它。

在命令内或Command内有一个按钮是非常罕见的。 关于MVVM的诀窍是将ViewModelView分开并且没有 ViewModel中的UIElements

如果您没有PropertyChangedBase(这个附带Caliburn.Micro),那么我建议使用一些简单的INotifyPropertyChanged实现。

I found this one here, should be german though

公共抽象类NotifyPropertyChangedBase:INotifyPropertyChanged     {         #region&lt; INotifyPropertyChanged&gt;成员

ViewModel

它非常易于使用!

在您的ViewModel中,您必须为Binding (这非常重要)提供公共属性,并触发更改通知。

以下是如何使用INPC的基本实现(INotifyPropertyChanged)

的示例
    /// <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;
    }
} 

这个INPC实现为你做了NotifyOfPropertyChange调用,你不必关心它!但你必须检查最适合你情况的东西。

在您的问题中,您已经拥有ViewModelBase。也许你想用这个而不是上面的。

答案 1 :(得分:0)

请尝试用英文写作,因为我对你所写的内容感到困惑(例如“如果你在上面”,“b / c”等等:P ..)

无论如何,至于你的问题,这应该解决它:

<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>

命令:

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) ? "-" : "+";
                    });
            }
        }

和转换器:

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.");
        }
    }

答案 2 :(得分:0)

首先,您的视图模型类应该是DependencyObject或实现INotifyPropertyChanged接口。您总是可以在那里找到合适的MVVM库并使用它们的基本视图模型类。

从您的XAML判断您的CheckBox可以绑定到按钮的相同上下文。因此,当您将buttonGetDetails绑定到ClickCommand时,您也可以将chkDuplicates绑定到视图模型属性中,让我们说CheckDuplicates。因此,您不需要将此作为命令的参数,因为该属性已经在视图模型中。如:

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;
    }        
}

由于这应该为您的视图建模,您可以删除命令绑定的任何参数,并使它们成为视图模型的一部分。