WPF TemplateBinding to an ObservableCollection - wpf

I'm creating a content control that contains another usercontrol. We'll call them InnerControl and OuterControl. The InnerControl has a dependency property of type ObservableCollection called "Items." I'm trying to bind that to an identical Dependency Property in the OuterControl. Here is a stub of the InnerControl code:
public class InnerControl : UserControl {
public InnerControl() {
InnerItems = new ObservableCollection<string>();
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata());
}
The outer control contains an identical Items property:
public class OuterControl : ContentControl {
public OuterControl() {
OuterItems = new ObservableCollection<string>();
}
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl),
new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata());
}
Then I'm defining the OuterControl's appearance in the generic.xaml file:
<Style TargetType="{x:Type userControls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type userControls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
<ContentPresenter Grid.Row="1" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The really important part of the above code I want to call your attention to is:
<local:InnerControl Grid.Row="0" Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}"/>
What I expect to happen is that when items are added to the OuterItems collection of the OuterControl, those same items will be added to the InnerControl.InnerItems collection. However, that doesn't happen, and I can't figure out why.
I've also tried a relative binding so that I could experiment with using TwoWay mode and so on. Something like this:
InnerItems="{Binding OuterItems, Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
But so far that hasn't worked either.
UPDATE
Everything that I thought solved this problem so far has only exposed new problems, so I've removed my previous updates. What I'm stuck with at this point is:
If I initialize InnerItems in the constructor, then the TemplateBinding doesn't seem to work (the items never get updated)
If I don't initialize InnerItems at all, the TemplateBinding works. However, if InnerControl is just used by itself in the Designer, it breaks, because InnerItems is null when the designer tries to add items to it.

When you have a collection type dependency property, you must not use an instance of the collection class as default value of the property. Doing so will make all instances of the control that owns the property use the same collection instance.
So your property metadata
new PropertyMetadata(new ObservableCollection<string>())
should be replaced by
new PropertyMetadata(null)
or you do not specify any metadata at all
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register(
"InnerItems", typeof(ObservableCollection<string>), typeof(InnerControl));
Now you would somehow have to initialize the property value. As usual, you'll do it in the control's constructor, like
public InnerControl()
{
InnerItems = new ObservableCollection<string>();
}
When you now bind the property of the control like
<local:InnerControl InnerItems="{Binding ...}" />
the value set in the constructor is replaced by the value produced by the Binding.
However, this does not happen when you create the Binding in a Style Setter, because values from Style Setters have lower precedence than so-called local values (see Dependency Property Value Precedence).
A workaround is to set the default value by the DependencyObject.SetCurrentValue() method, which does not set a local value:
public InnerControl()
{
SetCurrentValue(InnerItemsProperty, new ObservableCollection<string>());
}

I find it quite likely that #Clemens comment has the right answer. Anyhow, I tested your solution using the code below and it worked fine for me.
Check how you are binding and adding items. You did not post that code in your question.
OuterControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class OuterControl : UserControl
{
static OuterControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(OuterControl), new FrameworkPropertyMetadata(typeof(OuterControl)));
}
public ObservableCollection<string> OuterItems
{
get { return (ObservableCollection<string>)GetValue(OuterItemsProperty); }
set { SetValue(OuterItemsProperty, value); }
}
public static DependencyProperty OuterItemsProperty =
DependencyProperty.Register("OuterItems",
typeof(ObservableCollection<string>),
typeof(OuterControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
InnerControl
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest.Controls
{
public class InnerControl : UserControl
{
static InnerControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(InnerControl), new FrameworkPropertyMetadata(typeof(InnerControl)));
}
public ObservableCollection<string> InnerItems
{
get { return (ObservableCollection<string>)GetValue(InnerItemsProperty); }
set { SetValue(InnerItemsProperty, value); }
}
public static DependencyProperty InnerItemsProperty =
DependencyProperty.Register("InnerItems",
typeof(ObservableCollection<string>),
typeof(InnerControl),
new PropertyMetadata(new ObservableCollection<string>()));
}
}
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:TemplateBindingTest.Controls">
<Style TargetType="{x:Type controls:OuterControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:OuterControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding OuterItems}" />
<Border Grid.Row="2" BorderThickness="1" BorderBrush="Red">
<controls:InnerControl Grid.Column="0"
InnerItems="{TemplateBinding OuterItems}">Inner Control</controls:InnerControl>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:InnerControl}">
<Setter Property="Padding" Value="10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:InnerControl}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Grid.Row="0" Grid.Column="0"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
<ItemsControl Grid.Row="1" ItemsSource="{TemplateBinding InnerItems}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="TemplateBindingTest.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:controls="clr-namespace:TemplateBindingTest.Controls"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<controls:OuterControl OuterItems="{Binding OuterItems}">Outer Control</controls:OuterControl>
<Button Grid.Row="1" Content="Add" Click="Button_Click" HorizontalAlignment="Left" />
</Grid>
</Window>
MainWindow.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
namespace TemplateBindingTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ObservableCollection<string> _OuterItems;
public MainWindow()
{
InitializeComponent();
DataContext = this;
_OuterItems = new ObservableCollection<string>(new List<string>()
{
"Test 1",
"Test 2",
"Test 3",
});
}
public ObservableCollection<string> OuterItems
{
get
{
return _OuterItems;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_OuterItems.Add(System.IO.Path.GetRandomFileName());
}
}
}

