MVVM Execute Command from a User Control - c#

I have an MVVM application and part of the functionality is issuing a command in my viewmodel. The control which is bound to the command happens to reside in the rows of a DataGrid. Here is some XAML for that:
<DataGridTemplateColumn Width="25">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--<base:DeleteButton HorizontalAlignment="Center" ToolTip="Delete Polygon"
Visibility="{Binding Path=CanDeleteFromUI, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Command="{Binding Path=DataContext.DeletePolygonCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding}"/>-->
<CheckBox Template="{StaticResource RemoveXButtonTemplate}" Margin="0,0,3,0" HorizontalAlignment="Center" ToolTip="Delete Polygon" Cursor="Hand" Visibility="{Binding Path=CanDeleteFromUI, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Command="{Binding Path=DataContext.DeletePolygonCommand, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}" >
</CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Notes: For various reasons, the "button" is actually a checkbox.
This works just fine as coded. Note there is a commented-out user control, which I can't get to work properly, but the "real" checkbox works just fine.
Here is XAML for the user control:
<UserControl x:Class="Athena.Infrastructure.DeleteButton"
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" >
<Grid>
<CheckBox Command="{Binding Path=Command}" CommandParameter="{Binding Path=CommandParameter}" Cursor="Hand">
<CheckBox.Template>
<ControlTemplate>
<Border Width="14" Height="14" Background="#00000000" Margin="2,0,2,0">
....
....
</ControlTemplate>
</CheckBox.Template>
</CheckBox>
</Grid>
</UserControl>
I didn't include all the XAML for the control template, as everything displays just fine.
Here is the code-behind:
public partial class DeleteButton : UserControl
{
public DeleteButton()
{
InitializeComponent();
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(DeleteButton));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set{SetValue(CommandProperty, value);}
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("CommandParameter", typeof(object), typeof(DeleteButton));
public object CommandParameter
{
get { return (object) GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
I didn't show my VM code, but it contains the usual 3 methods, Command, CanExecute and Method. When I use the checkbox, the Command executes once (to initialize the Method), and thereafter the Method does fire when I click the checkbox.
When I use the user control instead of the normal checkbox, the Command in my VM fires once as expected. Thereafter, the method does not fire at all.
It seems that this is a common thing, to use a User Control instead of a native control, right? I can't figure out why my VM method will not fire using the User Control.
Can anybody shed some light on this for me? Thanks so much...

The first things to check in this case are the binding errors in the Visual Studio console, it can help you to target the error.
I think your problem comes from the Binding inside your UserControl. You try to bind your CheckBox's Command to the Command property of your DataContext. Instead, you have to bind it to the Command property of your UserControl, using ElementName:
<UserControl x:Class="Athena.Infrastructure.DeleteButton"
...
x:Name="deleteButton">
<Grid>
<CheckBox Command="{Binding Path=Command, ElementName=deleteButton}"
CommandParameter="{Binding Path=CommandParameter, ElementName=deleteButton}"
Cursor="Hand">
...
</CheckBox>
</Grid>
</UserControl>
Or RelativeSource:
<UserControl x:Class="Athena.Infrastructure.DeleteButton"
...
xmlns:local="clr-namespace:Athena.Infrastructure">
<Grid>
<CheckBox Command="{Binding Path=Command, RelativeSource={RelativeSource AncestorType={x:Type local:DeleteButton}}}"
CommandParameter="{Binding Path=CommandParameter, RelativeSource={RelativeSource AncestorType={x:Type local:DeleteButton}}}"
Cursor="Hand">
...
</CheckBox>
</Grid>
</UserControl>

Related

WPF firing a view model Command from a list view item

There is a WPF MVVM app. On the main view I have a list of elements, which are defined with ListView.ItemTemplate, in that I want to have a context menu with Delete action.
The Command for that is separated from the view and is kept in ViewModel DreamListingViewModel.
The problem is that on clicking on Delete I can't get it to execute the command on ViewModelk as context there is that of the item, not the items container.
I can make it work somehow by moving the context menu definition outside of the list view elements, but then when I open the context menu, it flickers, as if it's being called "20" times (which what I think does happen, as many times as I have elements in collection), anyways, I need a clean solution for that and I am very bad with XAML.
Here is how my View looks:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0 5 0 5" Background="Transparent" Width="auto">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding DeleteSelectedDream}"
CommandParameter="{Binding DeleteSelectedDream,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type viewmodels:DreamListingViewModel}}}"
/>
</ContextMenu>
</Grid.ContextMenu>
...
It's the main window and initialized in a generic host in App.cs:
public partial class App : Application
{
private readonly IHost _host;
public App()
{
...
_host = Host.CreateDefaultBuilder().ConfigureServices(services =>
{
...
services.AddTransient<DreamListingViewModel>();
services.AddSingleton((s) => new DreamListingView()
{
DataContext = s.GetRequiredService<DreamListingViewModel>()
});
...
}).Build();
The Command and CommandParameter values are what I've been experimenting with, but it doesn't work
Here is how my ViewModel looks:
internal class DreamListingViewModel : ViewModelBase
{
public ICommand DeleteSelectedDream{ get; }
...
Finally, when the command is fired, I need to pass the current element on which the menu has been shown.
So, here is what I want:
User clicks on a list item with mouse right button - OK
Sees a menu with Delete entry - OK
On Delete click, Command DeleteSelectedDream is fired with current dream (item in the list) as a parameter - ERR
Your example is somewhat lacking necessary information, but I'll try to help.
First you need to verify that you are actually bound to your view model. Are you using Prism or just standard WPF ? In the constructor of your code-behind of your view, set up the DataContext to an instance of your VM.
InitializeComponent();
this.DataContext = new DreamListingViewModel();
Now, you bind to a relative source via Mode 'FindAncestor' and the AncestorType is set to the type of a view model. That usually won't work, as the view model is not naturally a part of the visual tree of your WPF view. Maybe your ItemTemplate somehow wires it up. In a large WPF app of mine I use Telerik UI for WPF and a similar approach to you, however, I set up the DataContext of the Context menu to a RelativeSource set to Self combined with Path set to PlacementTarget.DataContext.
You do not have to use all the XAML in my example, just observe how I do it. Exchange 'RadContextMenu' with 'ContextMenu', Ignore the Norwegian words - here and only use what you need :
<telerik:RadContextMenu x:Key="CanceledOperationsViewContextMenu" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, UpdateSourceTrigger=PropertyChanged}">
<MenuItem Header="{Binding PatientName}" IsEnabled="False" Style="{StaticResource ContextMenuHeading}" />
<MenuItem Header="Gå til aktuell SomeAcme-liste" IsEnabled="{Binding IsValid}" Command="{Binding NavigateToListCommand}" />
<MenuItem Header="Åpne protokoll..." Command="{Binding CommonFirstProtocolCommand, Mode=OneWay}" CommandParameter="{Binding}" />
<MenuItem Header="Åpne Opr.spl.rapport...." Command="{Binding CommonFirstNurseReportCommand, Mode=OneWay}" CommandParameter="{Binding}" />
</telerik:RadContextMenu>
In your example it will be :
<ContextMenu x:Key="SomeContextMenu" DataContext="{Binding RelativeSource={RelativeSource Mode=Self}, Path=PlacementTarget.DataContext, UpdateSourceTrigger=PropertyChanged}">
<MenuItem Header="Delete" />
Command="{Binding DeleteSelectedDream}"
CommandParameter="{Binding DeleteSelectedDream,
RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type ListViewItem}}}"
/>
</telerik:RadContextMenu>
Now I here consider you are using the class ListViewItem
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.listviewitem?view=netframework-4.8
It might be that you need to specify DataContext.DeleteSelectedDream here to be sure you bind up to the DataContext where your implementation of ICommand is.
Accidentally found this answer, that's basically what I needed, just added to it a CommandParameter to send the item and it works like magic!
<ListView Name="lvDreams" ItemsSource="{Binding Dreams}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="0 5 0 5" Background="Transparent" Width="auto">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem
Header="Delete"
Command="{Binding DataContext.DeleteSelectedDream, Source={x:Reference lvDreams}}"
CommandParameter="{Binding}"
/>
</ContextMenu>
</Grid.ContextMenu>
...
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I find the following the simplest; perhaps it's because I do not understand WPF, but it's "simple" to remember, and it works with my MVVM pattern.
<ListBox ItemsSource="{Binding MyViewModelItemsCollection, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent" >
<TextBlock Text="{Binding Path=Name, Converter={StaticResource FullPathToFileName}, Mode=OneWay}" Grid.Column="0">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem
Command="{Binding Path=DataContext.MyViewModelAction, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}"
Header="{Binding Name, Converter={StaticResource resourceFormat}, ConverterParameter={x:Static res:Resources.CONTEXT_MENU_BLOCK_APPLICATION}}">
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</grid>
</DataTemplate>
</ListBox.ItemTemplate
</ListBox>
The MyViewModelXXXXXXX named items are in the view model that is mapped to the data context of the control.

WPF: Binding the command parameter for ListBox ContextMenu to the Selected Item of the ListBox

Is it possible to bind the CommandParameter for a ListBox ContextMenu to the Selected Item of the ListBox? I should say that the ContCommand is in the main window and it is called when the Context Menu item is clicked - however, I need to get the parameter to work properly.
I tried this but the binding fails:
<Window x:Class="ListBoxContextMenu.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ListBoxContextMenu"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock Text="ListBox here:"/>
<ListBox ItemsSource="{Binding Items}" MinHeight="100" TabIndex="0" x:Name="LB">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Foo" Command="{Binding ContCommand}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}},Path=SelectedItem}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</StackPanel>
</Grid>
</Window>
C# code for MainWindow:
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using MvvmFoundation.Wpf;
namespace ListBoxContextMenu
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += (sender, e) => MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
ContCommand = new RelayCommand<object>((object o) =>
{
System.Diagnostics.Debug.WriteLine("Context Menu pressed");
});
}
public ObservableCollection<string> Items { get; set; } = new ObservableCollection<string>{"Fred", "Jim", "Sheila"};
public RelayCommand<object> ContCommand { get; set; }
}
}
The ListBox is not a visual ancestor of the ContextMenu because the latter resides in its own visual tree.
But you could bind to the PlacementTarget of the ContextMenu, which is the ListBox.
This works:
<ListBox ItemsSource="{Binding Items}" MinHeight="100" TabIndex="0" x:Name="LB">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Foo" Command="{Binding ContCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.SelectedItem}"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
The context menu is on a different tree and so binding is tricky depending on the situation. Here are two options:
1
Bind to the listbox via its name such as
Binding SelectedItem, ElementName=LB
2 Use the reference name
Sometimes an element name binding fails and one has to use the x:ref name (which you have)
Binding Source={x:Reference LB}, Path=SelectedItem
As to the why, to quote x:Reference
In WPF and XAML 2006, element references are addressed by the framework-level feature of ElementName binding. For most WPF applications and scenarios, ElementName binding should still be used. Exceptions to this general guidance might include cases where there are data context or other scoping considerations that make data binding impractical and where markup compilation is not involved.
instead of binding it to the listbox bind it to the listboxitem that have been clicked
he is the concerned !! not the listbox he hold the object that you are seeking
<ListBox x:Name="lstAllTags" FocusVisualStyle="{x:Null}" ItemsSource="{Binding ResearchedTagsResult}" Margin="0" Background="{x:Null}" BorderBrush="{x:Null}" ItemTemplate="{DynamicResource SearchTagDataTemplate}" FontFamily="Consolas" Foreground="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" MouseMove="LstAllTags_MouseMove" MouseLeave="LstAllTags_MouseLeave" HorizontalContentAlignment="Stretch" Focusable="False" FontSize="13" SelectionChanged="LstTags_SelectionChanged" BorderThickness="0">
<ListBox.Resources>
<!--Defines a context menu-->
<ContextMenu x:Key="ContextMenu">
<MenuItem Command="{Binding DeleteTagCmd }" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=DataContext}" Foreground="{DynamicResource AppTextForeground}" DataContext="{DynamicResource TagManagement_instance}" Header="Edit" BorderBrush="#FF919191" BorderThickness="0" Padding="0">
<MenuItem.Icon>
<Image Source="/Resx/pencil.png"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.Resources>
</ListBox>
Add Mode=FindAncestor to the RelativeSource binding.
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=SelectedItem}"

