TreeView ContextMenu not fired events - caliburn.micro

TreeView ContextMenu not fired events (ContextMenu Opened event not fire), I'm trying Caliburn.Micro.Telerik convention not solved problem.
TreeViewItemTemplate
<HierarchicalDataTemplate x:Key="TreeViewItemTemplate" ItemsSource="{Binding Types}"
ItemTemplateSelector="{StaticResource NamespaceItemTemplateSelector}">
<StackPanel Orientation="Horizontal">
<Path
Data=""
Height="11.458" Margin="0,0,5,0" RenderTransformOrigin="0.5,0.5" Stretch="Fill"
UseLayoutRounding="False" Width="11.264">
<Path.Fill>
<SolidColorBrush Color="#FF333333">
<SolidColorBrush.RelativeTransform>
<MatrixTransform Matrix="Identity" />
</SolidColorBrush.RelativeTransform>
<SolidColorBrush.Transform>
<MatrixTransform Matrix="Identity" />
</SolidColorBrush.Transform>
</SolidColorBrush>
</Path.Fill>
<Path.RenderTransform>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Path.RenderTransform>
</Path>
<TextBlock Text="{Binding Header}" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=UIElement.Tag}" cal:Message.Attach="[Event Opened] = [Action Opened($dataContext)]" >
<MenuItem Header="NewChild" cal:Message.Attach="NewChild($datacontext)"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
TreeView
<TreeView x:Name="TreeView" ItemTemplate="{StaticResource TreeViewItemTemplate}" ItemsSource="{Binding Source}"/>

Before i tell you what was the problem i want to point out that the version of the code you uploaded is a bit different than the one you posted in your question.
OK, The problem lies exactly in this section of the template:
<TextBlock Text="{Binding Header}" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=UIElement.Tag}" cal:Message.Attach="[Event Opened] = [Action Opened($dataContext)]" >
<MenuItem Header="NewChild" cal:Message.Attach="NewChild($datacontext)"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
To fix it you need to replace it with this modified version:
<TextBlock Text="{Binding Header}" Tag="{Binding DataContext, RelativeSource={RelativeSource Self}}">
<TextBlock.ContextMenu>
<ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Tag}" cal:Message.Attach="[Event Opened] = [Action ContextMenuOpened($source)]">
<MenuItem Header="New" cal:Message.Attach="[Event Click] = [Action ClickMenuItem($source)]"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
The problem was caused by three issues:
In the template and specifically in this line of code <ContextMenu cal:Action.TargetWithoutContext="{Binding RelativeSource={RelativeSource Self},Path=UIElement.Tag}" the Path=UIElement.Tag was causing a binding error because there is no property on type ContextMenu that is called UIElement. Instead you have to replace that with the PlacementTarget property which represents the element in the user interface on which the context menu was opened.
The second issue is that you were putting the methods that handle those events on the wrong class. They shouldn't be on the ShellViewModel because in the template you are setting the Action.Target to be the DataContext behind the TextBlock which is actually the NamespaceViewModel in your situation, so the methods should go on the NamespaceViewModel.
The third and final issue is that your methods had the wrong signature so they couldn't be found by Caliburn.Micro. What i mean is that you are declaring your ContextMenuOpened method like this: void ContextMenuOpened(sender args, RoutedEventArgs args); but in the template you are calling the method like this: [Action ContextMenuOpened($source)] which sends the method the FrameworkElement that caused the event (ContextMenu in this case), so you should change the method signature for this event to this: void ContextMenuOpened(ContextMenu source) or the more general void ContextMenuOpened(FrameworkElement source).
Summary:
Fix your template to use the PlacementTarget property.
Move your methods to NamespaceViewModel.
Fix your methods signatures.
Finally: If you want to know everything about actions and action messages and what parameters get passed to what then you should really read the documentation wiki about Actions.

Related

XamarinForm DataTemplate ListView Binding

