Treeview SelectedItem is sometimes the VM and sometimes TreeViewItem - mvvm

I have a TreeView that the user navigates to select an item for display in a grid. Briefly the XAML looks like this:
<local:TreeViewEx x:Name="theTreeView" ItemsSource="{Binding theData}">
<local:TreeViewEx.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding theChildData}">
<TextBlock Text="{Binding Name}"/>
</sdk:HierarchicalDataTemplate>
</local:TreeViewEx.ItemTemplate>
</local:TreeViewEx>
<Grid DataContext="{Binding ElementName=theTreeView, Path=SelectedItem}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}" />
<TextBlock Text="{Binding Name}" /></StackPanel>
</Grid>
As the user clicks through the treeview the viewmodel type name is displayed along with the value of the Name property. Perfect. Howerver the user can also execute a search of the treeview (following to Josh Smith) which sets the IsSelected property of the TreeViewItem. Once that happens the {Binding} displays TreeViewItemEx rather than the ViewModel type name, and of course the Name property is not displayed.
How is that possible that the selectedItem would sometimes by the ViewModel, and sometimes be the TreeViewItem?

If you replace your grid with a ContentControl you can then use a DataTemplateSelector.
<ContentControl Content="{Binding ElementName=theTreeView, Path=SelectedItem}"
ContentTemplateSelector="{StaticResource TreeViewItemSelector}" />
On the DataTemplateSelector you can then reference two templates for the different types
<DataTemplate x:Key="ModelTemplate">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding}" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
<TreeViewItemSelector x:Key="TreeViewItemSelector"
ModelTemplate="{StaticResource ModelTemplate}"
TreeItemTemplate="{StaticResource TreeItemTemplate}" />
In the selector you will then want logic like this
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is ModelType)
return ModelTemplate;
if (item is TreeViewItem)
return TreeItemTemplate;
throw new NotImplementedException();
}

Related

Select ListView Element on button Click using MVVM

I have one list as RoomGroupList and another as RooList,One Room Group have multiple Roos in it, like
RoomGroup 1 => Room-1
Room-2
Room-3
I have bound RoomGroupList to ListView and RoomGroupControll is Generating From it as,
<ListView x:Name="lstRoomGroupList" SelectionMode="Single" ItemsSource="{Binding roomGroupList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding evokoRoomGroup,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
<ListView.ItemTemplate>
<DataTemplate>
**<UserConrols:RoomGroupControl/>**
</DataTemplate>
</ListView.ItemTemplate
Now RoomGroupControl has one ListView to Listout No.ofRooms in it as,
<ListView x:Name="lstRoomList" Grid.Row="3" ItemsSource="{Binding GetroomList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding DataContext.EvokoRoom,ElementName=tbDashBoard,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch" VerticalAlignment="Top" Background="#F3F3F5"
BorderBrush="LightGray" BorderThickness="0"
Margin="0,0,0,0" Height="Auto" MaxHeight="200" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.ItemTemplate>
<DataTemplate>
**<UserConrols:RoomControl/>**
</DataTemplate>
</ListView.ItemTemplate>
what I want is,when I click on any button from ListElements at that time it's corresponding ListItem(row) should be selected so that I have added EventSetter using Resource on RoomGroupList as
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
</ListView.Resources>
It's working properly for RoomGroupList I'm able to select Different Groups but Problem is I Can't select Rooms From RoomGroup which I have selected.
If anyone knows how to manage this, can also provide me Links and Suggestions,Thanks.

Attached Behaviors in Windows Store Apps with MVVM Light

I've been trying to use Attached Behaviors in a simple Windows Store App using the MVVM Light framework. Since we can't use System.Windows.Interactivity, as in Windows Phone, I've been using both Windows.UI.Interactivity and WinRtBehaviors lybraries. They work well but the problem is that I can't declare an attached behavior inside a data template.
What I really want is to attach a command to any item in a GridView, so that I can pass the item id as the parameter. Since that attached behavior doesen't work, the only solution I found consists on using the "SelectionChanged" event of the GridView and pass the SelectedItem as the parameter to a property in the ViewModel:
<GridView Grid.Row="1"
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Padding="116,136,116,46"
ItemsSource="{Binding GeoTopArtists.topartists.artist}"
SelectionMode="Single"
SelectedItem="{Binding SelectedArtist, Mode=TwoWay}"
IsSwipeEnabled="False"
IsItemClickEnabled="False">
<WinRtBehaviors:Interaction.Behaviors>
<Win8nl_Behavior:EventToCommandBehavior Event="SelectionChanged" Command="SelectArtistCommand" CommandParameter="{Binding SelectedArtist.mbid}"/>
</WinRtBehaviors:Interaction.Behaviors>
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding image[4].text}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding name}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding url}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
It would be very nice doing something like that (where there's no need to have a SelectedArtist property in the ViewModel)
<GridView Grid.Row="1"
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemsGridView"
AutomationProperties.Name="Items"
TabIndex="1"
Padding="116,136,116,46"
ItemsSource="{Binding GeoTopArtists.topartists.artist}"
SelectionMode="None"
IsSwipeEnabled="False"
IsItemClickEnabled="False">
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding image[4].text}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}"/>
</Border>
<StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding name}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding url}" Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
<WinRtBehaviors:Interaction.Behaviors>
<Win8nl_Behavior:EventToCommandBehavior Event="Tapped" Command="SelectArtistCommand" CommandParameter="{Binding Artist.mbid}"/>
</WinRtBehaviors:Interaction.Behaviors>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
This link may help you out. The dependency property is defined and hooked to a command in view model. So whenever there is interaction in view, the selected item can be sent as commanding parameter which is clearly demonstrated in this video.

