EventToCommand ancestor binding WPF - c#

Problem occures when invoke's
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<Command:EventToCommand Command="{Binding Path=Test, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ViewModel:ListViewModel}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
System.Windows.Data Error: 4 : Cannot find source for binding with
reference 'RelativeSource FindAncestor,
AncestorType='PhotoBrowser.List.ViewModel.ListViewModel',
AncestorLevel='1''. BindingExpression:Path=Test; DataItem=null; target
element is 'EventToCommand' (HashCode=37598799); target property is
'Command' (type 'ICommand')
How to fix it?
ListViewModel:
namespace PhotoBrowser.List.ViewModel
{
public class ListViewModel : ViewModelBase
{
public ObservableCollection<Item> Items { get; set; }
public ListViewModel()
{
Items = new ObservableCollection<Item>();
}
public RelayCommand Test
{
get;
set;
}
public RelayCommand DoubleClickOnItem
{
get
{
return new RelayCommand(() => OpenEdit());
}
}
private void OpenEdit()
{
Messenger.Default.Send(new NotificationMessage("Edit"));
}
public RelayCommand<DragEventArgs> Drop
{
get
{
return new RelayCommand<DragEventArgs>(CheckInstanceCertificate);
}
}
private void CheckInstanceCertificate(DragEventArgs args)
{
var path = string.Join("", (string[])args.Data.GetData(DataFormats.FileDrop, true));
Items.Add(new Item { ImagePath = path });
}
}
}
ListView.xaml
<UserControl x:Class="PhotoBrowser.List.View.ListView"
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="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
xmlns:ViewModel="clr-namespace:PhotoBrowser.List.ViewModel"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding Source={StaticResource Locator}, Path=List}"
>
<UserControl.Resources>
<Style x:Key="FileItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Margin" Value="5,5,5,5"/>
<Setter Property="Padding" Value="0,0,0,0"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Grid HorizontalAlignment="Left" VerticalAlignment="Top" Height="50" >
<Border x:Name="border" BorderBrush="{x:Null}" BorderThickness="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CornerRadius="2.5"/>
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter/>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ListView ItemsSource="{Binding Items}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemContainerStyle="{StaticResource FileItemStyle}" AllowDrop="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<Command:EventToCommand Command="{Binding Drop}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image Source="{Binding ImagePath}" Height="64" Width="64">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<Command:EventToCommand Command="{Binding Path=Test, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ViewModel:ListViewModel}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Image>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</UserControl>

Have a look at:
AncestorType={x:Type ViewModel:ListViewModel}
ViewModel is not the ancestor because ViewModel is no Control in your View. Correct would be:
ListView (Parent)
ListViewItem (Child)
Instead of this you should bind to the DataContext of the ListView (because this is your ViewModel):
<Command:EventToCommand Command="{Binding Path=DataContext.Test, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}}"/>

Related

EventToCommand for SelectionChanged on ComboBox inside of DataGrid mvvm/wpf

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

How to display a clickable list of things to datagrid cell?

I have a datagrid, in one of the columns I want to display a string - in each cell in the column a different string(that I get from the user) that looks like this: "data1, data2, data3, ..." and make each datai(data1,data2,...) clickable.
how can I achieve that?
Here is edited version:
Xaml (put it inside the <DataGridTemplateColumn.CellTemplate):
<DataTemplate DataType="{x:Type soDataGridHeplAttempt:ClicableItemsModel}">
<ListBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding ClickableItems}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Width="70" Content="{Binding }" Style="{StaticResource ButtonInCellStyle}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, Path=DataContext.Command}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=Content}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</DataTemplate>
Xaml resources use this if you want to edit the button content:
<Style x:Key="ButtonInCellStyle" TargetType="Button">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Background="{x:Null}" MaxWidth="40" BorderBrush="{x:Null}" Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=Content, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
HorizontalAlignment="Center" VerticalAlignment="Center" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}">
</TextBox>
</DataTemplate>
</Setter.Value>
</Setter>
Viewmodel:
private ICommand _command;
public ObservableCollection<ClicableItemsModel> Strings { get; set; }
public ICommand Command
{
get { return _command ?? (_command = new RelayCommand<object>(MethodOnCommmand)); }
}
private void MethodOnCommmand(object obj)
{
}
Clickable Items model code (create the ClicableItemsModel class an put it there):
public ObservableCollection<String> ClickableItems { get; set; }
when running:
regards,