I want to bind to a variable from inside a CustomControl referenced in a ListView DataTemplate
This is the situation:
I have a custom control containing a ListView with many DataTemplates to display the various ViewCell (it's basically a set of dynamic fields that must be displayed according to their "type").
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:s4gvForms="clr-namespace:S4GVMobile.Forms;assembly=S4GVMobile"
xmlns:s4gvControls="clr-namespace:S4GVMobile.Controls;assembly=S4GVMobile"
x:Class="S4GVMobile.Controls.AttributesList"
x:Name="S4GVAttributesListControl"
>
<ContentView.Resources>
<ResourceDictionary>
<!-- MANY DATA TEMPLATES HERE, ONLY ONE LEFT FOR REFERENCE -->
<DataTemplate x:Key="s4gvAttributePictureTemplate">
<ViewCell x:Name="s4gvAttributePictureViewCell">
<ViewCell.View>
<StackLayout Orientation="Vertical">
<Label Text="{Binding Attribute.Key}" />
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
<!-- ... -->
<!-- AGAIN, LONG LIST IN THE SELECTOR -->
<s4gvForms:AttributeDataTemplateSelector x:Key="s4gvAttributeDataTemplateSelector"
AttributePictureTemplate="{StaticResource s4gvAttributePictureTemplate}"
/>
</ResourceDictionary>
</ContentView.Resources>
<StackLayout>
<ListView x:Name="s4gvAttributes" HasUnevenRows="True" ItemTemplate="{StaticResource s4gvAttributeDataTemplateSelector}" ItemsSource="{Binding AttributeValues}" />
</StackLayout>
I'm passing to this custom control both the list of items (required by the ListView) and an additional EntityID.
<s4gv:AttributesList x:Name="s4gvSerialAttributes" AttributeValues="{Binding Serial.AttributeValues}" EntityID="{Binding Serial.SerialID}" VerticalOptions="FillAndExpand"/>
The custom control has the required bindable properties (AttibuteValues and EntityID) and relies on a dedicated ViewModel
I need to retrieve the EntityID from inside the DataTemplate (and put it in the ParentID property); this value is in the custom control's ViewModel but it is "outside" the ListView's DataTemplates. I've tried using the Parent.BindingContext (it works on Command and CommandParameter) but it seems that it can only go up to the ListView's context and not higher though I need to move one further step up to the CustomControl itself.
<s4gvControls:AttributePicture ParentID="{Binding Source={x:Reference s4gvAttributePictureViewCell}, Path=Parent.BindingContext.EntityID}" AttributeValue="{Binding .}" />
Any idea? I'm happy to restructure the whole thing if required.

AppBarButton is not binding to MVVM Relay command in ListView

Hi I am trying to add AppBarButton in a ListView and binded command to RelayCommand in ViewModel here is my xaml code
<DataTemplate x:Key="MyTemplage" >
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<AppBarButton Grid.Column="1" Command="{Binding DoCommand,Mode=OneWay}">
<AppBarButton.Icon>
<BitmapIcon UriSource="ms-appx:///assets/doIcon.png"></BitmapIcon>
</AppBarButton.Icon>
</AppBarButton>
</DataTemplate>
<ListView HorizontalAlignment="Left" Margin="0,45,0,0" Background="Khaki" VerticalAlignment="Top" ItemsSource="{Binding AList, Mode=TwoWay}"
ItemTemplate="{StaticResource MyTemplage}">
</ListView>
here is my Relay command code in VM
private RelayCommand _DoCommand;
/// <summary>
/// Gets.
/// </summary>
public RelayCommand DoCommand
{
get
{
return _DoCommand
?? (_DoCommand = new RelayCommand(
() =>
{
DoSomething();
}));
}
}
DoCommand is not raising in ViewModel. If I register click event handler in code behind it works fine. also AppBarButton is wokring fine with MVVM if I use it in Page.Bottombar.
any ideas?
The problem is that the binding inside the ListView DataTemplate is not to the ViewModel object, but to a different DataContext, in your case it's binding to a list called AList which is inside the ViewModel and contains a list of model classes - so the binding engine is essentially looking for DoCommand inside that model class.
In order to get the binding to work, you must make sure that the binding is pointing to the whole ViewModel DataContext, where the RelayCommand actually is. One way you could do that is to bind to some element in your page which has the DataContext set to the whole ViewModel:
Command="{Binding DataContext.DoCommand, ElementName=pageRoot, Mode=OneWay}"
In this case, I'm binding to pageRoot, where pageRoot is the name of the root page element of your page which has the proper DataContext set - ViewModel where the RelayCommand actually is.

Event Pass element source with short syntax

This Method worked button event passed TreeView element to ViewModel but short syntax event not passed, Where is the problem? Thanks.
<Button Content="Search in TreeView" Height="34" Width="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="SearchTreeView">
<cal:Parameter Value="{Binding ElementName=TreeView}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TreeView x:Name="TreeView" ItemsSource="{Binding TreeViewSource}"/>
Short syntax event
<Button x:Name="SearchTreeView" Content="Search in TreeView" cal:Message.Attach="[Event Click] = [Action SearchTreeView($source.TreeView)]" />
<TreeView x:Name="TreeView" ItemsSource="{Binding TreeViewSource}"/>
You don't need to specify $source.Treeview, just the name of the element, TreeView is enough
<Button x:Name="SearchTreeView" Content="Search in TreeView" cal:Message.Attach="[Event Click] = [Action SearchTreeView(TreeView)]" />
<TreeView x:Name="TreeView" ItemsSource="{Binding TreeViewSource}"/>
That said, it's NOT a good idea to pass a control to the ViewModel. If you want to do something with the the selected item, you should bind TreeView's SelectedItem property to a property on your ViewModel and access this from your SearchTreeView method, eg
<Button x:Name="SearchTreeView" Content="Search in TreeView" cal:Message.Attach="[Event Click] = [Action SearchTreeView()]" />
<TreeView x:Name="TreeView" ItemsSource="{Binding TreeViewSource}" SelectedItem={Binding MySelectedItem}"/>

How can I delete an item from a Databound windows phone application?

Starting with the stock Databound application, I replace the xaml on the MainPage
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
with this:
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Name="ItemName" Margin="10,10,0,0" Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextSubtleStyle}" Grid.Column="0" />
<Button Grid.Column="1" Click="Button_Click" BorderThickness="0" Height="40" HorizontalAlignment="Center">
</StackPanel>
In Button_Click(), I’d like to remove that item from Items. I know the syntax would be something like App.ViewModel.Items.Remove(something)
but I’m missing what that something is. How can I ensure the correct item is removed based on the value of LineOne that is displayed?
Thanks for looking.
There are a few ways to go about doing this. The best way is with an ICommand. But you also need to add a CommandButton class to hold the reference and the parameter.
If you want a quick workaround though, then the sender object in the button click event should be the button you clicked, and its DataContext property should be the list item. A nasty hack but a lot less work than going down the ICommand path if you are just messing around learning the tools.
private void Button_Click(object sender, RoutedEventArgs e) {
App.ViewModel.Items.Remove((ItemViewModel)((Button)sender).DataContext);
}

