Close dynamically added Tab Items using Prism - WPF - c#

I'm using Prism RegionManager, to register different views with a TabControl region inside the MainView.
MainView.xaml
<TabControl regions:RegionManager.RegionName="MainViewTabRegion">
<TabControl.ItemTemplate>
<DataTemplate>
<DockPanel Width="Auto">
<Button Command="{Binding DataContext.DataContext.CloseTabCommand, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontWeight="Bold"
Margin="4,0,0,0"
FontSize="10"
VerticalContentAlignment="Center"
Width="15" Height="15" />
<ContentPresenter Content="{Binding DataContext.DataContext.HeaderText, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" />
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
In MainViewViewModel I'm Adding different views with the same base class.
MainViewViewModel.cs:
private void AddProjectView() {
var view = _container.Resolve<ProjectSettingsView>();
var dataContext = _container.Resolve<ProjectSettingsViewModel>();
dataContext.HeaderText = "test header txt";
view.DataContext = dataContext;
_regionManager.RegisterViewWithRegion("MainViewTabRegion", () => view);
}
I can add new tab item with the view.
How can I close the tab item, the <TabControl.ItemTemplate> in the XAML code above, adds a close button with CloseCommand in the ProjectSettingsViewModel, with the TabItem bonded to it.
ProjectSettingsViewModel.cs
private void OnExecuteCloseCommand(object tabItem) {
//Close this TabItem
}

Bind the CommandParameter property of the Button to the DataContext of the TabItem:
<Button Command="{Binding DataContext.DataContext.CloseTabCommand, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontWeight="Bold"
Margin="4,0,0,0"
FontSize="10"
VerticalContentAlignment="Center"
Width="15" Height="15" />
You could then remove the view like this in the view model:
public class ProjectSettingsViewModel
{
private readonly IRegionManager _regionManager;
public ProjectSettingsViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
CloseTabCommand = new DelegateCommand<object>(OnExecuteCloseCommand);
}
private void OnExecuteCloseCommand(object tabItem)
{
_regionManager.Regions["MainViewTabRegion"].Remove(tabItem);
}
public DelegateCommand<object> CloseTabCommand { get; }
}

You just need to get the reference to your IRegionManager. Then you get the Region that your view belongs to, and call Remove on the region and pass the tabItem reference to remove it.
Ex:
private void OnExecuteCloseCommand(object tabItem) {
regionManager.Regions["MainViewTabRegion"].Remove(tabItem);
}
You can actually just place this in your MainViewViewModel and bind to it in the DataTemplate, then you don't have to rewrite the close command for each tab item's view model.

I cover this in my Pluralsight course "Prism Problems & Solutions: Mastering the Tab Control". You can see the solution here: https://app.pluralsight.com/library/courses/prism-mastering-tabcontrol/table-of-contents
Essentially, you just need to create a TriggerAction that does all the work for you. Simple. Nothing is needed in the VM.

Related

Binding Tag property of checkbox to ancestor datacontext

