I wish to create a "reusable" control item which has a behaviour attribute. As described in this blog
For the data binding the data has to be converted. The problem is; as StaticResource it can only see the top level dictionary (app), and dynamic resources don't work ("can only be used with a dependency property").
The simple (working) xaml (window):
<Window x:Class="testit.MainWindow"
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"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Rectangle x:Name="blah" Fill="Yellow" Stroke="Black" Width="100" Height="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter" >
<i:InvokeCommandAction Command="{Binding MouseEnterCommand}">
<i:InvokeCommandAction.CommandParameter>
<Binding Path="Name"
Converter="{StaticResource SelectionConverter}"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Rectangle}" />
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
<TextBlock x:Name="namefield" Text="{Binding Data}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
</Window>
app:
<Application x:Class="testit.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:testit"
StartupUri="MainWindow.xaml">
<Application.Resources>
<local:SelectionConverter x:Key="SelectionConverter" />
</Application.Resources>
</Application>
And finally the viewmodel:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Data;
namespace testit
{
class ViewModel : INotifyPropertyChanged
{
private string _data;
private readonly DelegateCommand<string> _mouseEnterCommand = null;
public ViewModel() {
_data = "hello ";
_mouseEnterCommand = new DelegateCommand<string>(
(s) => {
var a = s ?? "world ";
Data += s;
return;
});
}
public DelegateCommand<string> MouseEnterCommand => _mouseEnterCommand;
public string Data {
get { return _data; }
set {
if (value == _data) return;
_data = value;
OnPropertyChanged("Data");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SelectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value == null) return "";
char[] charArray = ((string)value).ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
}
Now as said, this "works". However it doesn't do what I wish: it clutters the top level resource dictionary. I wish to remove the resource from app. and put it under the Grid. (Or whatever panel I use).
<Grid.Resources>
<local:SelectionConverter x:Key="SelectionConverter" />
</Grid.Resources>
EDIT:If I add above line to the grid inside the window the following error is thrown on compilation (the line and position refer to the Converter="{StaticResource SelectionConverter}")
"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '13' and line position '38'."
With the inner exception:
Cannot find resource named 'SelectionConverter'. Resource names are case sensitive.
For clarity, this is the modified window.xaml:
<Window x:Class="testit.MainWindow"
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:local="clr-namespace:testit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Rectangle x:Name="blah" Fill="Yellow" Stroke="Black" Width="100" Height="100">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter" >
<i:InvokeCommandAction Command="{Binding MouseEnterCommand}">
<i:InvokeCommandAction.CommandParameter>
<Binding Path="Name"
Converter="{StaticResource SelectionConverter}"
RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Rectangle}" />
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Rectangle>
<TextBlock x:Name="namefield" Text="{Binding Data}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Grid.Resources>
<local:SelectionConverter x:Key="SelectionConverter" />
</Grid.Resources>
</Grid>
</Window>