Bind nested Command in Control Template

I have following control template:
public sealed partial class ItemSelectorControl : Control
{
...
public ICommand SelectionCommand
{
get { return (ICommand)GetValue(SelectionCommandProperty); }
set { SetValue(SelectionCommandProperty, value); }
}
public static readonly DependencyProperty SelectionCommandProperty =
DependencyProperty.Register("SelectionCommand", typeof(ICommand), typeof(ItemSelectorControl), new PropertyMetadata(null));
public ItemSelectorControl()
{
DefaultStyleKey = typeof(ItemSelectorControl);
}
...
}
and the corresponding theme style:
<Style TargetType="controls:ItemSelectorControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:ItemSelectorControl">
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock
Style="{StaticResource SectionHeader}"
Text="{TemplateBinding Headline}"
Visibility="{TemplateBinding HeadlineVisibility}"/>
<ItemsControl
x:Name="CurrencyItemPanel"
ItemsSource="{TemplateBinding ItemCollection}"
MaxHeight="{TemplateBinding MaximumItemsControlHeight}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Style="{StaticResource ItemSelectorButtonStyle}"
Command="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=SelectionCommand}"
CommandParameter="{Binding Path=Code}">
<interactivity:Interaction.Behaviors>
<behaviour:ItemSelectorVisualStateBehaviour
StateChangeTrigger="{Binding Selected, Mode=TwoWay}"/>
</interactivity:Interaction.Behaviors>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How do I have to write the command binding?
Command="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=SelectionCommand}"
In WPF I would work with FindAncestor but since that is not available in WinRT I don't not how to bind? I can't use DataContext.SelectionCommand because the current DataContext is the ViewModel.

wpf treeview xaml context menu click event not firing

for some reason unbeknownst to me I have been having some trouble getting this click event firing from the context menu of a datasourced treeviewitem.
The context menu appears as expected but is not handling its click events ( or at least not in the form I can see/retrieve ).
<UserControl x:Class="Pipeline_General.Custom_Controls.ProjectTree"
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:pm="clr-namespace:Pipeline_General"
mc:Ignorable="d"
DataContext = "{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TreeView Name="StructureTree" Background="{x:Static pm:myBrushes.defaultBG}" ItemsSource="{Binding ProjectList}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True"/>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<MenuItem Header="Add Episode.." Click="AddEp"/>
<MenuItem Header="Add Sequence.." Click="AddSeq"/>
<MenuItem Header="Add Scene.." Click="AddScene"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:ProjectRoot}" ItemsSource="{Binding Episodes}">
<TextBlock Text="{Binding Path=Title}" Foreground="{x:Static pm:myBrushes.pink}"
FontFamily="Calibri" FontSize="18"/>
<HierarchicalDataTemplate.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu Background="{x:Static pm:myBrushes.defaultBG}" Foreground="{x:Static pm:myBrushes.gray}">
<MenuItem Header="Add Sequence.." Click="AddSeq"/>
<MenuItem Header="Add Scene.." Click="AddScene"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</HierarchicalDataTemplate.ItemContainerStyle>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:Episode}" ItemsSource="{Binding Sequences}">
<TextBlock Text="{Binding Path=Key}" Foreground="{x:Static pm:myBrushes.orange}"
FontFamily="Calibri" FontSize="16"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:Sequence}" ItemsSource="{Binding Scenes}">
<TextBlock Text="{Binding Path=Key}" Foreground="{x:Static pm:myBrushes.yellow}"
FontFamily="Calibri" FontSize="14"/>
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type pm:Scene}">
<TextBlock Text="{Binding Path=Key}" Foreground="{x:Static pm:myBrushes.yellow}"
FontFamily="Calibri" FontSize="14"/>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
and in code behind...
public void AddSeq(object sender, RoutedEventArgs e)
{
var item = (TreeViewItem)StructureTree.SelectedItem;
Console.WriteLine(item.Header.ToString());
}
public void AddEp(object sender, RoutedEventArgs e)
{
Console.WriteLine(e.OriginalSource.ToString());
Console.WriteLine(e.Source.ToString());
Console.WriteLine("EP");
}
public void AddScene(object sender, RoutedEventArgs e)
{
Console.WriteLine(e.OriginalSource.ToString());
Console.WriteLine(e.Source.ToString());
Console.WriteLine("Scene");
}
From what I can tell, the problem is that you cannot attach a Click event like you did in a DataTemplate. You can refer to Event handler in DataTemplate to see how to do this, but what would be even better is to use a Command. This way your menu items will look like this:
<MenuItem Header="Add Episode.." Command="{x:Static throwAwayApp:MyCommands.AddEppCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
and you would add a command like this:
public static class MyCommands
{
static MyCommands()
{
AddEppCommand = new SimpleDelegateCommand(p =>
{
var menuItem = p as MenuItem;
//Console.WriteLine(e.OriginalSource.ToString());
//Console.WriteLine(e.Source.ToString());
Console.WriteLine("EP");
});
}
public static ICommand AddEppCommand { get; set; }
}
public class SimpleDelegateCommand : ICommand
{
public SimpleDelegateCommand(Action<object> executeAction)
{
_executeAction = executeAction;
}
private Action<object> _executeAction;
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_executeAction(parameter);
}
}