I know this is a frequently question but after viewing lots of question in this context i still did not find working solution.
I have this MainWindow
public partial class MainWindow : Window
{
public ObservableCollection<Camera> Cameras { get; set; } = new ObservableCollection<Camera>();
public ObservableCollection<Group> Groups { get; set; } = new ObservableCollection<Group>();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
And this class
public class Group : INotifyPropertyChanged
{
private int _number;
[XmlAttribute("Number")]
public int Number
{
get { return _number; }
set
{
_number = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is part of the MainWindow.xaml (the relevant part)
<StackPanel>
<Button Click="Button_Click_1" Margin="55,0,0,0" Padding="4">Add Group</Button>
<ListView Grid.ColumnSpan="3" Grid.Row="1" ItemsSource="{Binding Groups,Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Margin="3" Width="30" Grid.Column="0" Text="{Binding Number}"></TextBlock>
<ComboBox Margin="3" Width="50" Grid.Column="5" ItemsSource="{Binding DataContext.Cameras, RelativeSource={RelativeSource AncestorType=ListView}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Number}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<!--<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding Path=DataContext.Number, RelativeSource={RelativeSource AncestorType=ListView}}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />-->
<TextBlock Text="{Binding Path=Name, Mode=TwoWay}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
The list of checkbox inside the comboxbox will be filled according to the list of the cameras.
I want to bind the property "Tag" of the inner checkbox to the member Groups.Number like i did with the textblock above it.
The reason behind this (maybe you have another solution) is that the list of groups is a dynamic group, and i want to identify from which group the checkbox was checked.
I've tried everything with the ancestor issue but nothing seems to work.
other things i've tried are:
<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Number}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Groups.Number}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=Number}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
<CheckBox Width="20" VerticalAlignment="Center" Tag="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=Groups.Number}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" />
What do i miss here?
Ty!
ListView has the wron DataContext. It is outside the DataTemplate and is set to MainWindow. The DataTemplate that targets Group has the proper DataContext, of course the current Group item. You must chose an element of this DataTemplate as binding source. You couls bind to the ComboBox.DataContext:
<CheckBox Tag="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.Number}" />

Binding to ViewModel not working using RelativeSource

I have an application that has a Navigation Menu that uses Commands to go from page to page.
The Navigation Menu has been created in Xaml and i have it stored in Navigation.xaml see below
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Cirdan.Wpf.Navigation"
xmlns:infrastructure="clr-namespace:Cirdan.Wpf.Infrastructure">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Cirdan.Wpf;component/Resources/Styles/Stylesheet.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate x:Key="Navigation" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="4*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Columns="1">
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding ViewerPageCmd}" >Viewer Screen</Button>
<Button DataContext="{Binding NavigationViewModel, Source={x:Static infrastructure:MainWindow.LocatorX}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding}" >Acquisition Screen</Button>
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:NavigationViewModelBase}}}" Visibility="{Binding ElementName=Menu, Path=IsChecked, Converter={StaticResource VisibilityConverter}}" Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>
<ToggleButton Grid.Row="1" Style="{StaticResource ToggleBtnToolStyle}" x:Name="Menu" IsChecked="true" Background="Transparent" BorderThickness="0" >
<StackPanel Orientation="Horizontal">
<ContentPresenter Margin="5" Height="50" Content="{StaticResource MenuIcon}"></ContentPresenter>
<Viewbox>
<TextBlock Margin="5" Style="{StaticResource TxtToolStyle}">Menu</TextBlock>
</Viewbox>
</StackPanel>
</ToggleButton>
</Grid>
</DataTemplate>
The ViewModel that I am trying to Bind these Button Commands to is called NavigationViewModelBase.cs
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
namespace Cirdan.Wpf.Navigation
{
public abstract class NavigationViewModelBase : ViewModelBase
{
private List<DicomMetadataModel> _dicomMetadata;
//Navigation Cmd
public ICommand AcquisitionPageCmd { get; private set; }
public ICommand ManualEntryWindowCmd { get; private set; }
public ICommand SessionWindowCmd { get; private set; }
public ICommand SettingsWindowCmd { get; private set; }
public ICommand StudyInfoPageCommandCmd { get; private set; }
public ICommand ViewerPageCmd { get; private set; }
public ICommand WorklistPageCmd { get; private set; }
protected NavigationViewModelBase()
{
AcquisitionPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.AcquisitionScreen)));
ManualEntryWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.ManualEntry, DicomMetadata)));
SessionWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Session)));
SettingsWindowCmd = new RelayCommand(() => Messenger.Default.Send(new ShowDialogMessage(Pages.Settings)));
ViewerPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Viewer)));
WorklistPageCmd = new RelayCommand(() => Messenger.Default.Send(new GoToPageMessage(Pages.Worklist)));
}
}
}
On each page i then use the following code to add the Navigation
<ContentControl Grid.Column="2" Grid.Row="2" ContentTemplate="{StaticResource Navigation }" />
At the moment I'm not getting any errors, and when I have set the DataContext in one of my buttons above, when I go to bind my commands i can see all the properties of that Viewmodel so that bit is working correctly, However when i run the program and click on these buttons nothing is happening.
Thanks for any help
Ancestor Binding will only work for element in visualtree/view or simply elemnts in your page. A viewModel is not anywhere in the page as a control. So in place of viewModel give the type of view which has NavigationViewModelBase as datacontext.write binding As below (if your view/control is NavigationView the binding will be). and in path write the property of vm:NavigationViewModelBase
class :
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:NavigationView}}, path= PropertyYouWantToBind}"
just make sure if you have not defined DataContext for any other control in hierarchy, Button will be getting same DataContext as view, then you just have to bind the command. which you have already done correctly.
if you have not given the DataContext to any control in your view then just give that to you contentControl or Grid. simply as
<Grid>
<Grid.DataContext>
<vm:NavigationViewModelBase />
</Grid.DataContext>
<Grid.RowDef.......
and then use simple binding for command.
RelativeSource FindAncestor looks for ancestors in VisualTree.
VisualTree can look like:
Window
Grid
TextBlock
TextBox
Button
ViewModels are not in visual three
For example ancestors of Button are Grid and Window in this case.
How to do it then?
well, create custom UserControl instead ContentControl with ContentTemplate in ResourceDictionary.
In the usercontrol set DataContext to the class that inherits from NavigationViewModelBase.
then the binding will be simple:
<UniformGrid Grid.Row="0" Columns="1">
<Button Command="{Binding ViewerPageCmd}">Viewer Screen</Button>
<Button Command="{Binding AcquisitionPageCmd}">Acquisition Screen</Button>
<Button Command="{Binding WorklistPageCmd}" >Worklist Screen</Button>
</UniformGrid>

