Add Check Box for Treeview parent nodes only Wpf - wpf

I want to add the check box for treeview of observable collection. the following code add the check box for all the parent and children nodes. I need only for the parent node Is there any way to achieve this...
<HierarchicalDataTemplate x:Key="LogFolderExplorer" DataType="{x:Type TestAutomationClient:TestArtifact}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" MinWidth="200">
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<TextBlock Text="{Binding Name}" FontSize="14"/>
</StackPanel>
</HierarchicalDataTemplate>

There are tons of ways you can achieve this.
One of them:
You can go to the ViewModel for the TestAutomationClient:TestArtifact, and add the bool property here:
public bool IsEmpty { get { return !this.Children.Any() } }
You should update it when the entire collection has changed (if this is your case), i.e. you should make RaisePropertyChanged("IsEmpty"). In case you are using some kind of ICollectionView - you do not need that property on your ViewModel.
<HierarchicalDataTemplate x:Key="LogFolderExplorer"
DataType="{x:Type TestAutomationClient:TestArtifact}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" MinWidth="200">
<CheckBox Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsEmpty}" Value="True">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<TextBlock Text="{Binding Name}" FontSize="14"/>
</StackPanel>
</HierarchicalDataTemplate>

You need a DataTrigger with a Converter. The Trigger should look like below,
<HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="chk" Visibility="Collapsed"/>
<TextBlock Text="{Binding Name}"
VerticalAlignment="Center"
Margin="3 0" />
</StackPanel>
<HierarchicalDataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource TreeLevelFinder}}"
Value="True">
<Setter Property="Visibility"
TargetName="checkBox"
Value="Visible" />
</DataTrigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
The whole TreeViewItem is being send to the Converter. The Converter will look for a Visual Parent. If it finds a Parent as TreeView, it is the first level. If it finds TreeViewItem, then it is sub levels.
public class TreeLevelFinder : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is FrameworkElement)
{
var element = value as FrameworkElement;
var treeItem = element.TemplatedParent as TreeViewItem;
var parent = treeItem.FindAncestor<ItemsControl>();
if (parent is TreeView)
{
return true;
}
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The extension method FindAncestor<T> is available in this link.

Related

Creating a ListBox style Control with "Show X More"

I would like to create a type of control that will be able to see a narrowed and expanded list by some button, an expander or just a button, and also display some hidden items
I tried to create a style that would have a ListBox of a certain size (let's say it would be enough for 3 items)
And the Expander or some Button that would be below it would say how many more items there are
And give the option to expand and see everything or reduce and return to see 3 items
What I created here works pretty poorly and doesn't look good.
I would appreciate help on how to improve it or how to do it in a better and more correct way
<Style x:Key="CustomListBoxKey" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsPresenter/>
<Expander x:Name="Expander" Grid.Row="1" Header="Show X More" ExpandDirection="Down"/>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="False">
<Setter Property="MaxHeight" Value="80"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="True">
<Setter Property="MaxHeight" Value="Infinity"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public List<string> Items { get; set; } = new() { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
<Grid>
<ListBox ItemsSource="{Binding Items}" Style="{StaticResource CustomListBoxKey}" VerticalAlignment="Top">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
This is an improvement to your code, and it will give you what you asked for
<local:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverterKey"/>
<local:MathValueConverter x:Key="MathValueConverterKey"/>
<Style x:Key="CustomListBoxKey" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ItemsPresenter x:Name="ItemsPresenter"/>
<Expander
x:Name="Expander"
Grid.Row="1"
ExpandDirection="Down">
<Expander.Header>
<StackPanel
Orientation="Horizontal"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Expander}, Path=IsExpanded, Converter={StaticResource InvertableBooleanToVisibilityConverterKey}}">
<TextBlock Text="Show "/>
<TextBlock
Text="{Binding RelativeSource={RelativeSource Self}, Path=DataContext.Items.Count,
Converter={StaticResource MathValueConverterKey},
ConverterParameter=-2}">
</TextBlock>
<TextBlock Text=" More"/>
</StackPanel>
</Expander.Header>
</Expander>
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="False">
<Setter Property="MaxHeight" Value="80"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="True">
<Setter Property="MaxHeight" Value="Infinity"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class MathValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int.TryParse(parameter.ToString(), out var x);
return (int)value + x;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class InvertableBooleanToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is bool and true ? Visibility.Collapsed : Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I would like to present a solution using a UserControl. Actually we have a request for two slightly different presentation. As a first step I build a UserControl called DualDisplay. The DualDisplay gets : "Short Display" and "LongDisplay" as parameters.
The DualDisplay UserControl is hosting the Expander:
<UserControl.Resources>
<local:InvertBooleanToVisibilityConverter x:Key="InvertBooleanToVisibilityConverter"/>
<Style TargetType="Expander" x:Key="ExpanderHeaders">
<Style.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Header" Value="Show Less"/>
</Trigger>
<Trigger Property="IsExpanded" Value="false">
<Setter Property="Header" Value="Show More"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Expander Name="expander" IsExpanded="False" Style="{StaticResource ExpanderHeaders}">
<ContentPresenter Content="{Binding ElementName=ours,Path=LongContent}"/>
</Expander>
<ContentPresenter Content="{Binding ElementName=ours,Path=ShortContent}"
Visibility="{Binding Path=IsExpanded,ElementName=expander,Converter={StaticResource ResourceKey=InvertBooleanToVisibilityConverter}}"/>
</StackPanel>
</Grid>
And the using of the DualDisplay will look as following :
<local:DualDisplay>
<local:DualDisplay.ShortContent>
<ListBox ItemsSource="{Binding Items}" VerticalAlignment="Top" MaxHeight="80">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</local:DualDisplay.ShortContent>
<local:DualDisplay.LongContent>
<ListBox ItemsSource="{Binding Items}" VerticalAlignment="Top" MaxHeight="1000">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</local:DualDisplay.LongContent>
</local:DualDisplay>
When we use this method we can reuse the DualDisplay for different cases and we are free to design the short/long display as we wish.
I attach also the code behind of the DualDisplay:
public object ShortContent
{
get { return (object)GetValue(ShortContentProperty); }
set { SetValue(ShortContentProperty, value); }
}
public static readonly DependencyProperty ShortContentProperty =
DependencyProperty.Register("ShortContent", typeof(object), typeof(DualDisplay),
new PropertyMetadata(null));
public object LongContent
{
get { return (object)GetValue(LongContentProperty); }
set { SetValue(LongContentProperty, value); }
}
public static readonly DependencyProperty LongContentProperty =
DependencyProperty.Register("LongContent", typeof(object), typeof(DualDisplay),
new PropertyMetadata(null));

