Create a SearchCommand in wpf using Mvvm that load

2020-05-07 02:09发布

I could use some help, trying to understand how the commands works in Wpf with Mvvm and how you bind a button to it. Have read a lot articles about commands and wpf, but they all seem to be different.

I have created a wpf project using mvvm pattern. Have a list view that will get filled with customers.

Now I want to make a search command, so I can change the data in the list view.

CustomerViewModel

public class CustomerViewModel : INotifyPropertyChanged, ICommand
{
    private List<Customer> CustomerList;

    public CustomerViewModel()
    {
        CustomerList = LoadCustomerData();
    }

    public List<Customer> LoadCustomerData()
    {
        List<Customer> data = new List<Customer>();
        data.Add(new Customer() { Id = 1, FirstName = "Alexander", LastName = "Malkovich", PhoneNumber = "43215678", Email = "AlexanderMalkivich@gmail.com" });
        data.Add(new Customer() { Id = 2, FirstName = "Mikkel", LastName = "Larsen", PhoneNumber = "87654321", Email = "MikkelLarsen@gmail.com" });

        return data;
    }

    public List<Customer> CustomerDataLoadedOnSearch()
    {
        List<Customer> data = new List<Customer>();
        data.Add(new Customer() { Id = 1, FirstName = "Ulrik", LastName = "Trissel", PhoneNumber = "35325235", Email = "UlrikTrissel@gmail.com" });
        data.Add(new Customer() { Id = 2, FirstName = "Michael", LastName = "Mortensen", PhoneNumber = "14214124", Email = "Michael@gmail.com" });

        return data;
    }

    public List<Customer> Customer
    {
        get
        {
            return CustomerList;

        }

    }

    // Search Command


    #region INotifyPropertyChanged members

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

}

Basically I run the LoadCustomerData() in the ctor, and gets the CustomerList with the get method.

I'm trying to create a search command, that will run the other method (CustomerDataLoadedOnSearch), and then trigger the get call again in the view.

XML view code:

<StackPanel Orientation="Vertical">
            <Button Command="{Binding SearchCommand}" Padding="15, 5" HorizontalAlignment="Right">Search</Button>
        </StackPanel>
        <GroupBox Header="Customers">
            <StackPanel Margin="10, 10" Orientation="Vertical">
                <ListView Height="250" Width="Auto" ItemsSource="{Binding Customer}" Grid.Row="1">
                    <ListView.View>
                        <GridView >
                            <GridViewColumn Header="ID" Width="Auto">
                                <GridViewColumn.CellTemplate>
                                    <DataTemplate>
                                        <TextBlock Text="{Binding Id}" TextAlignment="Right" Width="40"/>
                                    </DataTemplate>
                                </GridViewColumn.CellTemplate>
                            </GridViewColumn>
                            <GridViewColumn DisplayMemberBinding="{Binding FirstName}" Header="First name" Width="100"/>
                            <GridViewColumn DisplayMemberBinding="{Binding LastName}" Header="Last name" Width="100"/>
                            <GridViewColumn DisplayMemberBinding="{Binding PhoneNumber}" Header="Phone" Width="100"/>
                            <GridViewColumn DisplayMemberBinding="{Binding Email}" Header="Email" Width="200"/>
                        </GridView>
                    </ListView.View>
                </ListView>
            </StackPanel>
        </GroupBox>

Have binded a command called SearchCommand. Now I'm not so sure what to do next. Should I create a folder called Command and keep all commands in there for all my buttons I make?

Or should I keep all the ICommand code in the ViewModel? Hope someone can show me how this works.

Good day.

Update:

So far, thanks for your replys.

I managed to implement the ViewModelBase & RelayCommand class. Futhermore I have implemented the Icommand of the search button, but when i click it, it clears the list (the view loses its customers).

void SearchCommand_DoWork(object obj)
{
     CustomerList.Clear();
     CustomerList = CustomerDataLoadedOnSearch();            
}

Have debugged the CustomerList and when I call the CustomerDataLoadedOnSearch, it have two rows with new customers inside, but the list hasen't been populated.

标签: wpf mvvm
1条回答
贼婆χ
2楼-- · 2020-05-07 02:29

Let me try to answer this and I hope this will clear things a little bit for you.

As sexta13 said there is some frameworks you can use. Personally I don't like them at all.

What I instead do is to implement RelayCommand, which is the easiest solution in my opinion.

RelayCommand:

public class RelayCommand : ICommand
{
    readonly Action<object> _execute;
    readonly Func<bool> _canExecute;

    public RelayCommand(Action<object> execute, Func<bool> canExecute = null)
    {
        if (execute == null)
            throw new ArgumentNullException(nameof(execute));

        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute.Invoke();
    }

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

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

Next to that I implement ViewModelBase which looks like this:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        VerifyPropertyName(propertyName);

        var handler = PropertyChanged;
        if (handler == null) return;
        var e = new PropertyChangedEventArgs(propertyName);
        handler(this, e);
    }

    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    private void VerifyPropertyName(string propertyName)
    {
        // Verify that the property name matches a real,  
        // public, instance property on this object.
        if (TypeDescriptor.GetProperties(this)[propertyName] != null) return;
        var msg = "Invalid property name: " + propertyName;
        Debug.WriteLine(msg);
    }
}

With these two classes you are ready to go MVVM.

And back to your example:

First of all your private List<Customer> CustomerList; should be public and not private. And then it should have getter and setter. otherwise it will not be reachable by your View.

public List<Customer> CustomerList{get; private set}

When this is said, then I will suggest that you will use ObservableCollection<T> instead of a List<T>. Observable collections implements INotifyPropertyChanged, and every time your collection has changed, your view will know. If you use List<T> then you have to tell your view manually that the collection has changed.

When this is done then you can create a command in your VM:

public ICommand SearchCommand{get;set;}

public CustomerViewModel()
{
    CustomerList = LoadCustomerData();
    SearchCommand = new RelayCommand(SearchCommand_DoWork, ()=>false);
}

void SearchCommand_DoWork(object obj)
{
   CustomerList.Clear();
   CustomerList.Add(New Customer()); //Or iterate the search list to your collection.
}

The ()=>false part of the assignment of the command is optional. if you leave it blank then you search button will be enabled. if you like this example use ()=>false it will always be disabled. You can use another attribute or delegate which returns a boolean.

查看更多
登录 后发表回答