Reputation: 3706
How can I catch the Selection Changed event that fires on a ComboBox
that is embedded into a DataGridComboBoxColum
? I would like to use MVVM pattern for this so something like a EventToCommmand
solution would be nice.
XAML:
<DataGridComboBoxColumn Header="ViewTemplate" Width="180" SelectedItemBinding="{Binding ViewTemplate}" DisplayMemberPath="Name">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemsSource" Value="{Binding DataContext.ViewTemplates, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
<Setter Property="IsReadOnly" Value="True"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemsSource" Value="{Binding DataContext.ViewTemplates, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
I would like to use something like this but don't know where/how to set it up in this particular case.
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding SelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
As requested this is my full code for XAML:
<UserControl x:Class="GrimshawRibbon.Revit.Views.ViewManager.ViewManagerView"
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:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:GrimshawRibbon.Revit.Views.ViewManager"
xmlns:ex="clr-namespace:GrimshawRibbon.Revit.Wpf.Extensions"
xmlns:controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="pack://application:,,,/GrimshawRibbon;component/Revit/Wpf/Style/GrimshawTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ex:DataGridEx x:Name="dgViews"
Style="{StaticResource AzureDataGrid}"
Margin="10"
AutoGenerateColumns="False"
ItemsSource="{Binding Views, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
CanUserAddRows="False"
IsReadOnly="False"
SelectionMode="Extended"
SelectionUnit="FullRow"
SelectedItemsList="{Binding SelectedViews, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="CellEditEnding">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding CellEditEndingCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
<DataGridTextColumn Header="ViewType" Binding="{Binding ViewType}" Width="100" IsReadOnly="True"/>
<DataGridTextColumn Header="ViewGroup" Binding="{Binding ViewGroup, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="130" IsReadOnly="False"/>
<DataGridTextColumn Header="ViewSubGroup" Binding="{Binding ViewSubGroup, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="130" IsReadOnly="False"/>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="OnSheet"
Binding="{Binding OnSheet, Mode=TwoWay}"
IsReadOnly="True"
Width="80">
</DataGridCheckBoxColumn>
<DataGridComboBoxColumn Header="ViewTemplate" Width="180" SelectedItemBinding="{Binding ViewTemplate}" DisplayMemberPath="Name">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding DataContext.SelectChangeCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemsSource" Value="{Binding DataContext.ViewTemplates, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
<Setter Property="IsReadOnly" Value="True"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource {x:Type ComboBox}}">
<Setter Property="ItemsSource" Value="{Binding DataContext.ViewTemplates, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</ex:DataGridEx>
</Grid>
</UserControl>
and view model:
public class ViewManagerViewModel : ViewModelBaseEx
{
public ViewManagerModel Model;
public ObservableCollection<ViewWrapper> Views { get; private set; }
public ObservableCollection<ViewWrapper> ViewTemplates { get; private set; }
public IList SelectedViews { get; set; }
public RelayCommand<DataGridCellEditEndingEventArgs> CellEditEndingCommand { get; set; }
public RelayCommand<SelectionChangedEventArgs> SelectChangeCommand { get; set; }
public ViewManagerViewModel(Document doc)
{
Model = new ViewManagerModel(doc);
Views = Model.CollectViews();
ViewTemplates = Model.CollectViewTemplates();
CellEditEndingCommand = new RelayCommand<DataGridCellEditEndingEventArgs>(args => OnCellEditEnding(args));
SelectChangeCommand = new RelayCommand<SelectionChangedEventArgs>(args => OnSelectionChanged(args));
}
private void OnSelectionChanged(SelectionChangedEventArgs e)
{
// do something
}
/// <summary>
/// Logic for handling cell editing events.
/// </summary>
/// <param name="e">DataGrid Row object.</param>
private void OnCellEditEnding(DataGridCellEditEndingEventArgs e)
{
var wrapper = e.Row.Item as ViewWrapper;
switch (e.Column.SortMemberPath)
{
case "Name":
Model.ChangeName(wrapper);
break;
case "ViewGroup":
Model.SetParameter(wrapper, "View Group", wrapper.ViewGroup);
break;
case "ViewSubGroup":
Model.SetParameter(wrapper, "View Sub Group", wrapper.ViewSubGroup);
break;
default:
break;
}
}
public override void Apply()
{
var vw = new List<ViewWrapper>();
foreach (ViewWrapper v in SelectedViews)
{
vw.Add(v);
}
Model.Delete(vw);
// update collection so that rows get deleted in datagrid
foreach (ViewWrapper i in vw)
{
Views.Remove(i);
}
}
}
Upvotes: 2
Views: 2860
Reputation: 169190
Using the MVVM pattern you don't handle the SelectionChanged
event for the ComboBox
element in the view.
Instead you should implement your selection changed logic in the setter of the ViewTemplate
property, or in a method that you call from there, that gets set whenever a new item is selected in the ComboBox
, .e.g.:
private ViewWrapper _viewTemplate;
public ViewWrapper ViewTemplate
{
get { return _viewTemplate; }
set { _viewTemplate = value; SelectionChanged(); }
}
This is one of corner stones of MVVM. Whenever the source property gets set by the view, the view model can perform some logic. The view only sets a property. It doesn't handle any event.
Edit: If this is not an option for some reason you should replace the DataGridComboBoxColumn
with a DataGridTemplateColumn
because you cannot apply an EventTrigger
to a Style
:
<DataGridTemplateColumn Header="ViewTemplate" Width="180">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ViewTemplate.Name}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.ViewTemplates, RelativeSource={RelativeSource AncestorType=DataGrid}}"
SelectedItem="{Binding ViewTemplate}"
DisplayMemberPath="Name">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand PassEventArgsToCommand="True"
Command="{Binding DataContext.SelectionChangedCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Upvotes: 2
Reputation: 76
what i would do is something like this so inside your viewmodel, you will have something like
Viewmodel.cs
public class ViewModel
{
public ViewModel()
{
_selectChangeCommand= new RelayCommand(OnSelectChange, CanSelectChange);
}
private RelayCommand _selectChangeCommand;
public ICommand SelectChangeCommand { get { return _selectChangeCommand; } }
private bool CanSelectChange()
{
return true;
}
private void OnSelectChange()
{
..//do something in here when you change something in your combo box
}
}
In your MainWindow.xaml.cs do something like this:
this.DataContext = new ViewModel();
In your ViewModel.xaml file
<ComboBox....>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand PassEventArgsToCommand="True" Command="{Binding SelectChangeCommand }"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
One important thing is to make sure that your viewmodel.xaml knows where it references from so that it can see where the SelectionChangeCommand is located so it can do the binding. FYI, for the relaycommand, my advice is to use the Galasoft.MVVMlight so you don't have to implement relaycommand yourself.
Upvotes: 0
Reputation: 937
You can add eventhandler to your combobox. I added to grid's showing event, you can add another event of course.
private void Grid1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb!=null)
{ cb.SelectionChangeCommitted -= new EventHandler(cb_SelectedIndexChanged);
// now attach the event handler
cb.SelectionChangeCommitted += new EventHandler(cb_SelectedIndexChanged);
}
}}
Upvotes: 0