TreeView HierarchicalDataTemplate with Navigation Properties EntityFramework

I have three linked Entities (Categories->Types->Classes) with one to many relationship.
Is it possible to bind only Categories entity and represent the rest using Navigation properties andHierarchicalDataTemplate ?
I imagine something like this :
<TreeView ItemsSource="{Binding Categories}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Categories}" >
<TextBlock Foreground="Red" Text="{Binding Types}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Classes}">
<TextBlock Text="{Binding TypeName}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate >
<TextBlock Text="{Binding ClassName}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
I've done this recently, in a WPF project, I've updated the Types to match your names and Nav Properties.
You might want to consider changing Type/Class to something more specific since they're used as keywords in the c# language, but I think you get the idea
<!-- The Root Category Table -->
<HierarchicalDataTemplateDataType="{x:Type Entities1:Categories}" ItemsSource="{Binding Types}" >
<StackPanel Orientation="Horizontal">
<Image Name="img" Source="{Binding ., Converter={Converters:DataTypeImageConverter}}" />
<TextBlock Text="{Binding CategoryName}" FontWeight="Bold" />
</StackPanel>
</HierarchicalDataTemplate>
<!-- Your Type Table -->
<HierarchicalDataTemplate DataType="{x:Type Entities1:Type}" ItemsSource="{Binding Classes}" >
<StackPanel Orientation="Horizontal">
<Image Name="img" Source="{Binding ., Converter={Converters:DataTypeImageConverter}}" />
<TextBlock Text="{Binding TypeName}" />
</StackPanel>
</HierarchicalDataTemplate>
<!-- Your Class Table-->
<DataTemplate DataType="{x:Type Entities1:Class}">
<StackPanel Orientation="Horizontal">
<Image Name="img" Source="{Binding ., Converter={Converters:DataTypeImageConverter}}" />
<TextBlock Text="{Binding ClassName}" />
</StackPanel>
</DataTemplate>
What this will do is, it will match the datatype in the tree - If it matches Category, it will create the stackpanel, then start another branch, using the navigation collection Types.
For types, it will do the same basic thing - create the panel for that item, then another branch, for it's navigation collection Class.
If it's class, it will only create the panel, since it's a DataTemplate, not a HierarchicalDataTemplate

Own DataTemplateSelector MVVM

