Interaction triggers inside DataTemplate not working with XamlReader - c#

I'm trying to parse with XamlReader.Load() a DataTemplate (for a WPF datagrid) created dynamically in code behind :
DataTemplate dataTemplate;
StringReader template = new StringReader($#"
<DataTemplate
xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""
xmlns:local=""clr-namespace:MyApp;assembly=MyApp"">
<DataTemplate.Resources>
<local:ArrayMultiValueConverter x:Key=""arrayMultiValueConverter""/>
</DataTemplate.Resources>
<StackPanel Orientation=""Vertical"">
<Expander VerticalAlignment=""Center"" xmlns:i=""http://schemas.microsoft.com/xaml/behaviors"">
<i:Interaction.Triggers>
<i:EventTrigger EventName=""IsExpanded"">
<i:InvokeCommandAction Command=""{{Binding DataContext.TextFilterColumn}}""/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Expander.Header>
<TextBlock x:Name=""{dtColumnName}"" VerticalAlignment=""Center"" Text=""{{TemplateBinding Content}}"" Margin=""5,5,5,0"" FontWeight=""SemiBold""/>
</Expander.Header>
<StackPanel Orientation=""Horizontal"">
<TextBox x:Name=""{"TbxFilter" + dtColumnName}"" Width=""100"" Margin=""5""/>
<TextBlock Margin=""0,0,5,0"" VerticalAlignment=""Center"" FontFamily=""Segoe MDL2 Assets"">
<Hyperlink TextDecorations=""{{x:Null}}"" Command=""{{Binding DataContext.FilterColumnName, RelativeSource={{RelativeSource FindAncestor, AncestorType={{x:Type Window}}}}}}"">
<Hyperlink.CommandParameter>
<MultiBinding Converter=""{{StaticResource arrayMultiValueConverter}}"">
<Binding Path=""Text"" ElementName=""{dtColumnName}"" />
<Binding Path=""Text"" ElementName=""{"TbxFilter" + dtColumnName}"" />
</MultiBinding>
</Hyperlink.CommandParameter>

</Hyperlink>
</TextBlock>
<TextBlock Margin=""0,0,5,0"" VerticalAlignment=""Center"" FontFamily=""Segoe MDL2 Assets"">
<Hyperlink TextDecorations=""{{x:Null}}"" Command=""{{Binding DataContext.FilterColumnName, RelativeSource={{RelativeSource FindAncestor, AncestorType={{x:Type Window}}}}}}"">
<Hyperlink.CommandParameter>
<MultiBinding Converter=""{{StaticResource arrayMultiValueConverter}}"">
<Binding Path=""Text"" ElementName=""{dtColumnName}""/>
<Binding Path=""Text"" RelativeSource=""{{RelativeSource FindAncestor, AncestorType={{x:Type TextBlock}}}}""/>
</MultiBinding>
</Hyperlink.CommandParameter>

</Hyperlink>
</TextBlock>
</StackPanel>
</Expander>
</StackPanel>
</DataTemplate>
");
XmlReader xmlReader = XmlReader.Create(template);
dataTemplate = XamlReader.Load(xmlReader) as DataTemplate;
textColumn.HeaderTemplate = dataTemplate;
All is working when I remove this part of code :
<i:Interaction.Triggers>
<i:EventTrigger EventName=""IsExpanded"">
<i:InvokeCommandAction Command=""{{Binding DataContext.TextFilterColumn}}""/>
</i:EventTrigger>
</i:Interaction.Triggers>
But when I add it, there is an Exception Thrown :
System.Windows.Markup.XamlParseException: ''Cannot set unknown member
'{http://schemas.microsoft.com/xaml/behaviors}Interaction.Triggers'.'
Line number '11' and line position '10'.'
I use "XAML Behaviors" following this article (but the same happened with Interactivity) :
https://devblogs.microsoft.com/dotnet/open-sourcing-xaml-behaviors-for-wpf/
It seems to be an issue with XamlReader.Load(xmlReader).
If someone knows a workaround, I will be grateful.
Configuration :
Framework 4.8 (tried with 4.7.2)
Microsoft.Xaml.Behaviors.Wpf
1.1.39
Thanks.

It's not mentioned in the doucmentation of XamlReader.Load but any custom assemblies referenced in a XAML namespace mapping must already be available to the application.
You have two options:
1.Load assembly Microsoft.Xaml.Behaviors or initialize some type from the assembly before reading xaml input.
Assembly assembly = Assembly.LoadFrom("Microsoft.Xaml.Behaviors.dll");
or
var et = new Microsoft.Xaml.Behaviors.EventTrigger();
2.Use the CLR namespace declaration in xaml
xmlns:i=""clr-namespace:Microsoft.Xaml.Behaviors;assembly=Microsoft.Xaml.Behaviors""

Related

How to bind button to a delegated command?

I have a list that looks like as follows. In the list I want buttons that do something through a delegated command.
<dxe:ListBoxEdit
Grid.Row="0"
Grid.Column="5"
Margin="0,50,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
DisplayMember="Name"
ItemSource="{Binding Items}"
ItemTemplate="{StaticResource ItemTemplate}"
Name="lista"
SelectionMode="Single"
ScrollViewer.CanContentScroll="True">
</dxe:ListBoxEdit>
and dataTemplate look like this:
<Window.Resources>
<DataTemplate x:Key="ProductsTemplate" >
<dxe:ListBoxEditItem>
<DockPanel>
<Image Source="{Binding Picture}" HorizontalAlignment="Left" Margin="25 50"/>
<TextBlock FontSize="14" TextWrapping="Wrap">
<TextBlock.Text >
<MultiBinding StringFormat="{}{0} Price: {1} TaxRateLevel: {2} ">
<Binding Path="Name" />
<Binding Path="Price" />
<Binding Path="TaxRateLevel" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<Button Content="Add" Command ="{Binding AddCommand}"></Button>
</DockPanel>
</dxe:ListBoxEditItem>
</DataTemplate>
</Window.Resources>
If you are using MVVM (Model-View-ViewModel) you can achieve the result with RelayCommand.
In your ViewModel file, you need to declare your command:
public RelayCommand YourCommand { get; set; }
Then, you need to initialize it (in the view model constructor for example):
YourCommand = new RelayCommand(YourMethodName);
Finally, for the XAML, I have this example of a ListBox that you can adapt for your specific situation:
<ListBox
x:Name="Files"
ItemsSource="{Binding Items, Mode=TwoWay}"
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.InputBindings>
<MouseBinding
Gesture="LeftDoubleClick"
Command="{Binding YourCommand }"/>
</TextBlock.InputBindings>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As you can see in this example, we have a ListBox that is binding some data. To set the command, we access TextBlock inside ListBox.ItemTemplate and add the command for the gesture LeftDoubleClick.
If the ViewModel is set to the Window's DataContext, and the AddCommand is set at the main level of the ViewModel, then the command must know what elements it works with.
That is, the command necessarily needs parameter processing.
The data required for the command is retrieved from the parameter.
How exactly this is done depends on the framework you use.
For binding, you can do this:
Give a name to the Window <Window x:Name="main"....
Bind to the DataContext of the Window using binding of the ElementName type and passing the current element in the parameter:
XAML:
<Button Content="Add"
Command ="{Binding DataContext.AddCommand, ElementName=main}"
CommandParameter="{Binding}"/>
Also, the layout of the elements is not clear from your question.
In the ListBox, you are using a template named "ItemTemplate" for the item, but you didn't show it.
You are showing the "ProductsTemplate" template, but where it is used is not clear.

ComboBox is displaying System.Data.DataRowView no matter what is selected. ItemTemplate Issue

I have a ComboBox that needs to display multiple fields in it's drop down. but only wants to store one. Right now it works to display multiple fields. but no matter what I select, the value shown in the textbox is "System.Data.DataRowView". Does anyone know why this is or how to fix it?
<ComboBox ItemsSource="{Binding Vwr.Table.Tbl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="Supplier"
SelectedValuePath="Name"
SelectedValue="Name"
IsEditable="True" Style="{StaticResource tabTextBox}"
DataContext="{Binding parties}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SupplierChangedCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}: {1}: {2}">
<Binding Path="Name"/>
<Binding Path="Funds"/>
<Binding Path="Terms"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Found the answer here :http://www.shujaat.net/2010/08/wpf-editable-combobox-with-datatemplate.html
The solution was to set TextSearch.TextPath. In my case, it was
TextSearch.TextPath="Name"
Updating my original code (unnecessary parts removed for brevity), the working version is here
<ComboBox
x:Name="Supplier"
ItemsSource="{Binding Vwr.Table.Tbl}"
SelectedValuePath="Name"
SelectedValue="Name"
IsEditable="True" Style="{StaticResource tabTextBox}"
TextSearch.TextPath="Name"
DataContext="{Binding parties}">
Hope this helps people out there!

Binding command - Ancestor

Okay, guys. I've been trying this for about 3 days now, and no amount of Googling is helping. Below is a snippet of my XAML (should be enough to follow along).
My problem is the command for the "ContextMenu".
As you can see I have DeleteTagCommand. Now that command works if I throw it in the position of CheckBoxCommand, which is great.. But it just will be called in it's current location, and it's driving me insane.
<ScrollViewer Grid.Column="0">
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding Tags, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Value}" Margin="10,5,10,5" Command="{Binding DataContext.CheckBoxCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Grid}}}"
CommandParameter="{Binding }">
<CheckBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding DataContext.DeleteTagCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Grid}}}"
CommandParameter="{Binding}" />
</ContextMenu>
</CheckBox.ContextMenu>
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
I've tried:
Calling 'ElementName' from all over the show, but it's never being picked up
Changing the 'AncestorLevel' to obscene numbers, hoping that was the problem
And more..
Not sure what would be useful for you guys, but below is the output message I get
Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.Grid', AncestorLevel='1''. BindingExpression:Path=DataContext.DeleteTagCommand; DataItem=null; target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')
Thanks
ContextMenus aren't actually part of the same visual tree as their parents so they can't directly bind to any elements within it. They can, however, still bind to StaticResources. The trick is to thus use an intermediate proxy such as the BindingProxy class shown on this page. Start by adding an instance to your ItemsControl resource block:
<ItemsControl.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</ItemsControl.Resources>
Then use it to bind your ContextMenu command:
<ContextMenu>
<MenuItem Header="Delete" Command="{Binding Data.DeleteTagCommand, Source={StaticResource Proxy}}" CommandParameter="{Binding}" />
</ContextMenu>

