WPF ItemsPanel with Caliburn.Micro MVVM - c#

I'm developing a WPF application and using Caliburn.Micro for MVVM. I'need to dynamically add buttons into view during the runtime.
I have done some research and I got to know that I can achieve this using an ItemControl.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ItemsControl>
<Button x:Name="ExecuteMethod1" Content="Execute1" />
<Button x:Name="ExecuteMethod2" Content="Execute2" />
<Button x:Name="ExecuteMethod3" Content="Execute3" />
</ItemsControl>
</Grid>
</Window>
Caliburn.Micro's convention allows to call a method which matches to Button's x:Name property. In above code button named ExecuteMethod1 will call a method ExecuteMethod1() in ViewModel.
Problem:
As I'm adding buttons dynamically how can I set the X:Name property for buttons?
I tried to set it using Binding, but WPF doesn't allow to bind X:Name property.
Is there any alternatives? Could anyone can help me with a suggestion?

I am not sure if this a new feature. Got it working from the Caliburn Micro Documentation.
Add Windows Interactivity Assembly. You can pass to your action the Name or the Content of the Button that shall be enough to identify which button was clicked and perform the right action.
CaliburnMicro Actions
<UserControl x:Class="Caliburn.Micro.HelloParameters.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cal="http://www.caliburnproject.org">
<StackPanel>
<TextBox x:Name="Name" />
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="SayHello">
<cal:Parameter Value="{Binding ElementName=Name, Path=Text}" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</UserControl>

Related

UWP MVVM NavigationView BackButton - Detect when it is pressed

This question is in the context of using MVVM pattern. I have a NavigationView in my XAML but I have no way to detect when the user clicks on the back button. I could do it in the code behind file easily but that doesnt seems like what you want to do in an MVVM context. So is there a way to detect when the user presses the back button from a NavigationView outside of the code behind cs file? I would attach a Command Object to it and use my NavigationService where I will be able to switch the Frame. So far I use the Behaviors NuGet package to detect the clicks on the MenuItems but it doesnt work for the BackButton. Here is the Xaml so far:
<Page
x:Class="ToolBoxApp.Views.AudioHomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ToolBoxApp.Views"
xmlns:viewmodels="using:ToolBoxApp.ViewModels"
xmlns:mainview="clr-namespace:ToolBoxApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<NavigationView x:Name="navigationViewControl"
IsBackEnabled="true">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<NavigationView.MenuItems>
<NavigationViewItem Icon="MusicInfo" Content="Text to Speech"/>
<NavigationViewItem Icon="MusicInfo" Content="Youtube to Mp3"/>
</NavigationView.MenuItems>
<ScrollViewer>
<Frame SourcePageType="{Binding ScrollAudioView, Mode=TwoWay}"/>
</ScrollViewer>
</NavigationView>
</Grid>
</Page>
Why don't you simply add another EventTriggerBehavior that invokes another command for the BackRequested event?:
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="BackRequested">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding BackCommand}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>

Set TargetObject in CallMethodAction as ViewModel from parent DataContext in XAML

I'm creating a UWP app and so far I have been using the CallMethodAction to call methods from the ViewModel and it worked fine.
But now, I'm trying to use the same method for some buttons inside a ListView, and nothing happens when I click the button. The XAML code is:
<Page.DataContext>
<vm:RoomPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<ListView x:Name="ActuatorListView"
ItemsSource="{x:Bind ViewModel.Room.Actuators}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Actuator">
<Button x:Name="OnButton" Content="On">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior
EventName="Click"
SourceObject="{Binding ElementName=OnButton}">
<Core:CallMethodAction
MethodName="OnButton_Click"
TargetObject="{Binding ElementName=ViewModel, Mode=OneWay}" />
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have tried several ways to access the parent DataContext, but I found none working.
Because your view model is actually not an element, you can't use it with ElementName for binding.
The right solution in your case would be to give your page a name (x:Name="Page") and bind to its DataContext:
<Core:CallMethodAction
MethodName="OnButton_Click"
TargetObject="{Binding Path=DataContext, ElementName=Page, Mode=OneWay}" />

EventTriggerBehavior Tapped not firing

I'm using MVVM-light approach for Pushpins on my Map.
I have bound the Tapped Event to a Command in my ViewModel.
However the event is not triggered.
All the other commands and properties bind perfectly.
I have also tried as an example to use a regular event, but it's also not firing.
My Command in my VM
private RelayCommand<Check> _showDetailCheckCommand;
public RelayCommand<Check> ShowDetailCheckCommand
{
get
{
Debug.WriteLine("Binded");
return _showDetailCheckCommand ?? (_showDetailCheckCommand = new RelayCommand<Check>((c) =>
{
Debug.WriteLine("Action!");
}));
}
}
In my XAML
<Page
x:Name="pageRoot"
...
<Maps:MapControl x:Name="Map" IsEnabled="False" Margin="0,8,0,8" MapServiceToken="******" LandmarksVisible="False" PedestrianFeaturesVisible="False" TrafficFlowVisible="True" ZoomLevel="16">
<!-- Incidents -->
<Maps:MapItemsControl ItemsSource="{Binding checks}">
<Maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<Image Tag="{Binding}" Source="{Binding image_path}" Maps:MapControl.Location="{Binding geodata, Converter={StaticResource RoadsmartCoordinatesToGeoPointConverter}}" Maps:MapControl.NormalizedAnchorPoint=".5,.5" >
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding DataContext.ShowDetailCheckCommand, ElementName=pageRoot}" CommandParameter="{Binding}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Image>
</DataTemplate>
</Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>
</Maps:MapControl>
My output log does say:
'Binded'
But when I click on the image on the map the 'Action' is not executed.
Stupid of me.
I had the Map property isEnabled set to false.
Which is confusing because all the Map gestures work, but none of the binded XAML controls on top of it not.
Stupid mistake but nonetheless it might help people.

