Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Listbox selectionchanged MVVM

Tags:

c#

mvvm

wpf

I have a list box in my WPF application. I know how to use the selectionchanged event. However I am trying to follow the MVVM design. However I am not sure how to do this.

I have done this for a button however not sure if I can do the same thing?

<Button Grid.Column="0" Name="buttImport" 
    Content="Import File" 
    Command="{Binding CommandButtImport}" 
    Style="{StaticResource ButtonTemplate}"/>

 

public class ViewModel : INotifyPropertyChanged 
{       
    // for the button that imports the orders file
    public ICommand CommandButtImport { get; set; }

    public ViewModel() 
    {
        CommandButtImport = new MyCommands(
            ExecuteCommandButtImport,
            CanExecuteCommandButtImport);
    }

    private bool CanExecuteCommandButtImport(object parameter)
    {
        return true;
    }

    // import button
    private void ExecuteCommandButtImport(object parameter)
    {
      // some code
    }
}

EDIT Please ignore code above

I have changed my code so have reposted below what I currently have. I have a strange issue. The XAML - Main Code contains the code for my datagrid. The block below App - XAML contains the styling for most of my app, but is just a snipet.

Also

Code Line added beneath my datagrid in XAML - Main Code for testing purposes.

<ListBox ItemsSource="{Binding SelectedItem.DuplicateSecurities, ElementName=dataGridOrders}" 
 SelectedItem="{Binding SelectedItem.Security, ElementName=dataGridOrders}"/>

My datagrid loads fine. When I click on a row, the row expands to show a list of Securities. The issue I have is that in this listbox when I click on an item nothing happens. However the Listbox which I added beneath my datagrid for testing purposes does work. For example I click on one of the items and my row is updated, also the listbox in my row detail becomes selected. Its very strange why the listbox in my rowdetail doesn't work but the one beneath my datagrid does. Any ideas?

XAML - Main Code

<StackPanel>
        <!-- The data grid to display orders-->
        <DataGrid DataContext="{Binding OrderBlock}" 
                  x:Name="dataGridOrders" 
                  ItemsSource="{Binding Orders}"
                  Style="{StaticResource DataGridTemplate}"
                  ColumnHeaderStyle="{StaticResource DG_ColumnHeader}"                      
                  RowHeaderStyle="{StaticResource DG_RowHeader}"
                  RowStyle="{StaticResource DG_Row}"
                  CellStyle="{StaticResource DG_Cell}"                      
                  RowDetailsTemplate="{StaticResource DG_RowDetail}"                      
                  AutoGenerateColumns="False"
                  HorizontalAlignment="Stretch" 
                  VerticalAlignment="Stretch"
                  Background="Silver"
                  RowHeaderWidth="30"                      
                  Margin="25,5,20,15">                                                     

            <DataGrid.Columns>                    
                <DataGridComboBoxColumn Header="Action">
                    <DataGridComboBoxColumn.ElementStyle>
                        <Style TargetType="ComboBox">
                            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.StatusList}"/>
                            <Setter Property="IsReadOnly" Value="True"/>
                            <Setter Property="SelectedValue" Value="{Binding StatusGood}"/>
                        </Style>
                    </DataGridComboBoxColumn.ElementStyle>
                    <DataGridComboBoxColumn.EditingElementStyle>
                        <Style TargetType="ComboBox">
                            <Setter Property="ItemsSource" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=DataContext.StatusList}"/>
                            <Setter Property="IsReadOnly" Value="True"/>
                            <Setter Property="SelectedValue" Value="{Binding StatusGood}"/>
                        </Style>
                    </DataGridComboBoxColumn.EditingElementStyle>
                </DataGridComboBoxColumn>                    
                <DataGridTextColumn Header="Fund" Binding="{Binding Account}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Security ID" Binding="{Binding Security.ID}" IsReadOnly="True"/>
                <DataGridTextColumn Header="ThinkFolio Security ID" Binding="{Binding ThinkFolioSecurityID}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Security Name" Binding="{Binding Security.Name}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Buy/Sell" Binding="{Binding TransType}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Quantity" Binding="{Binding OrderQunatity, StringFormat=\{0:N0\}}" IsReadOnly="False"/>
                <DataGridTextColumn Header="Currency" Binding="{Binding BuyCurrency}" IsReadOnly="False"/>
                <DataGridTextColumn Header="Manager" Binding="{Binding FundManager}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Order Reason" Binding="{Binding OrderReason}" IsReadOnly="True"/>
                <DataGridTextColumn Header="Reject Reason" Binding="{Binding RejectReason}" IsReadOnly="True" Width="*"/>                    
            </DataGrid.Columns>
        </DataGrid>
        <ListBox ItemsSource="{Binding SelectedItem.DuplicateSecurities, ElementName=dataGridOrders}" SelectedItem="{Binding SelectedItem.Security, ElementName=dataGridOrders}"/>
        </StackPanel>

