I use MVVM mode.
There are two ViewModels:
ClientViewModel and DiskViewModel, ClientViewModel has a collection ObservableCollection<DiskViewModel>.
I want to display their hierarchical relationship and do something via their ContextMenu. I set different ContextMenu via TypeToBooleanConverter in DataTrigger in TreeViewItem like this:
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Value="true" Binding="{Binding Converter={cvt:TypeToBooleanConverter},ConverterParameter={x:Type vm:ClientViewModel}}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Client"/>
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
But there is a problem:
If I only set ContextMenu for ClientViewModel via above method, not set ContextMenu for DiskViewModel, it will be show "Client" if right-click on Disk TreeViewItem. But I never set it.
With a picture: this is not I need. I want to nothing if I right-click on Disk TreeViewItem.
Above scene is the simplest. Actually hierarchical relationship is that:
ClientList
|---Client
|---DiskList
|---Disk
|---PartitionList
|---Partition
Different types of item or one type time with different status has different ContextMenu. Enumerate all cases may be a hadr work.
I Need your help.
Code files:
// MainViewModel.cs
using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
namespace WpfApplication12.ViewModel
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
ClientList = new ObservableCollection<ClientViewModel>();
var disk = new DiskViewModel();
disk.Name = "disk1";
var client = new ClientViewModel();
client.Name = "client1";
client.DiskList.Add(disk);
ClientList.Add(client);
}
#region ClientList
public const string ClientListPropertyName = "ClientList";
private ObservableCollection<ClientViewModel> _clientList;
public ObservableCollection<ClientViewModel> ClientList
{
get
{
return _clientList;
}
set
{
if (_clientList == value)
return;
_clientList = value;
RaisePropertyChanged(ClientListPropertyName);
}
}
#endregion // ClientList
}
public class ClientViewModel : ViewModelBase
{
public ClientViewModel()
{
DiskList = new ObservableCollection<DiskViewModel>();
}
#region Name
public const string NamePropertyName = "Name";
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
return;
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion // Name
#region DiskList
public const string DiskListPropertyName = "DiskList";
private ObservableCollection<DiskViewModel> _diskList;
public ObservableCollection<DiskViewModel> DiskList
{
get
{
return _diskList;
}
set
{
if (_diskList == value)
return;
_diskList = value;
RaisePropertyChanged(DiskListPropertyName);
}
}
#endregion // DiskList
}
public class DiskViewModel : ViewModelBase
{
public DiskViewModel()
{
}
#region Name
public const string NamePropertyName = "Name";
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
return;
_name = value;
RaisePropertyChanged(NamePropertyName);
}
}
#endregion // Name
}
}
.
// MainWindow.xaml
<Window x:Class="WpfApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WpfApplication12.ViewModel"
xmlns:cvt="clr-namespace:InfoCore.StreamerConsole.Converters"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding Source={StaticResource Locator},Path=Main}">
<Grid>
<TreeView Background="AliceBlue" Margin="10"
ItemsSource="{Binding ClientList}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:ClientViewModel}" ItemsSource="{Binding DiskList}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type vm:DiskViewModel}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Style.Triggers>
<DataTrigger Value="true" Binding="{Binding Converter={cvt:TypeToBooleanConverter},ConverterParameter={x:Type vm:ClientViewModel}}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Client"/>
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>
<!--<DataTrigger Value="true" Binding="{Binding Converter={cvt:TypeToBooleanConverter},ConverterParameter={x:Type vm:DiskViewModel}}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Disk"/>
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>-->
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
</Window>
.
// ToBooleanConverter.cs
[MarkupExtensionReturnType(typeof(TypeToBooleanConverter))]
[ValueConversion(typeof(Type), typeof(bool))]
public class TypeToBooleanConverter : MarkupExtension, IValueConverter
{
private static TypeToBooleanConverter _converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _converter ?? (_converter = new TypeToBooleanConverter());
}
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (value == null)
return Binding.DoNothing;
return Type.Equals(value.GetType(), parameter);
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
Related
I have am attempting to build a tree view where:
1. The TreeViewItems are generated by a list in my model.
2. Each TreeViewItem contains a ComboBox, and a dynamic element whose template I want to change based on the value selected in the ComboBox.
Here is my current xaml code.
<Window x:Class="MyTestWPF.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:MyTestWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:NodeTypeToTemplateConverter x:Key="NodeTypeToTemplateConverter"/>
<DataTemplate x:Key="Template1">
<TextBlock Text="Template 1" />
</DataTemplate>
<DataTemplate x:Key="Template2">
<TextBlock Text="Template 2" />
</DataTemplate>
<Style x:Key="MyNodeTemplate" TargetType="ContentPresenter">
<Setter Property="ContentTemplate" Value="{StaticResource Template1}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=NodeType}">
<DataTrigger.Value>
<local:NodeTypesEnum>Type1</local:NodeTypesEnum>
</DataTrigger.Value>
<Setter Property="ContentTemplate" Value="{Binding Converter={StaticResource NodeTypeToTemplateConverter}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
<HierarchicalDataTemplate DataType="{x:Type local:MyTreeNode}"
ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal">
<ComboBox ItemsSource="{Binding Path=GetAvailableNodeType}"
SelectedItem="{Binding Path=NodeType}" />
<ContentPresenter Style="{StaticResource MyNodeTemplate}" Content="{Binding}" />
</StackPanel>
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView x:Name="MyTree" ItemsSource="{Binding MyTreeModel}" />
</Window>
And its code-behind:
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new
{
MyTreeModel = new MyTreeNode[] {
new MyTreeNode() { Name = "1", Nodes = new MyTreeNode[] { new MyTreeNode() { Name= "2" } } }
}
};
}
}
The tree node type:
namespace MyTestWPF
{
public class MyTreeNode
{
public string Name { get; set; }
public NodeTypesEnum NodeType { get; set; }
public MyTreeNode[] Nodes { get; set; }
public NodeTypesEnum[] GetAvailableNodeType()
{
return new NodeTypesEnum[] { NodeTypesEnum.Type1, NodeTypesEnum.Type2 };
}
}
public enum NodeTypesEnum
{
Type1 = 0,
Type2 = 1
}
}
The Converter (NodeTypeToTemplateConverter) receives the whole ViewModel, and returns the name of the relevant template based on values in the model.
using System;
using System.Globalization;
using System.Windows.Data;
namespace MyTestWPF
{
public class NodeTypeToTemplateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if((value as MyTreeNode).NodeType == NodeTypesEnum.Type1)
{
return "Template1";
} else
{
return "Template2";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
The problem is that the above code causes a stack overflow exception. The first item in the TreeView endlessly calls NodeTypeToTemplateConverter's Convert method.
I figured it had to do with the DataTrigger.Value. Setting that to a value different from the default NodeType allows the page to load without overflow, but the moment any ComboBox is set to NodeType1, stack overflow.
I attempted to simply remove the DataTrigger.Value element, but that causes the Converter to never be called at all...
How can I dynamically build the template name based on the value selected by its neighboring ComboBox?
You probably want to use a DataTemplateSelector rather than a converter.
public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
public DataTemplate Template1 { get; set; }
public DataTemplate Template2 { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
//Logic to select template based on 'item' value.
if (item == <template1Value>) return Template1; //TODO: replace <template1Value>
else if (item == <template2Value>) return Template2; //TODO: replace <template2Value>
else return new DataTemplate();
}
}
<local:ComboBoxItemTemplateSelector x:Key="ComboBoxItemTemplateSelector">
<local:ComboBoxItemTemplateSelector.Template1>
<DataTemplate>
<TextBlock Text="" />
</DataTemplate>
</local:ComboBoxItemTemplateSelector.Template1>
<local:ComboBoxItemTemplateSelector.Template2>
<DataTemplate>
<TextBlock Text="" />
</DataTemplate>
</local:ComboBoxItemTemplateSelector.Template2>
</local:ComboBoxItemTemplateSelector>
<ContentPresenter Content="{Binding NodeType}" ContentTemplateSelector="{StaticResource ComboBoxItemTemplateSelector}"/>
I have not fully tested this code, so let me know if you have any issues.
EDIT:
The template selector is only executed when the content changes so this won't work if you use {Binding}. A workaround for this would be to have the DataTemplate content bind to the parent's DataContext.
<DataTemplate>
<TextBlock Text="" DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ContentPresenter}}"/>
</DataTemplate>
If this workaround is not acceptable, there are other ways to do this as well.
After getting the right answer, I thought I should update this to show the working code for people to have a future reference:
MainWindow.xaml
<Grid>
<Grid.Resources>
<local:ValueConverters x:Key="ValueConverters"></local:ValueConverters>
</Grid.Resources>
<TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text,Converter={StaticResource ValueConverters}}" Value="True">
<Setter Property="TextBox.Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : MetroWindow
{
private readonly DataBindingViewModel _vm = new DataBindingViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
}
DataBindingViewModel.cs
public class DataBindingViewModel : INotifyPropertyChanged
{
private string _text;
public string Text
{
get
{
return this._text;
}
set
{
this._text = value;
if (null != PropertyChanged)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
ValueConverters
public class ValueConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (null != value)
{
if (value.ToString() == "1")
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
The above now works:)
In your MainWindow's XAML the DataContext is set to an instance of ValueConverters:
<Window.DataContext>
<local:ValueConverters/>
</Window.DataContext>
That doesn't seem to make sense. You should replace it with
<Window.DataContext>
<local:DataBindingViewModel/>
</Window.DataContext>
You would not have to set the DataContext again in the MainWindow's constructor, but perhaps still assign the private field (for later use) like this:
private readonly DataBindingViewModel _vm;
public MainWindow()
{
InitializeComponent();
_vm = (DataBindingViewModel)DataContext;
}
Alternatively, do not set the DataContext at all in XAML, and create it in code behind like this:
private readonly DataBindingViewModel _vm = new DataBindingViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _vm;
}
you can change Foreground color for certain values without a converter. Use a trigger to compare Text value with a known string and change Foreground when they match
<TextBox Grid.Row="2" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}">
<TextBox.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBox.Text" Value="1">
<Setter Property="TextBox.Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Because ToolBarTray provider no itemsSource i'm using Toolbars as place holders but can't get it done. I have made a user control that is placed MainWindow. I used similar thing to build menu as a base for this and frankly i don't know what I'm doing. I use also the same MenuItemViewModel here. But I can't get anything to the toolbar. The menu works perfect.
<UserControl.Resources>
<ui:ToolbarButtonFilterConverter x:Key="converter" />
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Command" Value="{Binding Path=Command}"/>
<Setter Property="Visibility" Value="{Binding Path=Visibility}"/>
<Setter Property="Content" Value="{Binding Path=BitmapName, Converter={x:Static ui:NativeImageResourceConverter.Default }}"/>
</Style>
<HierarchicalDataTemplate DataType="{x:Type menu:MenuItemViewModel}" ItemsSource="{Binding Path=MenuItems}" />
</UserControl.Resources>
<ToolBarTray Grid.Row="0" VerticalAlignment="Center" >
<ToolBar Name="Toolbar1" ItemsSource="{Binding Path=ToolbarItems, Converter={StaticResource converter}, ConverterParameter=Toolbar1}"/>
<!-- here more toolbars as placeholders -->
</ToolBarTray>
</UserControl>
The ToolbarViewModel seems to work ok (bound collections) because in convert below the corret item with 6 child items is returned), but xaml is probably faulty. ui:NativeImageResourceConverter is never called for these toolbar buttons that should get created from MenuItemViewModels.
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
var items = value as ObservableCollection<MenuItemViewModel>;
if (parameter != null && items != null)
{
if (parameter.ToString() == "Toolbar1")
{
return items.Where(i => i.Header == "Toolbar1");
}
//...
}
return null;
}
Menu items are hiearchical and can contains children in MenuItemViewModel:
public class MenuItemViewModel
{
public Visibility Visibility { get; private set; }
public string BitmapName { get; private set; }
public ICommand Command { get; private set; }
private ObservableCollection<MenuItemViewModel> _menuItems;
public ObservableCollection<MenuItemViewModel> MenuItems;
//...
}
I want to use the DataGridComboBoxColumn as a autocomplete combobox.
I've got it partially working. When the Row is in EditMode I can type text in the ComboBox, also in ViewMode the control returns the text. Only how to get the Label (in template) to EditMode by mouse doubleclick?
Up front, I don't want to use the DataGridTemplateColumn control because it just doesn't handle keyboard and mouse entry like the DataGridComboBoxColumn does (tabs, arrows, edit/view mode/ double click etc..).
It looks like:
I fixed it adding a behavior to the TextBox to get a link to the parent DataGrid then setting the Row into Edit Mode by calling BeginEdit().
The solution I used:
View
<Window x:Class="WpfApplication1.MainWindow"
xmlns:local="clr-namespace:WpfApplication1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Model.Things}" Name="MyGrid" ClipboardCopyMode="IncludeHeader">
<DataGrid.Resources>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Object" MinWidth="140" TextBinding="{Binding ObjectText}" ItemsSource="{Binding Source={StaticResource proxy}, Path=Data.Model.ObjectList}" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True"/>
<Setter Property="Text" Value="{Binding ObjectText}"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox IsReadOnly="True" Text="{Binding Path=DataContext.ObjectText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}">
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="local:CellSelectedBehavior.IsCellRowSelected" Value="true"></Setter>
</Style>
</TextBox.Resources>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Model
public class Model : BaseModel
{
//List of objects for combobox
private List<string> _objectList;
public List<string> ObjectList { get { return _objectList; } set { _objectList = value; } }
//Rows in datagrid
private List<Thing> _things;
public List<Thing> Things
{
get { return _things; }
set { _things = value; OnPropertyChanged("Things"); }
}
}
public class Thing : BaseModel
{
//Text in combobox
private string _objectText;
public string ObjectText
{
get { return _objectText; }
set { _objectText = value; OnPropertyChanged("ObjectText"); }
}
}
ViewModel
public class ViewModel
{
public Model Model { get; set; }
public ViewModel()
{
Model = new WpfApplication1.Model();
Model.ObjectList = new List<string>();
Model.ObjectList.Add("Aaaaa");
Model.ObjectList.Add("Bbbbb");
Model.ObjectList.Add("Ccccc");
Model.Things = new List<Thing>();
Model.Things.Add(new Thing() { ObjectText = "Aaaaa" });
}
}
Behavior
public class CellSelectedBehavior
{
public static bool GetIsCellRowSelected(DependencyObject obj) { return (bool)obj.GetValue(IsCellRowSelectedProperty); }
public static void SetIsCellRowSelected(DependencyObject obj, bool value) { obj.SetValue(IsCellRowSelectedProperty, value); }
public static readonly DependencyProperty IsCellRowSelectedProperty = DependencyProperty.RegisterAttached("IsCellRowSelected",
typeof(bool), typeof(CellSelectedBehavior), new UIPropertyMetadata(false, OnIsCellRowSelected));
static void OnIsCellRowSelected(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TextBox item = depObj as TextBox;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
item.MouseDoubleClick += SelectRow;
else
item.MouseDoubleClick -= SelectRow;
}
static void SelectRow(object sender, EventArgs e)
{
TextBox box = sender as TextBox;
var grid = box.FindAncestor<DataGrid>();
grid.BeginEdit();
}
}
Helper (to find DataGrid)
public static class Helper
{
public static T FindAncestor<T>(this DependencyObject current) where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
}
I am trying to create a templated button control with databinding for the Visibility, tooltip, and Command. The Visibility binding works, as does the tooltip, but the Command does not. Another process is responsible for injecting the viewmodel and associating it with the View, and the other data bindings are working so I am pretty confident that is working properly.
In the resource dictionary:
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
<Style TargetType="local:ImageButton">
<Setter Property="Visibility" Value="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
<Setter Property="Command" Value="{Binding ButtonCommand}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}"
ToolTipService.ToolTip="{Binding ToolName}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the templated control
public class MyButton: ImageButton
{
public MyButton(MyCommandViewModel viewmodel)
{
this.DefaultStyleKey = typeof(ImageButton);
this.Image = new BitmapImage(new Uri("/MyProject;component/Themes/myimage.png", UriKind.Relative));
this.DataContext = viewmodel;
}
}
and in the view model
public MyCommandViewModel()
: base("My Tool", true)
{
}
public class CommandViewModel
{
public CommandViewModel(string toolName, bool isAvailable)
{
ToolIsAvailable = isAvailable;
ToolName = toolName;
_buttoncommand = new DelegateCommand(() =>
{
ExecuteCommand();
},
() => { return CanExecute; });
}
private bool _canExecute = true;
public bool CanExecute
{
get { return _canExecute; }
set
{
_canExecute = value;
OnPropertyChanged("CanExecute");
if (_command != null) _command.RaiseCanExecuteChanged();
}
}
private DelegateCommand _buttoncommand;
public ICommand ButtonCommand
{
get { return _buttoncommand; }
}
protected virtual void ExecuteCommand()
{
}
public bool ToolIsAvailable
{
get { return _toolIsReady; }
set { _toolIsReady = value; OnPropertyChanged("ToolIsAvailable"); }
}
public string ToolName
{
get { return _toolName; }
set { _toolName = value; OnPropertyChanged("ToolName"); }
}
}
Why are the other databindings functioning properly but not the Command data binding. I found this similar post
Overriding a templated Button's Command in WPF
Do I need to template a grid control instead and use RoutedCommands? I am not sure I understand why Silverlight treats the Command binding different than the others so I suspect I just have a bug in the code.
Does specifically looking for the datacontext work?
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ButtonCommand}"
This was my solution. Using the same commandviewmodel as above and same MyCommandViewModel
<Style TargetType="local:ImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The databinding is now done in a user control
<UserControl x:Class="SilverlightApplication11.Test"
...
>
<UserControl.Resources>
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
</UserControl.Resources>
<Grid>
<local:ImageButton Image="/SilverlightApplication11;component/Themes/hand.png" Command="{Binding ButtonCommand}" Visibility="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
</Grid>
</UserControl>
and the code behind
public Test(TestCommandViewModel vm)
{
InitializeComponent();
this.Loaded += (o, e) => this.DataContext = vm;
}