MVVM WPF bindings with RelativeSource

<DataTemplate DataType="{x:Type local:TestModel}">
<Button Content="Button" " Margin="0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<mvvm:EventToCommand
Command="{Binding ImageClick, Mode=OneWay}"
MustToggleIsEnabledValue="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
Hi all,
Above I have a data template which is switching on certain data type using a data template. I am using the mvvm pattern. I want to be to bind to the ImageClick property.
I was able to do this before, but since I have moved the button inside the data template I am unable to bind to that property.
I think I need to use Relative source for the binding but am really unsure how to do it.
Help on this would be amazing.
Thanks.
You may try the below.
<Button Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type YourViewModel}}, Path=DataContext.ImageClick}" />
I think it would help you..

WPF - Trouble binding ToolTip text on custom user control

I'm in the process of creating a simple user control; just an ImageButton.
I've already successfully bound the Image to the button and so I've decided to add a tooltip. Now I'm having troubles. It seems that I can hard-code the text for the tooltip in the XAML for the control, but when it's bound it's returning an empty string.
Here's the XAML for my control:
<Button x:Class="BCOCB.DACMS.Controls.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="this"
Style="{StaticResource DisabledButton}">
<Image Source="{Binding ElementName=this, Path=Source}" />
<Button.ToolTip>
<TextBlock Text="{Binding ElementName=this, Path=ToolTipText}" />
</Button.ToolTip>
</Button>
And here's the dependency property info for the tooltip text:
public static readonly DependencyProperty ToolTipTextProperty = DependencyProperty.Register("ToolTipText", typeof(string), typeof(ImageButton));
public string ToolTipText
{
get
{
return this.GetValue(ToolTipTextProperty) as string;
}
set
{
this.SetValue(ToolTipTextProperty, value);
}
}
And, finally, the declaration of the control in my Window:
<controls:ImageButton x:Name="btnAdd" Source="/DACMS;component/Resources/plus.png" ToolTipText="Add New Item" Click="btnAdd_Click" />
As I mentioned before, the image binds just fine and I've done it in exactly the same manner.
Any ideas?
Thanks,
Sonny
EDIT: I have it working now. I've removed the ElementName from the binding and set the TextBlock's DataContext = this in the code behind on instanciation. Still, I'd like to know how to fix this in the XAML, instead.
I'm unable to test this right now, but you can try:
<Button.ToolTip
DataContext=”{Binding Path=PlacementTarget.Parent.Parent,
RelativeSource={x:Static RelativeSource.Self}}"
>
<TextBlock Text="{Binding Path=ToolTipText}" />
</Button.ToolTip>
You may have to experiment a little with the number of "Parent" in PlacementTarget.
Hopefully this works. I don't like giving answers that I haven't tested, but I don't have VS on this computer. :)
I've had this same problem with binding to a ContextMenu. After my research I think that it is because the ToolTip and ContextMenu do not exist within the visual tree of your page/window/control. And therefore the DataContext is not inherited and makes binding troublesome.
Here is a Xaml hack I found that worked for me.
Binding to a MenuItem in a WPF Context Menu
The way to set the data context to "this" through xaml looks like this:
<Control DataContext={Binding RelativeSource={RelativeSource Self}}>
As another point, wpf buttons allow their content to be just about any (single) thing you want. If you want something other than text (ie, text and an image), it looks like this:
<Button Name="SampleButton" Click="SampleButton_Click">
<Grid Width="70" Height="62">
<Label Content="SampleText"/>
<Image Margin="3,3,3,3" Source="Graphics/sample.ico"/>
</Grid>
</Button>
Since you aren't changing anything but the Text on the tooltip TextBlock you can just use an inline declaration which will generate the TextBlock for you and doesn't require any hacking to get around the name scoping issue you're running into otherwise:
<Button ... ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=ToolTipText}">...
You could alternately set the ToolTip on the Image and use the control as the DataContext, which gets around the name scoping problem. The DataContext will be passed to the ToolTip, allowing normal binding:
<Image DataContext="{Binding ElementName=this}" Source="{Binding Source}">
<Image.ToolTip>
<TextBlock FontSize="18" Text="{Binding Path=ToolTipText}" />
</Image.ToolTip>
</Image>
This way allows additional settings on the TextBlock or more complex visuals.
This fixes the Problem with the Tooltip Bindings and Dependencies Properties:
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>
Instead of this ( doesnt Work ):
<UserControl x:Class="Extended.InputControls.TextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Extended.InputControls"
x:Name="UserControl">
<TextBox x:Name="textBox">
<TextBox.ToolTip>
<ToolTip Content="{Binding ElementName=UserControl, Path=CustomToolTip}" Background="Yellow"/>
</TextBox.ToolTip>
</TextBox>
</UserControl>

Categories

Resources