Adding buttons to WPF from ObservableCollection using MVVM pattern

I'm trying to add buttons to UserControl. I have to follow MVVM pattern. I have created a class, DeviceButton, with different set/gets and a constructor. In a viewmodel, used as datacontext, is the ObservableCollection and a get-method for the collection. I have bound the collection to a ItemsControl source and tried to add a template. I guess I'm missing something 'cause the buttons won't load.
The UserControl is added into a tab (using dragablz) which as well is a part of a ObservableCollection, also added at run time (this is working just fine). The idea is, that the tab has a list of buttons that has to be created at run time, where the list is fetched from a web service - so the buttons has to be added dynamically/programatically. Overview is just the first tab - a template for each tab (reflecting the fetched items) is being implemented when the buttons work. For now, I'm just adding a test button to the collection, but as stated, this won't show. What am I missing?
I have a Overview.xaml file:
<UserControl // attributes omitted to save spaces... ask if needed>
<StackPanel>
<Border>
<TextBlock FontSize="16" FontWeight="Bold" TextWrapping="WrapWithOverflow"
TextAlignment="Center" HorizontalAlignment="Center"
Foreground="{DynamicResource AccentColorBrush}">
Welcome to Greenhouse App
</TextBlock>
</Border>
<ItemsControl ItemsSource="{Binding DeviceButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vmodel:DeviceButton}">
<Button Width="50" Height="50" Margin="10 10 0 0" Command="{Binding OpenTab}"
CommandParameter="{Binding DeviceType}" HorizontalAlignment="Left"
VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// Manually added test button...
<Button x:Name="windMill" HorizontalAlignment="Left" Margin="10,0,0,0"
VerticalAlignment="Top" Width="50" Height="50"
Command="{Binding OpenTab}" FontFamily="Segoe Ui"
Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="/Greenhouse;component/Icons/Windmill.png" />
</StackPanel>
</Button
</StackPanel>
I'm trying to add an ObservableCollection of type DeviceButton, the _deviceButtons collection (Binded as ItemsSource to the ItemsControl)
The tabList items are working just find, and I can manually add more if needed (these will later be added through the OpenNewTab-command, which should be bound to the buttons)
The DeviceButton file:
public class DeviceButton
{
private readonly string _content;
private readonly string _deviceType;
private readonly string _imageUrl;
public DeviceButton(string content, string deviceType, string imageUrl)
{
_content = content;
_deviceType = deviceType;
_imageUrl = imageUrl;
}
public string Content
{
get { return _content; }
}
public string DeviceType
{
get { return _deviceType; }
}
public string ImageUrl
{
get { return _imageUrl; }
}
}
The collection is located in a viewmodel file, MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public ICommand OpenTab { get; set; }
private string tabControl { get; set; } = "0";
private IInterTabClient _interTabClient;
private ObservableCollection<TabContent> _tabContents = new ObservableCollection<TabContent>();
private ObservableCollection<DeviceButton> _deviceButtons = new ObservableCollection<DeviceButton>();
public MainWindowViewModel()
{
_interTabClient = new MyInterTabClient();
DeviceButtons.Add(new DeviceButton("Windturbine", "windturbine", "/Greenhouse;component/Icons/Windmill.png"));
TabContents.Add(new TabContent("Overview", new Overview()));
OpenTab = new RelayCommand<object>(OpenNewTab);
}
private void OpenNewTab(object obj)
{
MessageBox.Show("Yo"); // Not yet implemented
RaisePropertyChanged(() => tabControl);
}
public ObservableCollection<TabContent> TabContents
{
get { return _tabContents; }
}
public ObservableCollection<DeviceButton> DeviceButtons
{
get { return _deviceButtons; }
}
public IInterTabClient InterTabClient
{
get { return _interTabClient; }
}
}
The buttons aren't loaded when I start the program (only the "test" button added manually in the WPF).
The UserControl, Overview, is considered a tab in another Controls.Metrowindow, MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type dragablz:TabablzControl}">
<Setter Property="CustomHeaderItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<ContentPresenter Margin="4" Content="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<dragablz:TabablzControl SelectedIndex="{Binding tabControl}" ItemsSource="{Binding TabContents}" x:Name="InitialTabablzControl" Margin="4 0 4 4">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding MyInterTabClient}" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>
I guess it's an issue with the resources/binding in the Overview.xaml, but I've exhausted all suggested solutions I could find.
Issue was in binding
ItemsSource="{Binding Path=DataContext.TabContents,
RelativeSource={RelativeSource AncestorLevel=1,
AncestorType={x:Type Window}, Mode=FindAncestor}}"
I suspect the DataContext is being set to a TabContent object, which does not have a DeviceButtons property
I would suggest using a tool like Snoop to verify your DataContext is what you expect.

