Can't Reach ViewModel From Within ListBox - c#

I have a ViewModel set as the highest level DataContext for my wpf application but I can't seem to access it when I jump into a ListBox as the new DataContext is the element of the list. A simple example is below.
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<StackPanel>
<!--1D List-->
<ListBox ItemsSource="{Binding my_list}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--Individual Elements-->
<TetxBlock Text="{Binding ViewModel_DisplayString}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
That example won't actually work when it comes to the button click as the ViewModel_ClickFunction isn't on the items within the class.
So is there anyway for me to do {Binding parent.selected_item} or something like that? I just need to be able to access the ViewModel from within the ListBox.

The DataContext inside ItemTemplate is actually the item itself. So in this case you have to use RelativeSource to walk up the visual tree (to the ListBox) and change the Path to DataContext.ViewModel_DisplayString:
<TetxBlock Text="{Binding DataContext.ViewModel_DisplayString,
RelativeSource={RelativeSource AncestorType=ListBox}}"/>

Related

xaml inside xaml. How to bind controls

I have xaml inside an xaml. The inside xaml has some bindings which are giving some problems. To explain here is my code
Main xaml
<TabItem Header="Configuration" DataContext="{Binding ComponentsVM}">
<Grid>
<ListBox ItemsSource="{Binding SomeList}" DisplayMemberPath="Name" SelectedItem="{Binding SomeComponent}" SelectedIndex="0"/>
<ig:MyInsideXamlElement Content="{Binding MyUserControl}" DataContext="{Binding}" />
</Grid>
</TabItem>
The inside xaml is
<Grid>
<TextBox Text="{Binding MySearchPath}"/>
</Grid>
The MyUserControl property displays my binding of UserControl without problems. But the MySearchPath property does not get updated with entity framework class. I suspect the binding of my inner xaml(MySearchPath) does not get resolved because the whole tab item's datacontext is ComponentsVM. Is there any way to give a second datacontext to the inner xaml?

Adding custom items to stackpanel

I'm working on a project (for Windows Phone 8 with Visual Studio 2012 with C#) where I want to display some items that each have:
a picture
a title
a description
to be able to be clicked (so that I can navigate to a certain Page)
So I thought I could do that with a stackpanel. But I'm not sure how I can add items that have the above properties and to be able to add those items from XAML. I tired adding items through a ItemsControl in stackpanel but I'm not sure how I can add more complex items like the one I want.
The best approach is to use a ListBox or LongListSelector rather than a StackPanel. You can then:
Data bind the list to the control itself, which will handle adding/deleting items from the control automatically
Define the view for each control using ListBox's ItemTemplate property
First of all, in your code-behind/ViewModel/what-have-you, you'll want to create an ObservableCollection of objects to display. ObservableCollection will let the control know to update in the case an item is added, removed, etc.
public ObservableCollection<T> foo = new ObservableCollection<T>();
In XAML, you'll then want to databind this ObservableCollection to the ListBox you've created:
<ListBox x:Name="ListBox" ItemsSource="{Binding foo}" />
Finally, you can define the ItemTemplate of the ListBox like so:
<ListBox x:Name="ListBox" ItemsSource="{Binding foo}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Description}" />
<Image Source="{Binding Image}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I'd highly recommend reading this guide, especially "Binding a control to a collection of objects" and the section after on DataTemplates. :)

How can I bind a datatemplate to a observablecollection so that they all show up?

How can I bind a datatemplate to reproduce itself for each item within an observablecollection so that they all show up? Or must I add these as individual controls, at runtime?
So I have a datatemplate:
<UserControl.Resources>
<DataTemplate x:Key="QueueItem">
<StackPanel Orientation="Vertical">
<Label Content="{Binding caseNumber}"></Label>
<!--TODO: put my other controls for this template here...-->
</StackPanel>
</DataTemplate>
</UserControl.Resources>
And I implement it in xaml here:
<ContentControl
Content="{Binding TestItems}"
ContentTemplate="{StaticResource QueueItem}">
</ContentControl>
And in my viewmodel there is a instance TestItems of some class I made, and one member of that is caseNumber, so the code above works just fine for me... But now I would like to have an observable collection of that same class, so is it possible/ is there xaml syntax to bind to the collection instance to reproduce the above for each index?
Thanks!
You could use an ItemsControl and use the ItemsTemplate property for your template:
<ContentControl>
<ItemsControl
ItemsSource="{Binding YourTestItemList}"
ItemTemplate="{StaticResource QueueItem}"/>
</ContentControl>
Use ItemsControl instead of ContentControl
Click here for sample code

How to change Tab from TabControl in WPF without violating the MVVM pattern