How do I bind a command to a Listbox Item?

I have a Listbox. I have the ItemSource of the Listbox bound to a list.
Within the ItemTemplate, I want to be able to add a MouseBinding to each item.
Here's what I have so far:
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="Blue" CornerRadius="8" >
<Border.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{Binding Test}" CommandParameter="{Binding PropertyOfClickItem}" />
</Border.InputBindings>
<TextBlock Foreground="White" FontSize="24" FontWeight="Bold" Margin="5">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0}, {1}
Seat: {2}">
<Binding Path="LastName" />
<Binding Path="FirstName" />
<Binding Path="Seat" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
I'm confused, because it seems that I can only bind to things within my ItemSource, but my "Test" command is not within that. How can I bind to a Command that's in the ViewModel for the View, instead of the ItemSource that's bound to the Listbox?
Not only that, but I want to be able to pass a property of the selected item to the command. Is this possible?
You can use the RelativeSource property of the binding to get an ancestor that has the DataContext you want. For example:
Command="{Binding DataContext.Test, RelativeSource={RelativeSource AncestorType={x:Type ListBox}}}"
Use a Relative Source like clcto said.
Look here for a complete answer:
WPF "static" binding in a List

Apply multiple AttachedCommandBehaviors using a style

I am trying to use AttachedCommandBehavior V2 to translate ListBoxItem events such as double click into commands that are execute against the view model.
I want to fire commands for multiple events, this is the example code I am trying to emulate:
<Border Background="Yellow" Width="350" Margin="0,0,10,0" Height="35" CornerRadius="2" x:Name="test">
<local:CommandBehaviorCollection.Behaviors>
<local:BehaviorBinding Event="MouseLeftButtonDown" Action="{Binding DoSomething}" CommandParameter="An Action on MouseLeftButtonDown"/>
<local:BehaviorBinding Event="MouseRightButtonDown" Command="{Binding SomeCommand}" CommandParameter="A Command on MouseRightButtonDown"/>
</local:CommandBehaviorCollection.Behaviors>
<TextBlock Text="MouseDown on this border to execute the command"/>
</Border>
Since I want to apply that to a ListBoxItem, I am trying to do it through a style by doing:
<ListBox.ItemContainerStyle>
<Style>
<Setter Property="acb:CommandBehaviorCollection.Behaviors">
<Setter.Value>
<acb:CommandBehaviorCollection>
<acb:BehaviorBinding Event="MouseDoubleClick" Command="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
<acb:BehaviorBinding Event="KeyUp" Command="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding}"/>
</acb:CommandBehaviorCollection>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
But I get a compile error with that code that says error MC3089: The object 'CommandBehaviorCollection' already has a child and cannot add 'BehaviorBinding'. 'CommandBehaviorCollection' can accept only one child. Line 39 Position 11.
Also if I comment out one of the BehaviorBindings then it compiles but I get a runtime xaml load exception saying "Value cannot be null. Parameter name: property", so I'm not sure if I'm even taking the correct approach.
Can anyone provide an example of the correct syntax to set multiple behavior bindings on a ListBoxItem?
My solution uses interaction triggers and the ItemTemplate not the ItemContainerStyle.
This invokes a mouse double click or key up command in the text box, not the whole list box item.
<UserControl.Resources>
<DataTemplate DataType="{x:Type ViewModel:DataItem}" x:Key="ItemTemplate">
<ContentControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding DoubleClickCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="KeyUp">
<i:InvokeCommandAction Command="{Binding KeyUpCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox Text="{Binding Name}">
</TextBox>
</ContentControl>
</DataTemplate>
</UserControl.Resources>
<ListBox x:Name="listBox" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource={Binding Items} />
Where DataItem is something like
class DataItem : INotifyPropertyChanged
{
public string Name{get;set}
.. etc
}
and the view model set on the DataContext has an IList<DataItems> Items{get; private set} property.

Categories

Resources