Related

How to use ContentPresenter properly in custom WPF UserControl

I want do have a custom UserControl in WPF that basically only puts a caption TextBlock over the actual content (I call it 'AttributePanelItem' here.
However, in my current approach the TextBlock is not shown when I direclty assign the Content of the user control.
This is my current XAML for the custom UserControl:
<UserControl x:Class="Common.Controls.AttributePanelItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Name="MyAttributePanelItem"
d:DesignHeight="100" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Caption, ElementName=MyAttributePanelItem}"/>
<ContentPresenter Grid.Row="1" Content="{Binding InputMask, ElementName=MyAttributePanelItem}" />
</Grid>
</UserControl>
Here the code behind:
public AttributePanelItem()
{
InitializeComponent();
}
public static readonly DependencyProperty CaptionProperty = DependencyProperty.Register("Caption", typeof(string), typeof(AttributePanelItem), new PropertyMetadata(string.Empty));
public string Caption
{
get { return (string)GetValue(CaptionProperty); }
set { SetValue(CaptionProperty, value); }
}
public static readonly DependencyProperty InputMaskProperty = DependencyProperty.Register("InputMask", typeof(object), typeof(AttributePanelItem), new PropertyMetadata(null));
public object InputMask
{
get { return (object)GetValue(InputMaskProperty); }
set { SetValue(InputMaskProperty, value); }
}
This is my current XAML to use the custom UserControl:
<controls:AttributePanel>
<controls:AttributePanelItem Caption="This caption is shown">
<controls:AttributePanelItem.InputMask>
<TextBox Text="This is my input 1" />
</controls:AttributePanelItem.InputMask>
</controls:AttributePanelItem>
<controls:AttributePanelItem Caption="This caption is not shown">
<TextBox Text="This is my input 2" />
</controls:AttributePanelItem>
</controls:AttributePanel>
In my implementation I use the AttributePanelItem two times.
1. The first usage is working as expected however this is not my favorite.
2. The second usage is how I would like to have it. Unfortunately in this case the caption-TextBlock is not shown.
Would it be possible to make the second case work (with showing the caption TextBlock, but without having to use the )?
I assume I am using the ContentPresenter wrong. However I do not know what I need to change.
Could you please help?
Your use of ContentPresenter is correct, but it should be inside a ContentTemplate. You need to change the UserControl.ContentTemplate to do what you want:
<UserControl
x:Class="WpfApp1.AttributePanelItem"
x:Name="MyAttributePanelItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<UserControl.Style>
<Style TargetType="UserControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="UserControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Caption, ElementName=MyAttributePanelItem}" />
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Style>
</UserControl>
Now you'll be able to use your second usage. In fact, you can completely remove InputMask from your UserControl (unless you're using it for something else).

Why the Dependency Property can no be use in usercontrol?

