Nicholas Aysen
Nicholas Aysen

Reputation: 580

How to show data from selected item in listbox wpf using mvvm?

I am just doing some practice on WPF & MVVM forms So far, I have just completed the normal Listbox with data loaded from an array, that when the index changes, it loads other data into textblocks on the form

I would like to change this example however. I want the user to select a field in the listbox, then click a button to display all the other data

So, the form is (usercontrol): enter image description here And when someone selects, say Cena, they will see the data on the right filled out.

My code for the .xaml is:

<UserControl x:Class="SuperstarsRoster.RosterView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:SuperstarsRoster"
         xmlns:WpfToolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="600">

<UserControl.DataContext>
    <local:RosterViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White" >
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="250"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <!--SelectedItem="{Binding SelectedPerson}"-->
    <ListBox Grid.Column="0" Margin="0"
              ItemsSource="{Binding RosterList}"
              DisplayMemberPath="Name"
              Name="lstRoster"
              Height="250"
              VerticalAlignment="Top"/>
    <Button Content="Exit" Height="23" HorizontalAlignment="Left" VerticalAlignment="Bottom" Name="btnExit" Width="150" Command="{Binding ButtonCommand}" CommandParameter="Hai" />
    <Grid x:Name="PersonDetails" Grid.Column="1" DataContext="{Binding SelectedPerson}" Margin="5">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="150"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Grid.ColumnSpan="2" Text="Person Details" FontSize="15"/>

        <TextBlock Grid.Row="1" Grid.Column="0" Text="Name"/>
        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Name,Mode=TwoWay}"/>

        <TextBlock Grid.Row="2" Grid.Column="0" Text="Brand"/>
        <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Brand,Mode=TwoWay}"/>

        <TextBlock Grid.Row="3" Grid.Column="0" Text="Type"/>
        <TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding Type,Mode=TwoWay}"/>

        <Button Grid.Row="4" Content="Choose" Height="23" HorizontalAlignment="Left" VerticalAlignment="Bottom" Name="btnChoose" Width="90" Command="{Binding ChooseCommand}" CommandParameter="{Binding ElementName=lstRoster,Path=SelectedPerson}" />
    </Grid>
</Grid>

my problem is the Show button:

<Button Grid.Row="4" Content="Choose" Height="23" HorizontalAlignment="Left" VerticalAlignment="Bottom" Name="btnChoose" Width="90" Command="{Binding ChooseCommand}" CommandParameter="{Binding ElementName=lstRoster,Path=SelectedPerson}" />

I don't know what to do here. I don't know if the CommandParameters should be empty, or have something in it.

My ViewModel code is:

#region Show Button

    private ICommand m_ShowCommand;
    public ICommand ShowCommand
    {
        get
        {
            return m_ShowCommand;
        }
        set
        {
            m_ShowCommand = value;
        }
    }

    public void Selected(object obj)
    {
        RaisePropertyChanged("SelectedPerson");
    }

    #endregion

I have a feeling my Selected is the incorrect piece of code. On the ViewModel code there is also:

 #region Roster Details

    public ObservableCollection<Roster> rosterList;
    public Roster selectedPerson;


    public RosterViewModel()
    {

        ButtonCommand = new RelayCommand(new Action<object>(ShowMessage));
        ShowCommand = new RelayCommand(new Action<object>(Selected));
        rosterList = new ObservableCollection<Roster>()
        {
            new Roster(){Name="Batista", Brand="Smackdown!", Type="Heel"},
            new Roster(){Name="The Rock", Brand="RAW", Type="Face"},
            new Roster(){Name="John Cena", Brand="RAW", Type="Face"},
            new Roster(){Name="Randy Orton", Brand="Smackdown!", Type="Heel"}
        };

        RaisePropertyChanged("SelectedPerson");
    }

    #endregion

With the ShowCommand being the button event, but I don't know what to put here.

Ideally, user selects Cena, clicks show, and the textblocks will fill with the data from the array.

My RelayCommand class (the default for all button clicks) looks like so:

class RelayCommand : ICommand
{
    private Action<object> _action;

    public RelayCommand(Action<object> action)
    {
        _action = action;
    }

    #region ICommand Members

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

    public event EventHandler CanExecuteChanged;

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

    #endregion

Should there be something more here? Or in the ViewModel?

I'm using MVVM, so there is no code in my code behind class.

The Exit button works, that's why I know the commands will work.

EDIT

I changed some of the code now. Firstly, the show button was sitting in a grid that had a datacontext setting. That was taken out. Next, both buttons share the same command (ButtonCommand), but both have different parameters. Exit is "Exit", and choose is "Hai"

In the ViewModel code, the following was added and changed

 public string Name { get; set; }
    public string Brand { get; set; }
    public string Type { get; set; }

    #region Button Work
    private ICommand m_ButtonCommand;
    public ICommand ButtonCommand
    {
        get
        {
            return m_ButtonCommand;
        }
        set
        {
            m_ButtonCommand = value;
        }
    }

    public void DoAction(object obj)
    {
        if (obj.ToString() == "Hai")
        {
            if (selectedPerson != null)
            {
                this.Name = selectedPerson.Name;
                this.Brand = selectedPerson.Brand;
                this.Type = selectedPerson.Type;
                RaisePropertyChanged(null);
            }                
        }
        if (obj.ToString() == "Exit")
        {
            MessageBox.Show("This program will now close");
            Environment.Exit(0);
        }
    }
    #endregion

This way, data is set and I can populate the fields when wanted.

Along with initializing it first

 public RosterViewModel()
    {
        ButtonCommand = new RelayCommand(new Action<object>(DoAction));

So, it works now.

enter image description here

Upvotes: 0

Views: 4871

Answers (1)

Mashton
Mashton

Reputation: 6415

You have a couple of choices really:

(1) Have a VM property that is bound to the SelectedItem of your ListBox, and then you don't need to send any parameters with your button command ... you can just perform your ChooseCommand on the currently SelectedItem (as known by you VM).

or

(2) Pass in the SelectedItem as a parameter on your command, like this ...

<Button Grid.Row="4" Content="Choose" Height="23" HorizontalAlignment="Left"
    VerticalAlignment="Bottom" Name="btnChoose" Width="90"
    Command="{Binding ChooseCommand}" 
    CommandParameter="{Binding ElementName=MyListBox,Path=SelectedItem}" />

Note that you will need to give your ListBox a name (x:Name="MyListBox") and that your CommandParameter is referring to that ListBox, and its SelectedItem property.

Upvotes: 1

Related Questions