UWP Databinding: How to set button command to parent DataContext inside DataTemplate

Short explanation of need: I need to fire the command of a button inside a DataTemplate, using a method from the DataContext of the ViewModel.
Short explanation of problem: The templated button command only seems to be bindable to the datacontext of the item itself. The syntax used by WPF and Windows 8.1 apps to walk up the visual tree doesn't seem to work, including ElementName and Ancestor binding. I would very much prefer not to have my button command located inside the MODEL.
Side Note: This is built with the MVVM design method.
The below code generates the list of items on the VIEW. That list is one button for each list item.
<ItemsControl x:Name="listView" Tag="listOfStories" Grid.Row="0" Grid.Column="1"
ItemsSource="{x:Bind ViewModel.ListOfStories}"
ItemTemplate="{StaticResource storyTemplate}"
Background="Transparent"
IsRightTapEnabled="False"
IsHoldingEnabled="False"
IsDoubleTapEnabled="False"
/>
Inside the page resources of the same VIEW, I have created a DataTemplate, containing the problematic button in question. I went ahead and stripped out most of the formatting inside the button, such as text, to make the code easier to read on this side. Everything concerning the button works, except for the problem listed, which is the binding of the command.
<Page.Resources>
<DataTemplate x:Name="storyTemplate" x:DataType="m:Story">
<Button
Margin="0,6,0,0"
Width="{Binding ColumnDefinitions[1].ActualWidth, ElementName=storyGrid, Mode=OneWay}"
HorizontalContentAlignment="Stretch"
CommandParameter="{Binding DataContext, ElementName=Page}"
Command="{Binding Source={StaticResource Locator}}">
<StackPanel HorizontalAlignment="Stretch" >
<TextBlock Text="{x:Bind StoryTitle, Mode=OneWay}"
FontSize="30"
TextTrimming="WordEllipsis"
TextAlignment="Left"/>
</StackPanel>
</Button>
</DataTemplate>
</Page.Resources>
Because this is a DataTemplate, the DataContext has been set to the individual items that comprise the list (MODEL). What I need to do is select the DataContext of the list itself (VIEWMODEL), so I can then access a navigation command.
If you are interested in the code-behind of the VIEW page, please see below.
public sealed partial class ChooseStoryToPlay_View : Page
{
public ChooseStoryToPlay_View()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => { ViewModel = DataContext as ChooseStoryToPlay_ViewModel; };
}
public ChooseStoryToPlay_ViewModel ViewModel { get; set; }
}
I've tried setting it by ElementName, among many other attempts, but all have failed. Intellisense detects "storyTemplate" as an option when ElementName is input, which is the name of the DataTemplate shown in the first code block of this question.
I don't believe my problem can be unique, however I'm having great difficulty finding a solution for UWP. Allow me to apologize in advance in this is a simple question, but I've spent nearly two days researching answers, with none seeming to work for UWP.
Thank you guys!
What MVVM toolkit are you using (if any)? In MVVM Light, you can get a hold of ViewModel from DataTemplate same way you set DataContext for your view:
<DataTemplate x:Key="SomeTemplate">
<Button Command="{Binding Main.MyCommand, Source={StaticResource ViewModelLocator}}"/>
</DataTemplate>
It really is unfortunate that there is no ancestor binding in UWP. This makes scenarios like yours much more difficult to implement.
The only way I can think of is to create a DependencyProperty for ViewModel on your Page:
public ChooseStoryToPlay_ViewModel ViewModel
{
get { return (ChooseStoryToPlay_ViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(ChooseStoryToPlay_ViewModel), typeof(MainPage), new PropertyMetadata(0));
Now you can bind to it from your data template:
<DataTemplate x:Name="storyTemplate" x:DataType="local:Story">
<Button
Margin="0,6,0,0"
Width="{Binding ColumnDefinitions[1].ActualWidth, ElementName=storyGrid, Mode=OneWay}"
HorizontalContentAlignment="Stretch"
CommandParameter="{x:Bind Page}"
Command="{Binding ViewModel.NavigateCommand, ElementName=Page}">
<StackPanel HorizontalAlignment="Stretch" >
<TextBlock Text="{x:Bind StoryTitle, Mode=OneWay}"
FontSize="30"
TextTrimming="WordEllipsis"
TextAlignment="Left"/>
</StackPanel>
</Button>
</DataTemplate>
A couple of things to notice:
In CommandParameter I assumed that in your Story class there is a Page property that you want to pass as a parameter to your command. You can bind to any other property of Story class here or the class itself.
You have to set the name of your page to Page (x:name="Page"), so that you can reference it using ElementName in the data template.
I assumed that the command you're calling on the ViewModel is named NavigateCommand and accepts a parameter of the same type as the property bound to CommandParameter:
public ICommand NavigateCommand { get; } =
new RelayCommand<string>(name => Debug.WriteLine(name));
I hope this helps and is applicable to your scenario.
There is a few ways to do that. But i think the Command change better...
Example, you have a (grid,list)view with some itemtemplate like that:
<GridView.ItemTemplate>
<DataTemplate>
<Grid
x:Name="gdVehicleImage"
Height="140"
Width="140"
Background="Gray"
Margin="2"
>
</Grid>
</GridView.ItemTemplate>
And do you want to make a command to for example a FlyoutMenu... But the command it's in the ViewModel and not in GridView.SelectedItem...
What you can do is...
<Grid
x:Name="gdVehicleImage"
Height="140"
Width="140"
Background="Gray"
Margin="2"
>
<FlyoutBase.AttachedFlyout>
<MenuFlyout
Opened="MenuFlyout_Opened"
Closed="MenuFlyout_Closed"
>
<MenuFlyout.MenuFlyoutPresenterStyle>
<Style TargetType="MenuFlyoutPresenter">
<Setter Property="Background" Value="DarkCyan"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</MenuFlyout.MenuFlyoutPresenterStyle>
<MenuFlyoutItem
Loaded="mfiSetAsDefaultPic_Loaded"
CommandParameter="{Binding}"
/>
<MenuFlyoutItem
Loaded="mfiDeletePic_Loaded"
CommandParameter="{Binding}"
/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
</Grid>
And in the loaded events:
private void mfiDeletePic_Loaded(object sender, RoutedEventArgs e)
{
var m = (MenuFlyoutItem)sender;
if (m != null)
{
m.Command = Vm.DeleteImageCommand;
//Vm is the ViewModel instance...
}
}
Is not entirely beautiful... But you willnot breake mvvm pattern like this...

How to catch a custom event with Interactivity.EventTrigger

I'm trying to catch my custom event (TextUpdateEvent) with Interactivity.EventTrigger in the way shown below. But this code gives rise to System.ArgumentException with the message that "The event "MyTextBox" was not found in the type "tool:MyTextBox.TextUpdateEvent"." Could you tell me what is wrong with this?
<UserControl x:Class="mynamespace.MyControl"
xmlns:local="clr-namespace:mynamespace"
xmlns:info="clr-namespace:mynamespace.info"
xmlns:tool="clr-namespace:mynamespace.tool"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Border>
<ItemsControl ItemsSource="{Binding MyItems}" Name="itemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="info:MyInfo">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding MyComment}"/>
<tool:MyTextBox Text="{Binding MyName}" x:Name="myTextBox">
<i:Interaction.Triggers>
<i:EventTrigger EventName="tool:MyTextBox.TextUpdateEvent"
SourceObject="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type tool:MyTextBox}}}"
SourceName="myTextBox">
<ei:CallMethodAction MethodName="UpdateMyInfo" TargetObject="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Mode=TwoWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</tool:MyTextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Grid>
namespace mynamespace.tool
{
//used to judge whether or not the update suceeded
public delegate bool MyTextBoxUpdateHandler(object sender);
public partial class MyTextBox : TextBox
{
public MyTextBoxUpdateHandler TextUpdateEvent { get; set; }
}
}
Or, do I need to create a custom trigger to catch my custom event?
public class TextUpdateEventTrigger : EventTriggerBase<MyTextBox>
{
// I don't know what I should do here,
// since the event I'd like to catch is not a RoutedEvent.
}
You should change the TextUpdateEvent to an event instead of a property.
public event MyTextBoxUpdateHandler TextUpdateEvent;
Since you're calling a method in code-behind you also do not need to attach Interaction triggers:
<tool:MyTextBox
x:Name="myTextBox"
Text="{Binding MyName}"
TextUpdateEvent="UpdateMyInfo" />
The DataContext Property does not need to be bound on the UserControl itself, because it's already a partial class of the UserControl, you can remove that too.

How to capture dynamic button clicks in ViewModel

I have created a bunch of buttons by binding an ObservableCollection<module>.
In my ViewModel I would like to capture the click event.
For buttons I usually use:
RelayCommand launchCommand;
public ICommand LaunchCommand{
get{
if (launchCommand == null){
launchCommand = new RelayCommand(LaunchCommandExecute, CanLaunchCommandExecute);
}
return launchCommand;
}
}
private void LaunchCommandExecute(object parameter){
//Do something to recognize the button.
//Could use ObservableCollection<Module> module_objects
//to match, if I could get the buttons content or name
}
private bool CanLaunchCommandExecute(object parameter){
return true;
}
In LaunchCommandExecute I have placed a couple of thoughts. I would be interested in what object parameter holds? Is it anything useful to me?
The button has the following bindings which I could use to match up:
<Button Tag="{Binding ModuleName}" Content="{Binding ModuleAbbreviation}" Command="{Binding LaunchCommand}" IsEnabled="{Binding ModuleDisabled}" Style="{DynamicResource LauncherButton}" Background="{Binding ModuleColor}" />
Does anyone know how to do this?
[EDIT] This is after accepting the answer below
What I am finding is that LaunchCommand is not firing. I was wondering if anything in the below code is conflicting?
<UserControl.DataContext>
<viewmodel:LauncherViewModel />
</UserControl.DataContext>
<Grid >
<ItemsControl ItemsSource="{Binding Source={x:Static m:ModuleKey._module_objects}}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<my:AlignableWrapPanel HorizontalAlignment="Stretch" Name="alignableWrapPanel1" VerticalAlignment="Center" HorizontalContentAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Button Content="{Binding ModuleAbbreviation}" Command="{Binding LaunchCommand}" CommandParameter="{Binding ModuleName}" IsEnabled="{Binding ModuleDisabled}" Style="{DynamicResource LauncherButton}" Background="{Binding ModuleColor}" FontSize="32" FontFamily="Tahoma" Width="130" Height="100" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
[EDIT Answer] Nevermind thought about what I was trying to do and found that the command could not see the correct DataContext. Adding the following sorted it:
Command="{Binding DataContext.LaunchCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
The parameter is set by CommandParameter. In this case, all you have to do is bind it to "ModuleName":
<Button Command="{Binding LaunchCommand}" CommandParameter="{Binding ModuleName}" ...
Pick it up using a cast - assuming it's a string:
private void LaunchCommandExecute(object parameter){
string moduleName = parameter as string;
// ...
}
(Note that you could also set CommandParameter to the Button's Tag or Content by using {Binding RelativeSource={RelativeSource Self},Path=Tag}, but that would be a round-about approach in this case.)

Categories

Resources