WPF datatrigger not firing

I'm trying to change the background colour of a text box in WPF based on its content.
Using breakpoints, I see my ValueConverter gets constructed, but neither Convert nor ConvertBack methods ever get called, and so the styling doesn't work.
The 'LightBlue' style in the XAML does work at startup.
I tried to use Snoop but I don't know what I'm looking for.
XAML...
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
ValueConverter...
public class ThreadCreationLimitChanged : IValueConverter
{
public ThreadCreationLimitChanged()
{
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.ToString() == "120";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
A bit of class ManagerConfig... (NotifyPropertyChanged implements INotifyPropertyChanged)
class ManagerConfig : NotifyPropertyChanged
{
private uint _ThreadCreationLimit;
public uint ThreadCreationLimit
{
get { return _ThreadCreationLimit; }
set
{
_ThreadCreationLimit = value;
OnPropertyChanged("ThreadCreationLimit");
}
}
Why not create a computed property on ManagerConfig which reports a status for the change of the ThreadCreationLimit? That way one doesn't need a converter. Code such as this:
public bool IsOverLimit { get { return ThreadCreationLimit > 120 } }
public uint ThreadCreationLimit
{
get { return _ThreadCreationLimit; }
set
{
_ThreadCreationLimit = value;
OnPropertyChanged("ThreadCreationLimit");
OnPropertyChanged("IsOverLimit");
}
}
Then bind the data trigger to IsOverLimit which will have a change notification sent everytime ThreadCreationLimit is set/changed.
An added plus is that the business logic is on the intended class and not a converter.
Change this {Binding Path=Text,Converter={StaticResource ThreadCreationLimitChanged}} to {Binding Path=Text, RelativeSource={RelativeSource Self} ,Converter={StaticResource ThreadCreationLimitChanged}}
I guess you are having Binding errors.Have checked like below,
1) Accessing TextBox directly for Value,
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox x:Name="tBox" Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, ElementName=tBox,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
2) Accesing proper Model values,
<Grid.Resources>
<local:ThreadCreationLimitChanged x:Key="ThreadCreationLimitChanged"></local:ThreadCreationLimitChanged>
</Grid.Resources>
<Label Grid.Row="0" Grid.Column="0" Margin="0, 5, 0, 0" Content="New Thread Limit"></Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="10, 5, 10, 0" Width="100" Text="{Binding Path=ManagerConfig.ThreadCreationLimit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<TextBox.Style>
<Style>
<Setter Property="TextBox.Background" Value="LightBlue"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ManagerConfig.ThreadCreationLimit,Converter={StaticResource ThreadCreationLimitChanged}}" Value="false">
<Setter Property="TextBox.Background" Value="Yellow"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Page indicator using ItemsControl ignores data template