Windows Phone - custom control how propagate event to ViewModel

I am creating custom control for menu and I have ListBox in my control. Something like this:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when I want to catch Tap event, get properties from class and keep MVVM model. How can I catch this in MainPage.xaml.cs or in MainViewModel?
I have this code in my MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400">
</controls:BottomMenu>
I have prepare this code in my MainViewModel:
public RelayCommand<string> GoToSectionCommand
{
get
{
return goToArticleCommand
?? (goToArticleCommand = new RelayCommand<string>(
NavigateToSection));
}
}
But I don't know how can I call it. What's the best way?
Edit:
I tried to extend listbox:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Button Content="{Binding Title}"
Command="{Binding ListButtonClickCommand, Source={RelativeSource Self}}"
CommandParameter="{Binding Url}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With code-behind:
public ICommand ListButtonClickCommand
{
get { return (ICommand)GetValue(ListButtonClickCommandProperty); }
set { SetValue(ListButtonClickCommandProperty, value); }
}
public static readonly DependencyProperty ListButtonClickCommandProperty =
DependencyProperty.Register("ListButtonClickCommand", typeof(ICommand), typeof(BottomMenu), new PropertyMetadata(null));
public BottomMenu()
{
InitializeComponent();
}
Then in MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400"
ListButtonClickCommand="{Binding MenuItemButtonCommand}">
</controls:BottomMenu>
And in MainViewModel:
private ICommand menuItemButtonCommand;
public ICommand MenuItemButtonCommand
{
get
{
return menuItemButtonCommand
?? (menuItemButtonCommand = new RelayCommand(
NavigateToArticleSection));
}
}
For now without luck. It's not working. RelayCommand isn't triggered.
Edit2
I guess the problem is with binding command in custom control but I don't know how to fix it.
You just need to bind to the UserControl -- using "RelativeSource Self" will bind to the Button, which is not what you want. You should be able to use an "ElementName" binding to locate the user control:
<UserControl x:Name="UserControlName" ... >
...
<Button Content="{Binding Title}"
Command="{Binding ElementName=UserControlName,Path=ListButtonClickCommand}"
CommandParameter="{Binding Url}"/>
...
</UserControl>