MVVM Light toolkit Interaction.Triggers does not fire in datatemplate

I can use Interaction.Triggers to catch the textchanged event on a textbox like so:
<TextBox Text="{Binding Title}" Style="{StaticResource GridEditStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding TextChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
However when I use it in a datatemplate for a listview celltemplate as follows:
<ListView ItemsSource="{Binding LangaugeCollection}" SelectedItem="{Binding SelectedLangauge}" BorderThickness="0" FontFamily="Calibri" FontSize="11">
<ListView.View>
<GridView>
<GridViewColumn Width="200">
<GridViewColumn.CellTemplate>
<DataTemplate >
<Grid>
<TextBlock Text="{Binding Title}" Style="{StaticResource GridBlockStyle}">
</TextBlock>
<TextBox Text="{Binding Title}" Style="{StaticResource GridEditStyle}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<cmd:EventToCommand Command="{Binding TextChanged}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
the event will not trigger.
Does anyone know why this does not trigger and how to fix it?
When you are in a DataTemplate, the DataContext might not be what you expect. Typically the DataContext in a DataTemplate is set to the item that the DataTemplate represents. If your TextChanged command is on the "main viewmodel" instead of the data item, you need to be more precise in the way that you specify the data binding, for example:
Command="{Binding Source={StaticResource Locator}, Path=Main.TextChanged}"
You can see the issue when you run the code in debug mode (F5) in Studio and observe the Output window. A Data Error will be shown if the DataContext is incorrectly set.
Cheers,
Laurent
It seems something handles the event before the TextBox. Maybe you could listen to Title property (collection) changed inside you ViewModel, because anyway you are calling TextChanged on ViewModel inside the trigger, I suppose.
Btw I think you're missing TwoWay mode in your binding expression.
Hope this helps.

Resources