I'm trying to create a page indicator using ItemsControl. The idea is to bind to the tabs of a TabControl and display a circle for each tab, with the color determined by a trigger that checks whether the tab is selected or not:
<ItemsControl ItemsSource="{Binding Path=Items, ElementName=ATabControl}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type TabItem}">
<Ellipse x:Name="PageIndicator" Width="6" Height="6" Margin="2,0" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="False">
<Setter TargetName="PageIndicator" Property="Fill" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
<Setter TargetName="PageIndicator" Property="Fill" Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
It compiles without error but instead of the circles, I get the names of the tabs listed. Practically, it ignores the ItemTemplate/DataTemplate completely. Actually, if I remove the latter, the display remains the same all right.
There is always an issue while binding UI elements in two different containers. as one UI element can have only 1 parent so the last parent will have the actual element and hence the same will be removed from the former parent containers.
in the issue you've mentioned you attempted to bind the UI element TabItems to the items control which effectively pulled the original elements from the tab control and placed them as child of the items control.
in order to solve this issue I propose a solution to wrap such UI elements in a class and wire the properties needed.
I attempted a solution using converter
xaml
<StackPanel xmlns:l="clr-namespace:CSharpWPF">
<StackPanel.Resources>
<l:TabItemsConverter x:Key="TabItemsConverter" />
</StackPanel.Resources>
<TabControl x:Name="ATabControl">
<TabItem Header="item 1" />
<TabItem Header="item 2" />
<TabItem Header="item 3" />
</TabControl>
<ItemsControl ItemsSource="{Binding Path=Items, ElementName=ATabControl,Converter={StaticResource TabItemsConverter}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Ellipse x:Name="PageIndicator"
Width="6"
Height="6"
Margin="2,0"
Fill="Gray" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="False">
<Setter TargetName="PageIndicator"
Property="Fill"
Value="Gray" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsSelected}"
Value="True">
<Setter TargetName="PageIndicator"
Property="Fill"
Value="Blue" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
converter class
namespace CSharpWPF
{
public class TabItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
List<TabItemWrapper> result = new List<TabItemWrapper>();
foreach (TabItem item in (ItemCollection)value)
{
result.Add(new TabItemWrapper(item));
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
class TabItemWrapper : DependencyObject
{
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(TabItemWrapper), new PropertyMetadata(false));
public TabItemWrapper(TabItem source)
{
Binding b = new Binding("IsSelected");
b.Source = this;
b.Mode = BindingMode.TwoWay;
source.SetBinding(TabItem.IsSelectedProperty, b);
}
}
}
}
in this converter I binded the tab item's IsSelected to the property of a wrapper class and used it to bind in the view
Note: this converter work for static tab items only, if you intend to add or remove the tab items during runtime then perhaps you may need to handle CollectionChanged events to keep the result in sync.
result

How to reference a resource image in converter?