How to set a on click effect on a textBlock and open a new WPF window?

Hi I am new to the WPF and I am trying to learn it. So now I want to know how to create an onclick effect on a textblock that is in ListBox. I want to click on any of the items in the listBox and open a new window. I must be doing something wrong but I cant figure out what is it. So far I have the following.
<Grid>
<ItemsControl ItemsSource="{Binding Source={StaticResource cvsRoutes}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}" MinHeight="50">
<ListBox>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBox_MouseLeftButtonDown" />
<TextBlock Text="Something" >
<TextBlock.InputBindings>
<MouseBinding Command="" MouseAction="LeftClick" />
</TextBlock.InputBindings>
</TextBlock>
<TextBlock Text="Something" />
<TextBlock Text="Something" />
<TextBlock Text="Something" />
<TextBlock Text="Something" />
</ListBox>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The code above is in my XAML file. Do I need something else if so. Where should it be?
This is the MVVM police! ;)
Xaml: Use bindings to ICommand instead and System.Windows.Interactivity & forinstance galasoft mvvm light. I haven't tested the code below, I just wrote it in notepad++.. Ouch I see one thing here now, you are doing this inside a datatemplate & listboxitem... Your TextBlock will look for the command on the LI and not VM, so you need a funky binding here. Check if it works, but you want your click event to execute on the datacontext of the vm, and not the listbox item, so binding must be changed slightly (vacation... =) )
Items in a listbox is wrapped in ListBoxItems and the datacontext is set what the LI is supposed to present, an item in a list.
You might want to change the KeyUp binding below frpm
<command:EventToCommand Command="{Binding KeyUpCommand}" PassEventArgsToCommand="True"/>
To:
<command:EventToCommand Command="{Binding Path=DataContext.KeyUpCommandCommand, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}}}" PassEventArgsToCommand="True"/>
To be sure replace UserControl with the name of your control/page/cust ctrl/window.
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight"
xmlns:local="clr-namespace:YOURNAMSPACE"
...
<UserControl.DataContext>
<local:ViewModelListStuff/>
</UserControl.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Source={StaticResource cvsRoutes}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Name}" MinHeight="50">
<ListBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<command:EventToCommand Command="{Binding PreviewMouseLeftButtonDownCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock Text="Something" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyUp">
<command:EventToCommand Command="{Binding KeyUpCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
<TextBlock Text="Something" />
<TextBlock Text="Something" />
<TextBlock Text="Something" />
<TextBlock Text="Something" />
</ListBox>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Now you are going to need a viewmodel, which you set as datacontext. Here is an example with a simple baseclass (It's nice to expand ViewModelBase provided by galasoft to add functionality.
VM baseclass (simplified):
public class SomeBaseClass : INotifyPropertyChanged
{
// Other common functionality goes here..
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]// Commment out if your don't have R#
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
VM:
public class ViewModelListStuff : SomeBaseClass
{
private string name;
public ICommand PreviewMouseLeftButtonDownCommand { get; set; }
public ICommand KeyUpCommand { get; set; }
public String Name
{
get { return name; }
set
{
if (value == name) return;
name = value;
OnPropertyChanged();
}
}
// I would have exposed your cvsSomething here as a property instead, whatever it is.
public ViewModelListStuff()
{
InitStuff();
}
public void InitStuff()
{
PreviewMouseLeftButtonDownCommand = new RelayCommand<MouseButtonEventArgs>(PreviewMouseLeftButtonDown);
KeyUpCommandnCommand = new RelayCommand<KeyEventArgs>(KeyUp);
}
private void KeyUp(KeyEventArgs e)
{
// Do your stuff here...
}
private void PreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
// Do your stuff heere
}
}
Hope it helps! Create a breakpoint in the methods which will we invoked by the commands and watch your output and stacktrace of the command methods.
Cheers
Stian

Categories