Event Pass element source with short syntax - caliburn.micro

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

Related

Xamarin: Hide the button if condition is false

I am new to Xamarin Forms.
I want to know how to hide a button if a specific condition is false.
ex: I need to hide the submit button until the text field has a value.
(if the text field is null, cannot click the submit button)
this is my .xaml code
<Entry Grid.Row="0"
Margin="10,10,10,20"
x:Name="ground_area"
Grid.Column="1"
MaxLength="5"
HorizontalOptions="Center"
Placeholder="area"
FontSize="18"/>
<Button x:Name="avg_nut"
Text="Submit"
Grid.Row="5"
VerticalOptions="Center"
Margin="40,0,20,50"
Style="{StaticResource buttonStyle}"
Clicked="submit_click">
</Button>
Hey you can use the property of the button IsEnabled="{Binding booleanProperty}" and in your view model you can manipulate the value of this property depending in what you want
I recommended you to use View Model instead of Code Behind is not that hard learn how works MVVM (Model View ViewModel)
That's my advice.
I hope this can solve your problem
Use Trigger:
<Entry x:Name="ground_area"/>
<Button Text="Submit">
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Text.Length, Source={Reference ground_area}, FallbackValue=0}" Value="0">
<Setter Property="IsVisible" Value="False"/>
</DataTrigger>
</Button.Triggers>
</Button>
Also, check IsEnabled property, it's better than hiding the control.

Xamarin Forms ToolbarItem doesn't change IsEnabled from XAML

I'm facing a problem with ToolbarItem and IsEnabled property when trying to turn it on/off from XAML using triggers. ToolbarItem doesn't support triggers, so what I do is to create a Button (a hidden one) which supports triggers and then bind Button.IsEnabled to ToolbarItem.IsEnabled; here is the sample code:
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tlbSave" Text="Save" Clicked="Handle_Clicked">
<ToolbarItem.IsEnabled>
<Binding Source="{x:Reference btnTest}" Path="IsEnabled" />
</ToolbarItem.IsEnabled>
</ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Padding="10" VerticalOptions="CenterAndExpand">
<Entry x:Name="txtTest" HorizontalOptions="FillAndExpand" />
<Button x:Name="btnTest" Text="HIDDEN" IsEnabled="false" HorizontalOptions="FillAndExpand">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference txtTest}, Path=Text.Length,
Converter={convert:IsPositiveIntegerConverter}}" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
</Button>
</StackLayout>
</ContentPage.Content>
If you try this piece of code you will see how btnTest gets enable/disable when txtTest.Text has some value. But it isn't affecting tlbSave.IsEnabled property.
However, this work perfect in code behind when I set tlbSave.IsEnabled into btnText.PropertyChanged EventHandler
btnTest.IsVisible is false, I'm just showing it up for testing purposes.
Any idea about how to deal with this?
This is because of the IsEnabled property of ToolbarItem is read-only.
If you just set IsEnabled property of a toolbar item in your XAML to false or true, you will get the following exception at runtime.
System.InvalidOperationException: The BindableProperty "IsEnabled" is readonly.
And if you take a look at Microsoft's documentation, you will notice that you cannot directly set IsEnabled property of a toolbar item.
For disabling a toolbar item, the suggested way is to use a command and it's CanExecute.
I found out a way to solve this problem, at least a way better than implementing OnPropertyChange for btnTest
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="tlbSave" Text="Save" Clicked="Handle_Clicked" />
</ContentPage.ToolbarItems>
<ContentPage.Content>
<StackLayout Padding="10" VerticalOptions="CenterAndExpand">
<Entry x:Name="txtTest" HorizontalOptions="FillAndExpand" />
<Button x:Name="btnTest" Text="HIDDEN">
<Button.Triggers>
<MultiTrigger TargetType="Button">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Source={x:Reference txtTest}, Path=Text.Length,
Converter={convert:IsPositiveIntegerConverter}}" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="IsEnabled" Value="True" />
</MultiTrigger>
</Button.Triggers>
<Button.IsEnabled>
<Binding Source="{x:Reference tlbSave}" Path="IsEnabled" Mode="OneWayToSource" />
</Button.IsEnabled>
</Button>
</StackLayout>
</ContentPage.Content>
Then set btnTest.IsEnabled = false; inside constructor and everything will go as smooth as I want.

TreeView ContextMenu not fired events

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.

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.

Binding WPF menu items to WPF Tab Control Items collection

I have a WPF Menu and a Tab control. I would like the list of menu items to be generated from the collection of TabItems on my tab control. I am binding my tab control to a collection to generate the TabItems. I have a TabItem style that uses a ContentPresenter to display the TabItem text in a TextBlock. When I bind the tab items to my menu the menu items are blank. I am using a Style Setter to set the Menu Item name, but I am not sure what property of the TabItem I would use to set the MenuItem text. Is there a workaround for my scenario? Is it possible to bind to the Header property of the tab item, when I do not know the number of tabs in advance? Below is a copy of my xaml declarations. Tab Control and items:
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel HorizontalAlignment="Stretch">
<Button
Command="{Binding Path=CloseWorkSpaceCommand}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="10,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
Background="Red"
/>
<ContentPresenter HorizontalAlignment="Center"
Content="{Binding Path=DisplayName}">
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</ContentPresenter.Resources>
</ContentPresenter>
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"
Margin="10"
Background="#4C4C4C"/>
</DataTemplate>
My Menu and partial style listing
//I am not sure which value am I suppose to use, since I am not using header
<Menu Background="Transparent">
<MenuItem Style="{StaticResource TabMenuButtonStyle}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}, Path=Items}"
ItemContainerStyle="{StaticResource TabMenuItem}">
</MenuItem>
</Menu>
Create the following style and bind Header property to display property in ViewModel
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding PropertyInViewModel}" />
</Style>

Resources