My WPF Windows contains a TabControl which displays content on different tabs. A click on the button below executes a method via ICommand interface / Binding. The called method generates text which is intended to be displayed in the second tab.
How can I switch to the second tab on button click without violating the MVVM Pattern?
I tried to bind the TabItem.IsSelected Property to something in my ViewModel but I wanted to use the other tabs (tab1) as well.
Any thoughts?
I found it out by myself.
The key is a two way binding. When the button is clicked it sets the property DisplayXamlTab true. The IsSelected attribute is bound to this variable. if another tab is clicked the binding will set the DisplayXamlTab Property to false.
Note: UpdateSourceTrigger=PropertyChanged is also very important
Code comes below:
XAML:
<TabItem Header="XAML" IsSelected="{Binding DisplayXamlTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<Grid Background="#FFE5E5E5">
<TextBox x:Name="TxtXamlOutput" IsReadOnly="True" Text="{Binding XamlText, Mode=TwoWay, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Visible"/>
</Grid>
</TabItem>
C# Property:
private bool displayXamlTab;
public bool DisplayXamlTab
{
get { return this.displayXamlTab; }
set
{
this.displayXamlTab = value;
this.RaisePropertyChanged("DisplayXamlTab");
}
}
if you're going the MVVM way you're going to create two dependency properties in the code behind:
ObservableCollection<ItemType> Items;
ItemType MySelectedItem;
Then, bind the TabControl ItemsSource property to the Items and bind the SelectedItem property to MySelectedItem
<TabControl ItemsSource="{Binding Items}"
SelectedItem="{Binding MySelectedItem, Mode=TwoWay}">
<TabControl.ItemTemplate>
<DataTemplate>
<... here goes the UI to display ItemType ... >
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
When you want to change the selected tab, simply update the MySelectedItem dependecy property
Although this question is fairly old and well answered already, I thought I'd add this additional answer to demonstrate an alternative way of changing the selected TabItem in a TabControl. If you have a view model for each TabItem, then it can be helpful to have an IsSelected property in it to determine whether it is selected or not. It is possible to data bind this IsSelected property with the TabItem.IsSelected property using the ItemContainerStyle property:
<TabControl ItemsSource="{Binding MenuItems}" TabStripPlacement="Top">
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImageSource}" Margin="0,0,10,0" />
<TextBlock Text="{Binding HeaderText}" FontSize="16" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type ControlViewModels:MenuItemViewModel}">
<ContentControl Content="{Binding ViewModel}" />
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
You can now change the selected TabItem from the parent view model like this:
MenuItems[0].IsSelected = true;
Note that because this property is data bound to the TabItem.IsSelected property, calling this...:
MenuItems[1].IsSelected = true;
... will infact also automatically set the MenuItems[0].IsSelected property to false. so if the view model that you are working with has its IsSelected property set to true, then you can be sure that its related view is selected in the TabControl.
You can create a binding between the view model and the TabControl.SelectedIndex property - i.e., 0 selects the first TabItem , 1 selects the second, etc.
<TabControl DataContext="..." SelectedIndex="{Binding SomeVmProperty}" ...
(alternatively, depending on how you've got things set up, you could bind against SelectedItem...)
You'll likely want to use some sort of "Event Aggregator" pattern (I.e. the Messenger class in MVVM Light) to broadcast some sort of "navigation" message. Your View - the TabControl - can listen for the specific message, and navigate to Tab2 when the message is received.
Alternatively, you can bind the "SelectedItem" property of the TabControl to your ViewModel, and simply call CurrentTab = MySecondTabViewModel from within your VM. This is the approach recommended by #HighPoint in the comments to the OP, but I'm not a fan; see below. Another caveat to this approach is that you need to be familiar with DataTemplates, as you will need to map a view to each ViewModel which you display.
I personally like the first approach, because I don't consider it to be a "responsibility" of the ViewModel to handle tab navigation. If you simply alert your View when data changes in your ViewModel, you allow the View to decide whether or not it wants to change tabs.

Changing content according to selected option in WPF

I'm interested in creating an app that displays some buttons and changes a viewport according to the selected button. The viewport in my app is a ContentControl and I thought of changing its content whenever a button is clicked. However, I believe there's a better approach, by perhaps injecting the ViewModels of each of the Views I want to present to the ContentControl and styling them using DataTemplates (Since I want to avoid having a grid with many controls and just setting their Visibility property whenever I want to show a particular view). Which of the approaches seems better to you? Do you have a different approach for this?
The view should be something similar to this:
Thanks!
Usually have a ViewModel behind the window which contains:
ObservableCollection<IViewModel> AvailableViewModels
IViewModel SelectedViewModel
ICommand SetCurrentViewModelCommand
I display the AvailableViewModels using an ItemsControl, which has its ItemTemplate set to a Button. The Button.Command is bound to the SetCurrentViewModelCommand, and it passes the current data item from the AvailableViewModels collection in through the CommandParameter
To display the content area, I use a ContentControl with ContentControl.Content bound to SelectedViewModel, and DataTemplates get used to tell WPF how to render each ViewModel.
The end result is my XAML looks something like this:
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ViewB />
</DataTemplate>
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding AvailableViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.SetCurrentViewModelCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding }"
Margin="2,5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<ContentControl Content="{Binding SelectedViewModel}" />
</DockPanel>
You can view an example of the full code used for such a setup on my blog

Categories

Resources