I'm using a MVVM-Pattern with a ModelView-First approach. This works fine, so far.
Now I have a UserControl (View) which should display various content depending on a Property located in my ViewModel.
First, I tried to solve the issue with DataTemplates and a DataTemplateSelector (See this tutorial) This was working very well. But I was not happy with the solution, because then I have a class (the overrided DataTemplateSelector) which is not connected to the ViewModel and can't be filled from the model.
So I tried to create a own TemplateSelector which uses a Property from the ViewModel. Unfortunately the DataTrigger is not triggering. The Binding from a CheckBox to the ViewModel is also working but not at the DataTrigger (even the designer can't find this path).
Ok, please have a look at the code:
<UserControl.Resources>
<!--Define Template which is displayed for Users-->
<DataTemplate x:Key="templateUser">
<Image
Name="logo"
Source="blanked out"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</DataTemplate>
<!--Define Template which is displayed for Administrators-->
<DataTemplate x:Key="templateAdmin">
<TextBlock Background="Yellow" Margin="3" Text="YEAH, I'm an Administrator" />
</DataTemplate>
<!--My own TemplateSelectpr-->
<DataTemplate x:Key="myTemplateSelector">
<ContentControl x:Name="DynamicContent" ContentTemplate="{StaticResource templateUser}"/>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsAdministrator}" Value="true">
<Setter TargetName="DynamicContent" Property="ContentTemplate" Value="{StaticResource templateAdmin}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentPresenter ContentTemplate="{StaticResource myTemplateSelector}"/>
</Grid>
Of course, I can seperate the Task in two further contentcontrols, but I don't want to maintain those if same content is intersecting.
So can someone suggest anything?
Best regards, and thanks in advance!
The simpler, the better: use a single template, which includes all the controls you need to show. Then switch their visibility using a binding to your property:
<UserControl.Resources>
<DataTemplate x:Key="myTemplate">
<Grid>
<Grid Visibility="{Binding IsAdministrator, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Content for admin -->
</Grid>
<Grid Visibility="{Binding IsAdministrator, Converter={StaticResource NotBooleanToVisibilityConverter}}">
<!-- Content for user -->
</Grid>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentPresenter ContentTemplate="{StaticResource myTemplate}"/>
</Grid>
Answer is to long for comment
Arnaud Weil brought me on the right way:
To access the Property 'IsAdministrator' in ViewModel from the Datatemplate, I gave the UserControl a Name e.g.:
<UserControl
x:Class="blanked out"
x:Name="this"
Used the code from Arnaud with some modifications, to inherit the Binding to the ViewModel from UserControl
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<helper:NotBooleanToVisibilityConverter x:Key="NotBooleanToVisibilityConverter"/>
<DataTemplate x:Key="myTemplate">
<Grid>
<Grid Visibility="{Binding DataContext.IsAdministrator, ElementName=this, Converter={StaticResource BooleanToVisibilityConverter}}">
<!-- Content for admin -->
<TextBlock Background="Yellow" Margin="3" Text="ICH BIN ADMNIN; JUCHUUU" />
</Grid>
<Grid Visibility="{Binding DataContext.IsAdministrator, ElementName=this, Converter={StaticResource NotBooleanToVisibilityConverter}}">
<!-- Content for user -->
<Image
Name="logo"
Source="/blanked out"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</Grid>
</DataTemplate>
</UserControl.Resources>
And for the inverted BooleanToVisibilityConverter:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace blankedout.Helper
{
[ValueConversion(typeof(bool), typeof(Visibility))]
public class NotBooleanToVisibilityConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = (bool)value;
return !boolValue ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Thanks once again to Arnaud Weil
Regards

How to bind a combobox having an item as a datagrid in wpf? The ComboBox itself is a part of a Datatemplate

I have a TabControl generating some tab items following the MVVM pattern using WPF.
The TabControl ItemTemplate i.e. for each of the tab items that is generated, I have a combobox that should show some data from a different list. This list is not the same as the bound object of the Tab Item itself.
For eg: If a tabitem is bound to a address object the combobox should show data from a state list and show the state present in the address object as the selected item.
The Combo box is having only one item as a datagrid which is bound to the state list
The problem is that all data is being shown in all sections but the combo box simply wouldnt show the data. Surprisingly, the Combobox when taken out of the TabControl DataTemplate works fine....
Below is the code both outside and inside the tabcontrol. Some suggestions please !!!
------This one is outside the Tabcontrol and works perfectly--------------
<ComboBox SelectedValue="{Binding SelectedState}" HorizontalAlignment="Left" VerticalAlignment="Center" IsEditable="True" Margin="50,55,0,61" Height="27" Width="193">
<ComboBoxItem TextSearch.Text="{Binding SelectedState}">
<ContentControl>
<DataGrid SelectedIndex="{Binding StateSelectedIndex}" ItemsSource="{Binding StateData}" AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" cal:Message.Attach="[Event SelectionChanged] = [Action SelectionChanged]">
<DataGrid.Columns>
<DataGridTextColumn Header="StateName" Binding="{Binding StateName}" />
</DataGrid.Columns>
</DataGrid>
</ContentControl>
</ComboBoxItem>
</ComboBox>
----This one is inside the TabControl ---------
<TabControl Margin="3,3,0,0" HorizontalAlignment="Left" Width="752" Height="255" VerticalAlignment="Top" Grid.ColumnSpan="2" Grid.Row="1" ItemsSource="{Binding Addresses}" SelectedIndex="{Binding WhichAddressTab}" >
<TabControl.ItemContainerStyle>
...................................
</TabControl.ItemContainerStyle>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl>
..........................
<ComboBox SelectedValue="{Binding SelectedState}" HorizontalAlignment="Left" VerticalAlignment="Center" IsEditable="True" Margin="108,3,0,1" Grid.ColumnSpan="3" Grid.Row="1" Width="147">
<ComboBoxItem TextSearch.Text="{Binding SelectedState}" >
<ContentControl>
<DataGrid SelectedIndex="{Binding StateSelectedIndex}" ItemsSource="{DynamicResource StateData}" AutoGenerateColumns="False" Height="200" HorizontalAlignment="Left" VerticalAlignment="Top" Width="200" cal:Message.Attach="[Event SelectionChanged] = [Action SelectionChanged]">
<DataGrid.Columns>
<DataGridTextColumn Header="StateName" Binding="{Binding StateName}" />
</DataGrid.Columns>
</DataGrid>
</ContentControl>
</ComboBoxItem>
</ComboBox>
...........................
</Grid>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I have tried searching and have understood the problem is with the databinding within a datatemplate, but have been unable to achieve the desired result.
Yes, the problem is with your binding. The DataContext inside of the DataTemplate is for a single element (from the collection bound to the ItemsSource). Meaning that you can't access properties that are directly on the ViewModel itself.
To work around this, you can name the TabControl for example, and convert your bindings inside of the DataTemplate to use ElementName bindings. Another solution, if you have your ViewModel defined as a Static Resource, is to bind directly to the resource, and use the Path to specify the property to which you want to bind.
Hope this helps :)