I have a few images defined in my resource section of my UI :
<Window.Resources>
<!-- Converters -->
<loc:UserStatusToIconConverter x:Key="UserStatusToIconConverter" />
<!-- Images -->
<BitmapImage x:Key="ConnectIcon" UriSource="/WPFClient;component/Images/connect.png" />
<BitmapImage x:Key="ActiveIcon" UriSource="/WPFClient;component/Images/active.png" />
<BitmapImage x:Key="IdleIcon" UriSource="/WPFClient;component/Images/idle.png" />
<BitmapImage x:Key="AwayIcon" UriSource="/WPFClient;component/Images/away.png" />
<BitmapImage x:Key="UnknownIcon" UriSource="/WPFClient;component/Images/unknown.png" />
...
I would like to select one of these for a binding in my converter, I assume this would be more efficient than creating a new image each time (500 times) from the converter.
public class UserStatusToIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string userStatus = value.ToString();
string iconName = ...;
switch (userStatus)
{
case "Active":
// select ActiveIcon;
break;
case "Idle":
// select IdleIcon;
break;
case "Away":
...
break;
}
return iconName;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Here is where I use it :
<ListBox ItemsSource="{Binding Users}">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Image Source="{Binding Status, Converter={StaticResource UserStatusToIconConverter}}" Height="16" Width="16" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I think you're better off using DataTemplate.Triggers in this case rather than a Converter:
<DataTemplate>
<DockPanel>
<Image x:Name="Img" Height="16" Width="16" />
<TextBlock Text="{Binding Nick}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Status}" Value="Active">
<Setter TargetName="Img" Property="Source" Value="{StaticResource ActiveIcon}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Status}" Value="Idle">
<Setter TargetName="Img" Property="Source" Value="{StaticResource IdleIcon}"/>
</DataTrigger>
<!-- And So on... -->
</DataTemplate.Triggers>
</DataTemplate>
You may just do the following in your Convert method:
return Application.Current.MainWindow.FindResource(iconName);

StackPanel Dynamic Binding of Visibility and IsEnabled Properties via Converters

My small objective is to implement a dynamic on-the-go generation of controls and associate their respective properties, such as Visibility and IsEnabled.
The controls must be inserted into the StackPanel. And based on some conditions, the children nodes properties would change. I've used the following nasted StackPanel structure:
The Parent StackPanel with Vertical orientation of Children (collection of Labels ad input Fields to simulate a form)
The Child node StackPanel with Horizontal orientation of Children (Label and an input field)
The main idea is that there is only one input field per each label: Date Picker, Combo, Text or any other. As result, I created a Label and a Grid with multiple Controls. I manipulate Visibility and IsEnabled properties of the input controls via Converters.
Here is the question: is it possible to implement all these by other means (more efficient/aesthetic)? Constructive criticism and suggestions are more than welcome :)
Thank you in advance.
XAML:
<StackPanel Grid.Row="1" Orientation="Vertical">
<ItemsControl ItemsSource="{Binding DataClass}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0, 5, 0, 0" >
<Label Content="{Binding KeyName}" Width="150"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="190"/>
</Grid.ColumnDefinitions>
<DatePicker Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='DateTime'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<ComboBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Lookup'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<TextBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Number'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
<TextBox Text="{Binding Value}"
Visibility="{Binding Type, Converter={StaticResource TypeVisiblity}, ConverterParameter='Text'}"
IsEnabled="{Binding RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource VisibilityEnabled}}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Visibility Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isMatched;
string type,
controlType;
// Safe Convert.
type = System.Convert.ToString(value);
controlType = System.Convert.ToString(parameter);
if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(controlType))
{
return Visibility.Hidden;
}
// Check matching.
isMatched = string.Equals(type, controlType, StringComparison.CurrentCultureIgnoreCase);
return isMatched ? Visibility.Visible : Visibility.Hidden;
}
IsEnabled Converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Control control;
control = value as Control;
if (control == null)
{
return false;
}
return control.Visibility == Visibility.Visible;
}
I would use a ContentControl and set the ContentTemplate in a style
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<!-- Default Template -->
<Setter Property="ContentTemplate"
Value="{DynamicResource TextBoxTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Type}" Value="DateTime">
<Setter Property="ContentTemplate"
Value="{DynamicResource DateTimeTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Type}" Value="Lookup">
<Setter Property="ContentTemplate"
Value="{DynamicResource ComboBoxTemplate}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Create a user control that has your DatePicker, ComboBox, TextBox etc as user interface and implement all necessary logic in code behind of this user control. You can add dependency properties to implement your logic without converters.
You can add your user control into StackPanel easily by adding it into its Children property.

Resources