Chaning the ItemsSource DataContext - c#

I'm having a problem binding to my command in my ListBox ItemTemplate. My command is defined in my ViewModel, but because I'm using ItemsSource for my listbox, that's set as the DataContext.
<ListBox ItemsSource="{Binding CreatureModel.TypeFlagsValues}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Command="{Binding SetCommand">
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I've tried using RelativeSource
Command="{Binding SetCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type vm:CreatureEditorViewModel}}}"
And
<DataTemplate DataType="vm:CreatureEditorViewModel">
<CheckBox Content="{Binding}" Command="{Binding SetCommand}" CommandParameter="test">
</CheckBox>
</DataTemplate>
I feel like I'm missing something simple here.

You almost did it right with RelativeSource.
Try this:
Command="{Binding Path=DataContext.SetCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ItemsControl}} }"
Alternatively you may give a name to your listbox (e.g. "_listBox" and use the following binding:
Command="{Binding DataContext.SetCommand, ElementName=_listBox}

Try setting the ElementName and Path on the Command; you have to set the x:Name on the ListBox, then you can reference the parent DataContext.
<ListBox x:Name="list" ItemsSource="{Binding CreatureModel.TypeFlagsValues}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" Command="{Binding ElementName=list,
Path=DataContext.SetCommand}">
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

WPF - binding event to the ViewModel

I have a stack panel for which I need a mouse event to be handled in the ViewModel but the binding doesn't work - the command isn't found although the other elements are also binded to the ViewModel and they work. Here is the xaml:
<Window.DataContext>
<vm:Ticker/>
</Window.DataContext>
<ListView Grid.Row="3"
ItemsSource ="{Binding TickersCollectionView}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Name="STPListView">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction
Command="{Binding MouseLeftButtonDownCommand}"
CommandParameter="{Binding ElementName=STPListView}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBlock
Width="130"
Text="{Binding Market}"
Foreground="WhiteSmoke"/>
<TextBlock
Width="110"
Text="{Binding Price}"
Foreground="{Binding LastPrice, Converter={StaticResource BoolToForeground}}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The command is a property of the ViewModel. How can I bind it?
If the MouseLeftButtonDownCommand property is defined in the same view model as the TickersCollectionView property, you should bind to it using a RelativeSource:
Command="{Binding DataContext.MouseLeftButtonDownCommand,
RelativeSource={RelativeSource AncestorType=ListView}}"

How to bind a command to navigate between pages?

I am learning navigation with MVVM as seen here:
Navigation with MVVM
In this tutorial, binding a command for button control is implemented by using ItemsControl like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomeViewModel}">
<local:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ProductsViewModel}">
<local:ProductsView />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding CurrentPageViewModel}" />
</DockPanel>
If I understand it correctly, it iterates through collection of PageViewModels and binds a button command for every VIewModel. This example works fine and I am able to switch between pages with no problem.
However, I don't want to create buttons iteratively. I want to have two buttons inside a grid with the same command that switches views as in example above. Something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomeViewModel}">
<local:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ProductsViewModel}">
<local:ProductsView />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{Binding HomeView.Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"/>
<Button Grid.Column="1"
Content="{Binding ProductsView.Name}"
Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"/>
</Grid>
But this doesn't work as I expected. I am able to bind View's names but not the Command. Clicking buttons is doing nothing. DataContext is set to an instance of my ApplicationViewModel. What could be a problem here?
The problem is in the CommandParameter="{Binding }", in the first example that binding is pointing to ItemsSource="{Binding PageViewModels}". The second example doesn't have that part, so that binding is failing.
EDIT>>>>
To clarify, you must bind the command parameter to an instanciated viewModel in your datacontext.
ViewModel:
public IPageViewModel homeViewModel{get; set;}
public IPageViewModel productsViewModel{get; set;}
...
IPageViewModel homeViewModel = new HomeViewModel();
IPageViewModel productsViewModel = new ProductsViewModel();
View:
CommandParameter="{Binding DataContext.homeViewModel, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
I dont have a code editor right now, but hope it helps.

Bind to command from within a treeview

I'm building a treeview with HierarchicalDataTemplates and would like to bind the nodes to a command from my MainViewModel. I guess there is some conflict with the scopes, since the binding works if I e.g. use a button and define it outside of the treeview. If I define it inside, however, it does not work.
I've searched through Stackoverflow and found several solutions but none that worked for me. Jehof e.g. suggested here to use
<Button Command="{Binding DataContext.Command,
RelativeSource={RelativeSource AncestorLevel=2, AncestorType=TreeViewItem}}"
CommandParameter="{Binding}" />
but that did not work. Thank you for any suggestions!
<TreeView ItemsSource="{Binding _questions}" Grid.Row="0" Margin="10" BorderThickness="1">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Question}" ItemsSource="{Binding Converter={StaticResource QuestionConverter}}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MainOption}" ItemsSource="{Binding MainOptions}">
<StackPanel Orientation="Horizontal">
<CheckBox Content="{Binding Path=Name}" />
/////////////////////////////////////
<Button Command="{Binding ThisIsMyCommand}" Content="Click Me"/>
/////////////////////////////////////
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
Is your TreeView-Control inside a Window or UserControl?
If you are inside a Window:
<Button Command="{Binding DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}" CommandParameter="{Binding}" />
and for UserControl
<Button Command="{Binding DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" CommandParameter="{Binding}" />

How to bind mouse click to ContentPresenter within a HierarchicalDataTemplate to ViewModel?

I have the following DataTemplate used in a TreeView.
How do I have LeftMouseClick on the ContentPresenter to bind to a property of the DataType in MVVM fashion?
Thanks for any help (or better idea).
<DataTemplate x:Key="sharedTemplate">
<StackPanel Orientation="Horizontal">
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<ContentPresenter
Content="{Binding Name, Mode=OneTime}"
Margin="2,0"
/>
</StackPanel>
</DataTemplate>
<TreeView
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding ReportTree}" >
<TreeView.Resources>
<HierarchicalDataTemplate
DataType="{x:Type r:ReportViewModel}"
ItemsSource="{Binding Children}"
>
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource sharedTemplate}" />
</HierarchicalDataTemplate>
................
Addendum: This works!
<DataTemplate DataType="{x:Type r:PrinterViewModel}">
<!--Bind the ContentControl to the DataType-->
<ContentControl Content="{Binding}"
ContentTemplate="{StaticResource sharedTemplate}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<h:EventToCommand
Command="{Binding DataContext.SelectItem, RelativeSource={RelativeSource FindAncestor, AncestorType=TreeView}}"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ContentControl>
</DataTemplate>
Addendum: As it turns out, the above Interaction.Trigger does work to correctly send the selected viewmodel back to the DataContext of the TreeView. Problem solved.

How to align auto generated content DataTemplate next to each other?

i want those buttons next to each other and not under each other
this is a mvvm, so the buttons are generated by a class
<ItemsControl ItemsSource="{Binding Pages}" Grid.Column="1">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="8" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You just have to change the default ItemsPanel from your ItemsControl into a horizontal oriented one, here's an example :
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Margin="8" Content="{Binding Name}" Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" CommandParameter="{Binding}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
By defining an horizontal StackPanel as your ItemsControl panel, each item will be aligned next to each other (Note that the default one is a vertical StackPanel).

Categories