How can I bind a command from my IntemsControl list to my main ViewModel?

How can I bind a command from my IntemsControl list to my main ViewModel?
<Window x:Class="MemoryGame.MainWindow"
...
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<me:ColorConverter x:Key="ColorConverter"/>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="#FF44494D">
<ItemsControl ItemsSource="{Binding GameBoard.CardList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle x:Name="Card01" Fill="{Binding Converter={StaticResource ColorConverter}}" Height="100" Width="100" Margin="10,10,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" Stroke="Black">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding SelectCardCommand, ???????????}" CommandParameter="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Rectangle}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
SelectCardCommand is defined in my main window ViewModel. I already tryed to add RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window} but it doesn't work.
---- EDIT More information ----
In my DataContext ViewModel I have this :
public class MainViewModel : ViewModelBase
{
private RelayCommand<string> _selectCardCommand;
public RelayCommand<string> SelectCardCommand
{
get
{
return _selectCardCommand;
}
}
public MainViewModel()
{
_selectCardCommand = new RelayCommand<string>((s) => DoSelectCardCommand(s));
// GameBoard = new Board();
// this.StartGame();
}
private void DoSelectCardCommand(string card)
{
// Code here
}
}
you can use two methods
using RelativeSource
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding DataContext.SelectCardCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Rectangle}}"/>
</i:EventTrigger>
or using ElementName
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction Command="{Binding DataContext.SelectCardCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Rectangle}}"/>
</i:EventTrigger>
EDIT
after executing the code I realized the reason, as rectangle do not have any event named Click so it does not bind, however you can bind to UIElement.MouseDown
alternatively I came up with a different approach to solve your issue.
I modify the template a bit to use Button and templated the button as needed and also changed the ItemsPanelTemplate so it look more like a memory game
<ItemsControl ItemsSource="{Binding GameBoard.CardList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.SelectCardCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ItemsControl}}"
CommandParameter="{Binding}">
<Button.Template>
<ControlTemplate>
<Rectangle Fill="{Binding Converter={StaticResource ColorConverter}}"
Height="100"
Width="100"
Margin="10,10,0,0"
Stroke="Black">
</Rectangle>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
the command
#region SelectCardCommand
public RelayCommand<Card> SelectCardCommand { get; private set; }
#endregion
public MainViewModel()
{
SelectCardCommand = new RelayCommand<Card>((s) => DoSelectCardCommand(s));
GameBoard = new Board();
this.StartGame();
}
private void DoSelectCardCommand(Card card)
{
if (card != null)
card.Upside = true;
}
this will help you remove int.Parse(card.Substring(4,2)); and other string manipulations too

Categories