App XAML

  <!-- Row Detail Template for Data Grid -->
    <DataTemplate x:Key="DG_RowDetail">
        <Grid x:Name="RowDetailGrid"                  
              Margin="5"
              HorizontalAlignment="Left">
            <Border HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Width="500"
                    Height="80"
                    CornerRadius="5">
                <Border.Background>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                        <GradientStop Offset="0" Color="Transparent"/>
                        <GradientStop Offset="1" Color="Transparent"/>
                    </LinearGradientBrush>
                </Border.Background>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="2.5*"/>
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/> 
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Row="0"
                               Grid.ColumnSpan="3"
                               Margin="5,0,0,5"
                               HorizontalAlignment="Left"
                               FontSize="12"
                               FontWeight="Bold"
                               Foreground="Black"
                               Text="Select Security Identifier">
                    </TextBlock>
                    <ListBox Grid.Row="1" Grid.ColumnSpan="3" Name="lbIdentifier" ItemsSource="{Binding DuplicateSecurities}" SelectedItem="{Binding Security}"                                 
                             SelectionMode="Single" HorizontalContentAlignment="Stretch">
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <Grid Margin="0,2">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
                                    <TextBlock Grid.Column="0" Text="{Binding Path=ID}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
                                    <TextBlock Grid.Column="1" Text="{Binding Path=Name}" FontSize="10" HorizontalAlignment="Left" Margin="5,0,0,0"/>
                                </Grid>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
                </Grid>
            </Border>                                        
        </Grid>
    </DataTemplate>

ViewModel

public class ViewModel : INotifyPropertyChanged 
{       
    public ICommand CommandButtImport { get; set; }                     // for the button that imports the orders file
    public ICommand CommandButtSend { get; set; }                       // the button where the user sends the orders in our data grid to thinkFolio
    public ICommand CommandButtExit { get; set; }                       // exit application

    private QoEMain _QoEManager;                                        // manages the Model
    public QoEMain QoEManager { get { return this._QoEManager; } set { _QoEManager = value; } }

    private OrderBlocks _orderBlock;                                    // order block - contains all the order information
    public OrderBlocks OrderBlock
    {
        get
        {
            return this._orderBlock;
        }
        set
        {
            this._orderBlock = value;
            OnPropertyChanged("OrderBlock");
        }
    }
 }

OrderBlocks Class which contains the other classes

 public class OrderBlocks : INotifyPropertyChanged
{
 private List<Order> _orders;
    [XmlElement("tF_Transactions")]
    public List<Order> Orders { get { return _orders; } set { _orders = value; OnPropertyChanged("Orders"); } }
}

Orders Class

    public class Order : INotifyPropertyChanged
    {
        Security security;
        public Security Security
        {
            get { return security; }
            set { security = value; OnPropertyChanged("Security"); }
        }

        List<Security> duplicateSecurities;
        public List<Security> DuplicateSecurities
        {
            get { return duplicateSecurities; }
            set { duplicateSecurities = value; OnPropertyChanged("DuplicateSecurities"); }
        }

Security Class

 public class Security : INotifyPropertyChanged
 {
    private string _id;
    public string ID
    {
        get
        {
            return _id;
        }
        set
        {
            _id = value;
            OnPropertyChanged("ID");
        }
    }

    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }

    public Security() { }

    public Security(string newID, string newName)
    {
        ID = newID;
        Name = newName;
    }

Edit - My code now works please see the code snippet below that works for me

<DataGrid Grid.Row="1" Grid.Column="0" 
     ItemsSource="{Binding SelectedItem.DuplicateSecurities, ElementName=dataGridOrders}" 
     SelectedItem="{Binding SelectedItem.Security, ElementName=dataGridOrders}"> 
like image 425
mHelpMe Avatar asked Sep 23 '13 12:09

mHelpMe


3 Answers

Example of binding ListBox SelectionChanged Event to command in your ViewModel

<ListBox x:Name="myListBox" ItemsSource="{Binding SomeCollection}">
   <ie:Interaction.Triggers>
      <ie:EventTrigger EventName="SelectionChanged">
        <ie:InvokeCommandAction Command="{Binding SelectedItemChangedCommand}"  CommandParameter="{Binding ElementName=myListBox, Path=SelectedItem}"/>
    </ie:EventTrigger>
  </ie:Interaction.Triggers>
</ListBox >

In your ViewModel :

public class myViewModel
{

    public myViewModel()
    {
        SelectedItemChangedCommand = new DelegateCommand<object>((selectedItem) => 
        {
             // Logic goes here
        });
    }

    public List<SomeData> SomeCollection { get; set; }

    public DelegateCommand<object> SelectedItemChangedCommand { get; set; }
}

This particular example uses Prism MVVM Framework, but you can apply the same idea with any other MVVM framework you are using as well.

Hope this helps

like image 82
Omri Btian Avatar answered Nov 15 '22 06:11

Omri Btian


using SelectionChanged with MVVM is quite simple:

An approach could be to bind to the SelectedIndex property of the ListBox and in the property setter in the VM, act accordingly as it would be triggered whenever the property changes.

Example: Here

In this example whenever the selected index changes, the value of that item is increased by one.

the main bit is:

public int SelectedIndex {
  get {
    return _selectedIndex;
  }

  set {
    if (_selectedIndex == value) {
      return;
    }

    // At this point _selectedIndex is the old selected item's index

    _selectedIndex = value;

    // At this point _selectedIndex is the new selected item's index

    RaisePropertyChanged(() => SelectedIndex);
  }
}

xaml would just be:

<ListBox ItemsSource="{Binding Items}" SelectedIndex="{Binding SelectedIndex}" />

Items is the collection of items we bind to.

like image 28
Viv Avatar answered Nov 15 '22 06:11

Viv


System.Windows.Interactivity.WPF package has been deprecated.

Install Microsoft.Xaml.Behaviors.Wpf package

<ListBox ItemsSource="{Binding ListItems}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedItem}">
     <Behaviors:Interaction.Triggers>
          <Behaviors:EventTrigger EventName="SelectionChanged">
               <Behaviors:InvokeCommandAction Command="{Binding ItemChangedCommand}" />
          </Behaviors:EventTrigger>
     </Behaviors:Interaction.Triggers>
</ListBox>
like image 22
teej Avatar answered Nov 15 '22 07:11

teej