I have created a user control that has a Label and a ComboBox. It is used like this:
<cc:LabeledComboBox
HeaderLabelContent="Months"
ItemsSource="{Binding AllMonths}"
SelectedValue="{Binding SelectedMonth}"/>
And here is what the UserControl XAML looks like:
<UserControl x:Class="CustomControls.LabeledComboBox" ...>
<UserControl.Resources>
<converters:MonthEnumToTextConverter x:Key="MonthEnumToTextConverter" />
</UserControl.Resources>
<DockPanel>
<Label x:Name="LblValue" DockPanel.Dock="Top"/>
<ComboBox x:Name="LstItems">
<ComboBox.ItemTemplate>
<DataTemplate>
<!-- TODO: Fix so that the Converter can be set from outside -->
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</UserControl>
In the comment above you can see my problem. The control is generic (the ComboBox can contain pretty much anything) but on the Binding inside the DataTemplate I have specified a Converter that is very specific.
How can I specify the Converter from outside the UserControl?
I'm hoping for some kind of solution using a dependency property like this:
<cc:LabeledComboBox
...
ItemConverter="{StaticResource MonthEnumToTextConverter}"/>
You may have an internal binding converter that delegates its Convert and ConvertBack calls to one set is settable as dependency property:
<UserControl ...>
<UserControl.Resources>
<local:InternalItemConverter x:Key="InternalItemConverter"/>
</UserControl.Resources>
<DockPanel>
...
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding
Converter={StaticResource InternalItemConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</UserControl>
The internal converter could look like this:
class InternalItemConverter : IValueConverter
{
public LabeledComboBox LabeledComboBox { get; set; }
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (LabeledComboBox != null && LabeledComboBox.ItemConverter != null)
{
value = LabeledComboBox.ItemConverter.Convert(
value, targetType, parameter, culture);
}
return value;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (LabeledComboBox != null && LabeledComboBox.ItemConverter != null)
{
value = LabeledComboBox.ItemConverter.ConvertBack(
value, targetType, parameter, culture);
}
return value;
}
}
And finally the dependency property code like this:
public partial class LabeledComboBox : UserControl
{
private static readonly DependencyProperty ItemConverterProperty =
DependencyProperty.Register(
"ItemConverter", typeof(IValueConverter), typeof(LabeledComboBox));
public IValueConverter ItemConverter
{
get { return (IValueConverter)GetValue(ItemConverterProperty); }
set { SetValue(ItemConverterProperty, value); }
}
public LabeledComboBox()
{
InitializeComponent();
var converter = (InternalItemConverter)Resources["InternalItemConverter"];
converter.LabeledComboBox = this;
}
}
You can create multiple datatemplates for the the combobox items and then you can control what and how you want to display your comboxitem like below
<DataTemplate DataType="{x:Type vm:MonthDataTypeViewModel}" >
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
<DataTemplate DataType={x:Type vm:OtherViewModel}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
If you do not have multiple viewmodels then you can use a template selector to select different data templates based on some property in your viewmodel.
OP here. Presenting the solution that I'll use until I find something better.
I don't specify only the Converter, but the whole DataTemplate:
<cc:LabeledComboBox>
<cc:LabeledComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
</cc:LabeledComboBox.ItemTemplate>
</cc:LabeledComboBox>
And here's the ItemTemplate dependency property:
public partial class LabeledComboBox : UserControl
{
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate",
typeof(DataTemplate),
typeof(LabeledComboBox),
new PropertyMetadata(default(DataTemplate), ItemTemplateChanged));
private static void ItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var labeledComboBox = (LabeledComboBox)d;
labeledComboBox.LstItems.ItemTemplate = (DataTemplate)e.NewValue;
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// ...
}
EDIT: reworked to not use my personal example to avoid confusion ...
On your user control code behind you could define your dependency property.
I don't know what type your converters derive from so change 'myConverterType' to the type of converters you use.
public bool ItemConverter
{
get { return (myConverterType)GetValue(IntemConverterProperty); }
set { SetValue(ItemConverterProperty, value); }
}
public static readonly DependencyProperty ItemConverterProperty =
DependencyProperty.Register("ItemConverter", typeof(myConverterType),
typeof(LabeledComboBox), null);
In XAML you should then just be able to set the converter property as per your example. In my example it is used like this:
<cc:LabeledComboBox ItemConverter="{StaticResource theSpecificConverter}"/>
Then use this property, on your user control xaml, like this:
<ComboBox x:Name="LstItems">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={Binding ItemConverter, ElementName=UserControl}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Related
i have the following ItemsControl defined, i want to bind the Content of a Button to a object Property path, which will be provided as a string through a method. The Items are generic. How can i achieve this ? because i can't set DisplayMemberPath and bind it to the ItemTemplate. (i cant't set both DisplayMemberPath and ItemTemplate)
<ScrollViewer Grid.Row="2"
Grid.Column="1"
Margin="5 3"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
Style="{DynamicResource MaterialDesignFlatButton}"
Margin="2"
CommandParameter="{Binding}">
<Button.Content>
<TextBlock Text="{Binding}" />
</Button.Content>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
ViewModel:
public class SelectionViewModel<T> : DialogViewModelBase, IDialogViewModel
{
// Fields
private string _title;
private bool _showTitleSeparator;
private string _displayMemberPath;
private IEnumerable<T> _items;
// Properties
public string Title
{
get { return _title; }
set { _title = value; Notify(); }
}
public IEnumerable<T> Items
{
get { return _items; }
set { _items = value; Notify(); }
}
// Member Path to bind to
public string DisplayMemberPath
{
get { return _displayMemberPath; }
set { _displayMemberPath = value; Notify(); }
}
}
if i remove the ItemTemplate and simply bind the DisplayMemberPath of the ItemsControl to the Property in my ViewModel it works fine.
I was able to do this using a converter and reflection.
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Contains(DependencyProperty.UnsetValue))
{ return null; }
object source = values[0];
string path = (string)values[1];
Type sourceType = source.GetType();
System.Reflection.PropertyInfo propertyInfo = sourceType.GetProperty(path);
return propertyInfo.GetValue(source, null);
}
Binding has to be set with Path to the property name of the class whose list is bound to the ItemsSource.
if your class looks like:
Class MyItem{
String Prop1 { get; }
}
Then binding will be
<TextBlock Text="{Binding Path=Prop1}" />
I effectively have -
<UserControl ...>
<Grid>
<TreeView Name="nTree">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="NodeType" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<Image x:Name="icon" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
</UserControl>
NodeType is a type returned by an underlying library. I would like to set the icon images source based on the name provided - however I can't modify NodeType to add a getter.
So ideally what I would like is the icon image source to be bound to a function on the UserControl class which receives the Name and returns an ImageSource.
i.e.
public partial class Panel : UserControl
{
public Panel(NodeType n)
{
nTree.Items.add(n);
}
public ImageSource GetIcon(string name)
{
...
}
}
This feels like it should be possible but i'm struggling to work it out. Assistance would be appreciated.
Unfortunately you can't bind to methods, you need to convert the method to a property
you can do this in several ways
the easiest would be to have: (though this should be on your VM not your V)
public ImageSource Icon
{
...
}
or you can use a value converter:(the best fit for what you are descibing)
public class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
GetImageLogic
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("One way only");
}
}
the 3rd option would be to use an CommandBinding :
then bind to the command
CommandBinding OpenCmdBinding = new CommandBinding(
ApplicationCommands.Open,
OpenCmdExecuted,
OpenCmdCanExecute);
this.CommandBindings.Add(OpenCmdBinding);
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
String command, targetobj;
command = ((RoutedCommand)e.Command).Name;
targetobj = ((FrameworkElement)target).Name;
MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj);
}
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
This question already has answers here:
How to get clicked item in ListView
(5 answers)
Closed 7 years ago.
I've got a ListView with a DataTemplate like this, using MVVM pattern
<ListView ItemsSource="{Binding Source}"
IsItemClickEnabled="True"
commands:ItemsClickCommand.Command="{Binding ItemClickedCommand}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding A}" />
<Button Content="{Binding B}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ItemsClickCommand is defined in this way
public static class ItemsClickCommand
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(BindableCommand), typeof(ItemsClickCommand), new PropertyMetadata(null, OnCommandPropertyChanged));
public static void SetCommand(DependencyObject d, BindableCommand value)
{
d.SetValue(CommandProperty, value);
}
public static BindableCommand GetCommand(DependencyObject d)
{
return (BindableCommand)d.GetValue(CommandProperty);
}
private static void OnCommandPropertyChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var control = d as ListViewBase;
if (control != null)
control.ItemClick += OnItemClick;
}
private static void OnItemClick(object sender, ItemClickEventArgs e)
{
var control = sender as ListViewBase;
var command = GetCommand(control);
if (command != null && command.CanExecute(e.OriginalSource))
command.ExecuteWithMoreParameters(e.OriginalSource, e.ClickedItem);
}
}
What I'm asking is how can I know if user tap on the TextBlock or Button.
I tried to handle ItemClickCommand event in this way in ViewModel to search controls in VisualTree (is this the best solution?), but the cast to DependencyObject doesn't work (returns always null)
public void ItemClicked(object originalSource, object clickedItem)
{
var source = originalSourceas DependencyObject;
if (source == null)
return;
}
There are a few solutions that come to mind
Solution 1
<ListView
x:Name="parent"
ItemsSource="{Binding Source}"
IsItemClickEnabled="True"
Margin="20">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding A}" />
<Button
Content="{Binding B}"
Command="{Binding DataContext.BCommand, ElementName=parent}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Note how the ListView has the name set to "parent" with the attribute: x:Name="parent" and how the binding for the button's command uses that. Also note that the command will be provided with a parameter that is the reference to the data source for the element that was clicked.
The view model for this page will look like this:
public class MainViewModel : MvxViewModel
{
public ObservableCollection<MySource> Source { get; private set; }
public MvxCommand<MySource> BCommand { get; private set; }
public MainViewModel()
{
Source = new ObservableCollection<MySource>()
{
new MySource("e1", "b1"),
new MySource("e2", "b2"),
new MySource("e3", "b3"),
};
BCommand = new MvxCommand<MySource>(ExecuteBCommand);
}
private void ExecuteBCommand(MySource source)
{
Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
}
}
'MvxCommand' is just a particular implementation of ICommand. I used MvvMCross for my sample code but you don't have to do that - you can use whatever MvvM implementation you need.
This solution is appropriate if the responsibility to handle the command lies with the view model for the page that contains the list.
Solution 2
Handling the command in the view model for the page that contains the list may not always be appropriate. You may want to move that logic in code that is closer to the element that is being clicked. In that case, isolate the data template for the element in its own user control, create a view model class that corresponds to the logic behind that user control and implement the command in that view model. Here is how the code would look like:
The XAML for the ListView:
<ListView
ItemsSource="{Binding Source}"
IsItemClickEnabled="True"
Margin="20">
<ListView.ItemTemplate>
<DataTemplate>
<uc:MyElement DataContext="{Binding Converter={StaticResource MySourceToMyElementViewModelConverter}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The XAML for the user control representing one element:
<Grid>
<StackPanel>
<TextBlock Text="{Binding Source.A}" />
<Button Content="{Binding Source.B}" Command="{Binding BCommand}" />
</StackPanel>
</Grid>
The source code for MySourceToMyElementViewModelConverter:
public class MySourceToMyElementViewModelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return new MyElementViewModel((MySource)value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}
The view model for the main page:
public class MainViewModel : MvxViewModel
{
public ObservableCollection<MySource> Source { get; private set; }
public MainViewModel()
{
Source = new ObservableCollection<MySource>()
{
new MySource("e1", "b1"),
new MySource("e2", "b2"),
new MySource("e3", "b3"),
};
}
}
The view model for the user control representing one element in the list:
public class MyElementViewModel : MvxViewModel
{
public MySource Source { get; private set; }
public MvxCommand BCommand { get; private set; }
public MyElementViewModel(MySource source)
{
Source = source;
BCommand = new MvxCommand(ExecuteBCommand);
}
private void ExecuteBCommand()
{
Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", Source.A, Source.B);
}
}
Solution 3
Your sample assumes that the view model for the main page exposes a list of data model elements. Something like this:
public ObservableCollection<MySource> Source { get; private set; }
The view model for the main page could be changed so that it exposes a list of view model elements instead. Something like this:
public ObservableCollection<MyElementViewModel> ElementViewModelList { get; private set; }
Each element in ElementViewModelList would correspond to an element in Source. This solution can get slightly complex if the contents of Source changes at run time. The view model of the main page will need to observe Source and change ElementViewModelList accordingly. Going further don this path you may want to abstract the concept of a collection mapper (something similar with an ICollectionView) and provide some generic code for doing so.
For this solution, the XAML will look like this:
<ListView
ItemsSource="{Binding ElementViewModelList}"
IsItemClickEnabled="True"
Margin="20">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding A}" />
<Button Content="{Binding B}" Command="{Binding BCommand}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Notes for Solution 1, 2 and 3
I see that your original sample associates a commanding not with the button inside of the element but with the entire element. That raises the question: what are you going to do with the inner button? Will you have a situation where the user can click either on the element or on the inner button? That may not be the best solution as far as UI/UX goes. Be mindful of that. Just as an exercise and in order to get closer to your original sample, here is what you can do if you want to associate commanding with the entire element.
Wrap your entire element in a button with a custom style. That style will modify the way a click is handled visually. The simplest form of that is to have the click not create any visual effect. This change applied to Solution 1 (it can easily be applied to Solution 2 and Solution 3 as well) would look something like this:
<ListView
x:Name="parent"
ItemsSource="{Binding Source}"
IsItemClickEnabled="True"
Margin="20">
<ListView.ItemTemplate>
<DataTemplate>
<Button
Command="{Binding DataContext.BCommand, ElementName=parent}"
CommandParameter="{Binding}"
Style="{StaticResource NoVisualEffectButtonStyle}">
<StackPanel>
<TextBlock Text="{Binding A}" />
<TextBlock Text="{Binding B}" />
</StackPanel>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In this case you would have to write NoVisualEffectButtonStyle but that is a simple task. You would also need to decide what kind of commanding you want to associate with the inner button (otherwise why would you have an inner button). Or, more likely you could transform the inner button in something like a textbox.
Solution 4
Use Behaviors.
First, add a reference to "Behaviors SDK".. Then modify your XAML code:
...
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
...
<Grid>
<ListView ItemsSource="{Binding Source}" IsItemClickEnabled="True" Margin="20">
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemClick">
<core:InvokeCommandAction
Command="{Binding BCommand}"
InputConverter="{StaticResource ItemClickedToMySourceConverter}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding A}" />
<TextBlock Text="{Binding B}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
ItemClickedToMySourceConverter is just a normal value converter:
public class ItemClickedToMySourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (MySource)(((ItemClickEventArgs)value).ClickedItem);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotSupportedException();
}
}
The view model will look like this:
public class Main4ViewModel : MvxViewModel
{
public ObservableCollection<MySource> Source { get; private set; }
public MvxCommand<MySource> BCommand { get; private set; }
public Main4ViewModel()
{
Source = new ObservableCollection<MySource>()
{
new MySource("e1", "b1"),
new MySource("e2", "b2"),
new MySource("e3", "b3"),
};
BCommand = new MvxCommand<MySource>(ExecuteBCommand);
}
private void ExecuteBCommand(MySource source)
{
Debug.WriteLine("ExecuteBCommand. Source: A={0}, B={1}", source.A, source.B);
}
}
I have a traffic application. The light status is updated in the listbox.
<ListBox x:Name="lbxCallProgress" ItemsSource="{Binding Messages,Mode=TwoWay}" Height="373" FontSize="8" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
For the Messages:
public partial class MainWindow : Window
{
public ObservableCollection<string> Messages { get; set; }
To update messages to the listbox.
void UpdateMessage(string message)
{
try
{
Dispatcher.BeginInvoke((Action)delegate()
{
Dispatcher.BeginInvoke(new Action(() => { this.Messages.Add(message); }));
});
}
Now if the string message contains the keyword "green", then I want to set the item color on the listbox as color green, etc.
How?
here you go
with the power of WPF binding you can use the value to bind to the desired property Background and the implicit converter will do the rest for you.
<DataTemplate>
<TextBlock Text="{Binding}" Background="{Binding}"/>
</DataTemplate>
you can choose to bind Foreground in case if you want to change the text color
Using converters
if simple binding is not sufficient enough you may use converters to perform custom logic of conversion, eg converting The light is red to Brushes.Red
public class MyColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = value as string;
if(text.Contains("red"))
return Brushes.Red;
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
usage
<DataTemplate xmlns:l="your namespace to converter class">
<DataTemplate.Resources>
<l:MyColorConverter x:Key="MyColorConverter" />
</DataTemplate.Resources>
<TextBlock Text="{Binding}" Background="{Binding Converter={StaticResource MyColorConverter}}"/>
</DataTemplate>
The default DataTemplate in a wpf application displays the result of the .ToString() method. I'm developing an application where the default DataTemplate should display nothing.
I've tried:
<Grid.Resources>
<DataTemplate DataType="{x:Type System:Object}">
<Grid></Grid>
</DataTemplate>
</Grid.Resources>
But this doesn't work. Does anyone knows if this is possible without specifiing a specific DataTemplate for every class type in the application?
If you are using the MVVM pattern and have an abstract class which all your ViewModel classes derive from, you can use that class instead of System.Object:
<Grid.Resources>
<DataTemplate DataType="{x:Type vm:VMBase}">
</DataTemplate>
</Grid.Resources>
I know of no way to do this. As per Joe's comment below, WPF specifically disallows specifying a DataTemplate for type Object.
Depending on your exact requirements, it may be easier to search for a DataTemplate that matches the specific type. If you find one, use it. Otherwise, display nothing. For example:
<ContentControl Content="{Binding YourContent}" ContentTemplateSelector="{StaticResource MyContentTemplateSelector}"/>
And in your selector (pseudo-code, obviously):
var dataTemplateKey = new DataTemplateKey() { DataType = theType; };
var dataTemplate = yourControl.FindResource(dataTemplateKey);
if (dataTemplate != null)
{
return dataTemplate;
}
return NulloDataTemplate;
I used Nullable, worked for my situation.
<DataTemplate DataType="{x:Type sys:Nullable}">
<!-- Content -->
</DataTemplate>
I'm not sure about replacing the default DataTemplate, but you can use a ValueConverter to pass display ToString in the case of certain types and an empty string otherwise. Here's some code (note that the typeb textblock doesnt have the converter on it to show what it looks like normally):
.xaml:
<Window x:Class="EmptyTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:EmptyTemplate"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<loc:AType x:Key="atype"/>
<loc:BType x:Key="btype"/>
<loc:TypeConverter x:Key="TypeConverter"/>
</Window.Resources>
<StackPanel>
<Button Content="{Binding Source={StaticResource atype}, Converter={StaticResource TypeConverter}}"/>
<Button Content="{Binding Source={StaticResource btype}, Converter={StaticResource TypeConverter}}"/>
<TextBlock Text="{Binding Source={StaticResource atype}, Converter={StaticResource TypeConverter}}"/>
<TextBlock Text="{Binding Source={StaticResource btype}}"/>
</StackPanel>
</Window>
.xaml.cs:
namespace EmptyTemplate
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
public class AType { }
public class BType { }
public class TypeConverter : IValueConverter
{
public DataTemplate DefaultTemplate { get; set; }
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.GetType() == typeof(AType))
{
return value.ToString();
}
return DefaultTemplate;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
Here a working example about how to do this using a selector (the best way IMO):
public class EmptyDefaultDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item != null)
{
var dataTemplateKey = new DataTemplateKey(item.GetType());
var dataTemplate = ((FrameworkElement) container).TryFindResource(dataTemplateKey);
if (dataTemplate != null)
return (DataTemplate) dataTemplate;
}
return new DataTemplate(); //null does not work
}
}
I discovered something accidentally. I was using a custom dependency property to set the Datacontext on a usercontrol that had a contentcontrol with Datatemplates based on types(entities in my case). Since I had several different kinds of entities my custom dependency property was
` typeof(object)
This was the device I used to bind to the datacontext of the ContentControl.
public object MySelectedItem
{
get { return (object)GetValue(Property1Property); }
set { SetValue(Property1Property, value); }
}
public static readonly DependencyProperty Property1Property
= DependencyProperty.Register(
"MySelectedItem",
typeof(object),
typeof(PromotionsMenu),
new PropertyMetadata(false)
);
Used like this:
MySelectedItem = SomeEntity;
I discovered I could also use it like this:
MySelectedItem = "some text";
And the contextcontrol would print some text as its context.
MySelectedItem = "";
works for a totally blank context.
`