I want to make a brand new button, so that I create a usercontrol inherits Button to do this.
Here is the XAML:
<Button x:Class="Uploader.PropertyButtonControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Uploader"
mc:Ignorable="d"
xmlns:s="clr-namespace:Svg2Xaml;assembly=Svg2Xaml"
d:DesignHeight="450" d:DesignWidth="800">
<Button.Template>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.6*"></RowDefinition>
<RowDefinition Height="0.4*"></RowDefinition>
</Grid.RowDefinitions>
<Border Width="{Binding Path=ActualHeight,RelativeSource={RelativeSource Self}}" Grid.RowSpan="2" Background="{Binding IconBackground,Mode=TwoWay}">
<s:SvgShape Source="{Binding IconSource,Mode=TwoWay}"></s:SvgShape>
</Border>
<TextBlock Grid.Column="1" Text="{Binding ButtonTitle,Mode=TwoWay}"></TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1" Foreground="#575757" Text="{Binding ButtonContent,Mode=TwoWay}"></TextBlock>
<Border Grid.ColumnSpan="2" Grid.RowSpan="2" BorderBrush="#cecece" BorderThickness="1" Visibility="Collapsed" Name="Bo"></Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bo" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
And here is code-behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Uploader
{
/// <summary>
/// Interaction logic for PropertyButtonControl.xaml
/// </summary>
public partial class PropertyButtonControl : Button
{
public PropertyButtonControl()
{
InitializeComponent();
}
public SolidColorBrush IconBackground
{
get { return (SolidColorBrush)GetValue(IconBackgroundProperty); }
set { SetValue(IconBackgroundProperty, value); }
}
// Using a DependencyProperty as the backing store for IconBackground. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconBackgroundProperty =
DependencyProperty.Register("IconBackground", typeof(SolidColorBrush), typeof(PropertyButtonControl),null);
public ImageSource IconSource
{
get { return (ImageSource)GetValue(IconSourceProperty); }
set { SetValue(IconSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for IconSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(PropertyButtonControl), null);
public string ButtonTitle
{
get { return (string)GetValue(ButtonTitleProperty); }
set { SetValue(ButtonTitleProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonTitle. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonTitleProperty =
DependencyProperty.Register("ButtonTitle", typeof(string), typeof(PropertyButtonControl), null);
public string ButtonContent
{
get { return (string)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
// Using a DependencyProperty as the backing store for ButtonContent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(string), typeof(PropertyButtonControl), null);
}
}
If I used it in a page as:
<local:PropertyButtonControl IconBackground="Red" ButtonTitle="123" ButtonContent="456"></local:PropertyButtonControl>
After the program ran ,the content do not showed and color not changed? It seems is the binding problem.
But what's wrong with this? Thank you.
Your problem is the UserControl which is not adapted for a brand new button.
You should rather use a CustomControl. Read this if you want more details.
Here is how we do:
Create a new class inheriting from the Button control in a separate file PropertyButtonControl.cs:
public class PropertyButtonControl : Button
{
//No need for Constructor and InitializeComponent
public SolidColorBrush IconBackground[...]
public static readonly DependencyProperty IconBackgroundProperty =
DependencyProperty.Register("IconBackground", typeof(SolidColorBrush), typeof(PropertyButtonControl), null);
public ImageSource IconSource[...]
public static readonly DependencyProperty IconSourceProperty =
DependencyProperty.Register("IconSource", typeof(ImageSource), typeof(PropertyButtonControl), null);
public string ButtonTitle[...]
public static readonly DependencyProperty ButtonTitleProperty =
DependencyProperty.Register("ButtonTitle", typeof(string), typeof(PropertyButtonControl), null);
public string ButtonContent[...]
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(string), typeof(PropertyButtonControl), null);
}
Create a ResourceDictionary containing the XAML template in a separate file PropertyButtonControl.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:StackTest.View">
<Style TargetType="{x:Type view:PropertyButtonControl }">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.6*"/>
<RowDefinition Height="0.4*"/>
</Grid.RowDefinitions>
<Border Width="{Binding Path=ActualHeight, RelativeSource={RelativeSource Self}}"
Grid.RowSpan="2"
Background="{Binding Path=IconBackground, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}">
</Border>
<TextBlock Grid.Column="1"
Text="{Binding Path=ButtonTitle, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}"/>
<TextBlock Grid.Column="1"
Grid.Row="1"
Foreground="#575757"
Text="{Binding Path=ButtonContent, RelativeSource={RelativeSource AncestorType=view:PropertyButtonControl}}"/>
<Border Grid.ColumnSpan="2"
Grid.RowSpan="2"
BorderBrush="#cecece"
BorderThickness="1"
Visibility="Collapsed"
Name="Bo"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Bo" Property="Visibility" Value="Visible"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Add this resource dictionary to your App.XAML resources:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="View/PropertyButtonControl.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Enjoy your control:
<local:PropertyButtonControl IconBackground="Blue" ButtonTitle="123" ButtonContent="456"/>
I put all my files in a folder named View. You must adapt it to your own structure.

WPF Change a property in a customcontrol after a button click

I posted a question in this link. maybe I'm not well expressed.
It's very simple, I want to change a property in a usercontrol or CustomControl after a click on a Boutton outside...
The code of the customcontrol is as follows :
<Style TargetType="{x:Type local:CustomControl1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl1}">
<Border x:Name="container" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Hidden" Value="true">
<Setter Property="BorderBrush" Value="Blue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public class CustomControl1 : Control
{
static CustomControl1()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
}
public bool Hidden
{
get { return (bool)GetValue(HiddenProperty); }
set { SetValue(HiddenProperty, value); }
}
// Using a DependencyProperty as the backing store for Hidder. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HiddenProperty =
DependencyProperty.Register("Hidden", typeof(bool), typeof(CustomControl1), new PropertyMetadata(false));
}
And a simple window for test
<Window x:Class="WpfTestCustomControl.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCustomBorder;assembly=WpfCustomBorder"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400"/>
<ColumnDefinition Width="70"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<local:CustomControl1 x:Name="cc" BorderBrush="Red" BorderThickness="3" Margin="10" Grid.RowSpan="2"/>
<Button Grid.Column="1" Content="Ok" Margin="5" Click="Button_Click"/>
</Grid>
namespace WpfTestCustomControl
{
/// <summary>
/// Logique d'interaction pour MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
cc.Hidden = true;
}
}
}
The property "Hidden" is a dependency property inside the custom control.
When i click on the button in mainwindow i want to change the hidden property to true. this must fire the trigger inside the custom control to change borderbrush to "blue" color. While nothing happen.
Is there something missing or is not the right way to do it ?
Thanks in advance..
Don't hard-set BorderBrush="Red" in your Control's declaration, it's prioritary over any trigger's setter.
You might want to check msdn's Dependency Property Value Precedence

Change input form into a display form

Some context:
Users enter data into a Window with multiple input control (standard textbox, combobox etc).
Users open same window in -readmode- displaying previously entered data.
Sure, input form is easy and for readmode I can use the IsEnabled dependancyproperty to disable the input controls.
Is it possible to replace all input controls with labels using Style with Triggers?
This will turn all TextBoxes into TextBlocks when IsReadOnly is true. As you guessed, there's a trigger on the Style that changes the control template.
<Window x:Class="SO_Xaml_ReadOnlyInputForm.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsReadOnly}"
Value="True">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<TextBlock Text="{TemplateBinding Text}"
Width="{TemplateBinding Width}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0"
IsChecked="{Binding IsReadOnly}"
Content="Is Read-only?" />
<StackPanel Grid.Row="1"
Orientation="Horizontal">
<TextBlock>Item1</TextBlock>
<TextBox Text="{Binding Item1Text}"
Width="100" />
</StackPanel>
</Grid>
</Window>
VIEWMODEL class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.ViewModel;
using Microsoft.Practices.Prism.Commands;
namespace SO_Xaml_ReadOnlyInputForm
{
public class ViewModel : NotificationObject
{
private string _itemText;
private bool _isReadOnly;
public string Item1Text
{
get { return _itemText; }
set
{
_itemText = value;
RaisePropertyChanged(() => Item1Text);
}
}
public bool IsReadOnly
{
get { return _isReadOnly; }
set
{
_isReadOnly = value;
RaisePropertyChanged(() => IsReadOnly);
}
}
public ViewModel()
{
Item1Text = "This is the text";
}
}
}

Elements are not shown with ItemsPanelTemplate

I just try to understand the concept of the ItemsPanelTemplate. For this i built a small sample solution.
I have a UserControl "MyListView" with the following Code.
MyListView.xaml:
<UserControl x:Class="WpfApplication2.MyListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="Black" BorderThickness="1" Margin="0" Padding="0" Width="100" Background="Gray">
<TextBlock Text="Text" HorizontalAlignment="Center" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<ListView ItemsSource="{Binding Path=TreeItemChildren}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
</StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
</UserControl>
In MyListView.cs I added a DependencyProperty to bind the data to that should be displayed:
public partial class MyListView : UserControl
{
public MyListView()
{
this.TreeItemChildren = new ObservableCollection<string>();
this.TreeItemChildren.Add("Text0");
this.TreeItemChildren.Add("Text1");
this.TreeItemChildren.Add("Text2");
InitializeComponent();
}
public ObservableCollection<string> TreeItemChildren
{
get { return (ObservableCollection<string>)GetValue(TreeItemChildrenProperty); }
set { SetValue(TreeItemChildrenProperty, value); }
}
// Using a DependencyProperty as the backing store for TreeItemChildren. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TreeItemChildrenProperty =
DependencyProperty.Register("TreeItemChildren", typeof(ObservableCollection<string>), typeof(MainWindow), new UIPropertyMetadata(null));
}
When I now try to use this UserControl in my MainWindow, there is no data to be displayed. What is the reason for that?
You have not bound any ListBoxItem properties in the control template, the Content is not displayed for that reason.
Usually you would replace this:
<TextBlock Text="Text" HorizontalAlignment="Center" />
with:
<ContentPresenter HorizontalAlignment="Center"/>
(If the templated control is a ContentControl the ContentPresenter binds to the Content related properties automatically)
Also the ListView.ItemsSource binds to a property on the DataContext (which isn't set and should not be set since it interferes with bindings on the instances of the control), change it to somehow target the UserControl (e.g. use ElementName or RelativeSource).
(There should be binding errors caused by the ItemsSource binding, learn how to debug them)

Resources