WPF Binding Treeview element to a UserControl - c#

I have a tree view that is using the Model View architecture, each TreeViewItem has a windows Form attribute, when I click on a node I want the application to display the form associated with that node to the right hand side of the tree.
How can you achieve this using binding I have tried the following but the user control Associated with ApplicationForms doesn't get displayed.
<ContentControl Margin="163,5,127,5" Content="{Binding SelectedItem,ElementName=ApplicationTree}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:ApplicationViewModel}">
<StackPanel>
<TextBlock Text="Displaying an A!" />
<ContentPresenter Name="MyContent">
<ContentPresenter.Content>
<UserControl x:Name="UserCntrl2" HorizontalAlignment="Stretch" Height="Auto" Width="Auto" Content="{Binding ApplicationForms}"/>
</ContentPresenter.Content>
</ContentPresenter>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ApplicationsViewModel}">
<StackPanel>
<TextBlock Text="Displaying a B!" />
<!--<TextBlock Text="{Binding Bar}" />-->
</StackPanel>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>

Since you're using MVVM, you can alternativly put that kind of logic in the view model. you can then bind the IsSelected property of tree node to your viewmodel, then when IsSelected get set to true by wpf (when the use selects the item) you can do whatever you want.
Its a very useful pattern to use view models this way. your viewmodels can have references to all kinds of stuff and affect them based on selection or expansion. You can also go the other way around and have code affect the viewmodels and let the databinding update the actual controls
Here is a pretty good article on MVVM and treeview
You should also check out the HierarchicalDataTemplate if you're working with treeviews
-edit-
After reading the question properly, i see that you're already doing the right thing, that is binding your master control to the SelectedItem of the Treeview. I do belive the SelectedItem property points to the TreeViewItem though, not the actual VM. Perhaps thats the problem?

Related

switch between viewmodels on the same view using wpf mvvm

I'm new to WPF MVVM, and a bit stuck. I need to switch between about 100 different tables on the same view using MVVM with wpf.
I have Treeview with the list of table names and on item selection the correct DataGrid has to be displayed beside the Treeview.
I created Model and ViewModel classes for each table. However, how do I select the right Viewmodel to bind depend on the selection.
If I understand your problem right - then you have a design problem.
First get the SelectedItem of your TreeView
To use the SelectedItem-Binding on a TreeView see this. But you could do also the bad way in the code behind.
Second bind your SelectedItem
So what you want to do is:
bind the SelectedItem on something like a ContentControl or ContentPresenter. Or do it the bad way in the code behind.
For example like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding MyItemSource}">
<!-- Get the selected item here (watch how to in the linked answer) -->
</TreeView>
<ContentPresenter Grid.Column="1"
Content="{Binding Path=SelectedItem}"
>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<DataGrid>
<!-- Your DatGrids or what ever -->
</DataGrid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Grid>
Third (optional) If you have different DataGrids
You could also use a DataTemplateSelector to change your Views depending on your SelectedItem too. You would use it on the ContentPresenter in this example.

Can't Reach ViewModel From Within ListBox

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}}"/>

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

Modifying an individual TreeViewItem on a databound WPF TreeView without modifying the data source

I've got a MVVM WPF app with the TreeView databound to a viewmodel class. It is essentially a file explorer. I want to add the ability to "Add a new folder" to the hierarchy. To achieve the desired functionality what I am trying to do is simply switch the Textblock out for an editable TextBox in my datatemplate. This is what my datatemplate looks like:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel Name="tv_itempanel"
Orientation="Horizontal"
Margin="2">
<Image Source="{Binding Icon}" Margin="4"/>
<TextBlock Name="treeitem_tblock" Margin="4" Text="{Binding Name}"/>
<TextBox Width="200" Visibility="Collapsed" Name="treeitem_tbox"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
The problem is that I cannot modify an individual TreeViewItem since the treeview is databound. Any ideas? Thanks
Add a bool IsEditable property to your VM objects, and bind the visibility of the TextBox to is (using a converter to transform the boolean value to a Visibility enum). That way you don't need to manipulate the TreeViewItem directly, simply mark the data object as editable, and it will flow naturally to your view.

Categories

Resources