Content Presenter in WPF exception - wpf

Exception using content presenter
Type 'System.Windows.Controls.ContentPresenter' does not have a content property.
Specify the name of the property to set, or add a ContentPropertyAttribute or
TypeConverterAttribute on the type.
Below is the XAML
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Series}" Width="50" VerticalAlignment="Center" Checked="CheckSeries_Checked" Unchecked="CheckSeries_UnChecked" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

You are trying to set the ContentPresenter.Content property implicitly by setting the inner text of the ContentPresenter control:
<ContentPresenter>
MyContent
</ContentPresenter>
Instead you should set it like this
<ContentPresenter Content="MyContent" />
You're getting this error because ContentPresenter doesn't have the ContentProperty Attribute which tells the XAML parser to set the inner text as the value for its Content property.

Well the only guess possible in this case is that you are trying to assign "content" property instead of "Content" with capital C. If it's not then post your XAML code please.

Have you tried using
Content="{TemplateBinding Content}"
Since it is a datatemplate, and the content will be set by the ItemSource used?

Related

DataTemplate Binding depending on property type and with working property Binding

I check those articles about doing DataTemplate :
WPF DataTemplate Binding
WPF DataTemplate and Binding
WPF DataTemplate Textblock binding
and thoses about DataTemplate depending on property type :
WPF DataTemplate Binding depending on the type of a property
Dynamically display a control depending on bound property using WPF
I'm trying to display a property with different controls depending of the property value. I have this Xaml that is partialy working. I have 2 problems :
The property is displaying with the right control, but when I set the value it doesn't go back to the property. Means the "set" of My property is not call (but was before I creates the DataTemplate). I detect that the problem about setting the property is about the ="{Binding Path=.}" but I cannot find the solution to set it otherwise.
Also To be able to make it work, I had to "isolate" the Value into a single ViewModel so that the DataTemplate doesn't affect all the other control.
Can you help me find betters solutions to resolves those 2 problems?
Here is the xaml code of my View linked with MyContainerViewModel that has a "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
More informations about 2 :
I wanted to have in a view a label and a property that changes depending of the object. Something like this :
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo". So I had to create another view that contains the DataTemplate and MyChangingProperty so that the label Allo would not be affected. But the extra View created just for one property is kind of ugly to me, I'm sure there is a better way to isolate the DataTemplate so it can apply only to one UIControl.
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
Note : MyContainerViewModel here is linked with the first view described.
Thanks in advance!
One possible solution would be to use a DataTemplateSelector. You cannot bind primitive types using two way bindings because that would have to be somehow by reference via the DataTemplate which I think is not supported by WPF.
The DataTemplateSelector now selects the right DataTemplate based on the property type and searches for the right DataTemplate in the resources by name. This also solves your problem that your DataTemplates interacted with the Label.
So first you need to define a DataTemplateSelector that changes the DataTemplate based on the type of the property:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
Then you need to change the UserControl like this:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
You can find a bit more information regarding the DataTemplateSelector here.
You can of course also set a DataType on this new DataTemplates but it isn't required because the x:Key makes them unique anyway. But if you want then it has to look like this:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
In my opinion, the previously posted answer is overkill. While a DateTemplateSelector is a useful thing to know about, it seems unnecessary to me in this scenario.
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
The reason it affects the Label object is that the Label object is a ContentControl, and so does the same template-matching behavior for content types as your own ContentPresenter element does. And you've set the content of the Label object to a string value. But you can put anything you want as the content for it.
The way to fix the undesired effect is to intercept that behavior by changing the content from a string object to an explicit TextBlock (the control in the template that a string object normally gets assigned). For example:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
In that way, you bypass the template-finding behavior (since TextBlock doesn't map to any template and can be used directly), and the content for the Label will just be the TextBlock with the text you want.
This seems like a lot simpler way to fix the issue, than either to create a whole new view or to add a DataTemplateSelector.

How to display too long text properly in WPF ComboBox

I have a ComboBox that shows text of various lengths. For texts that are not long there is not a problem. For the texts longer than the width of ComboBox I would like to trim the text and add "..." (an ellipsis) at the end to show them properly. The bottom line is that I don't want to change the width of the ComboBox. Does anyone know how to do this?
Use a custom ItemTemplate for your ComboBox, which makes use of a TextBlock with the TextTrimming property set to CharacterEllipsis.
Example:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding ...}"
TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The answer, as Ross said, is to implement a custom ItemTemplate. However, to make it work properly, you need to do the binding properly.
A note on this method: You cannot set both the DisplayMemberPath and the ItemTemplate, it must be one or the other.
So, for the general case where the display member is the item (such as for a string), you can use binding with no properties to bind to the DataContext of the template:
<ComboBox ItemsSource="..." SelectedValuePath="...">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Or, you can put it in a style.
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }" TextTrimming="CharacterEllipsis" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
For the case where you want to bind to a specific property of the object, similar to how you would use the DisplayMemberPath property, replace the binding with the binding that you would use to a property on the object that you are binding. So, replace the fourth line in my first example with something like this:
<TextBlock Text="{Binding MyDisplayMemberProperty}" TextTrimming="CharacterEllipsis" />
The binding is in the context of a single item of the type bound to your ComboBox. To make this more explicit, you can do the following:
<DataTemplate DataType="{x:Type namespace:MyItemType}">
<!-- My DataTemplate stuff here -->
</DataTemplate>
This will give you hints for the properties on the object while you are writing code inside the DataTemplate.
You can use TextTrimming CharacterEllipsis or WordEllipsis for the textblocks in your combobox.
Also works with a more complex DataTemplate; however, I had to resort to a DockPanel instead of the standard WrapPanel.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<AccessText DockPanel.Dock="Left" Text="{Binding Icon}"/>
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" />
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Binding ListBox Items using Templates

I am going crazy trying to figure this out without success.
I have a DependencyObject, ("UserObject"). It has a "DataItems" DependecyProperty that is an ObservableCollection. "UserDefiniton" is a DependencyObject with a DependencyProperty of "Data". Data has two properties: DataType (an enumeration) and Value (a string).
I am trying to define a ListBox in XAML that uses the "DataItems" property as its ItemsSource. In the ItemTemplate, I have several different controls. For simplicity of this issue, I am using a CheckBox and a TextBox. I want CheckBox to be available and visible when DataType is 0, while I want the TextBox to be available and visible when the DataType is 1. Only one control can be available and visible at a time.
This works:
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=0}"
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
<TextBox
Visibility="{Binding Path=Data.DataType, Mode=OneWay, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}"
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
<Listbox.ItemTemplate>
</ListBox>
The problem is that even though only one is visible, both are fighting over the Data.Value property (the boolean of the checkbox will show in the textbox, even though the checkbox is hidden).
Basically, though, the binding in this case is working--but the implementation is incorrect.
So, I switched to using Templates. The problem I am having is that I can't get the binding to work.
This is the code that I have for the Template. The Template selector is working correctly, but the Text property of the TextBox and the IsChecked property of the checkbox are not binding to Data.Value:
<DataTemplate x:Key="TextBoxItem">
<TextBox
Text="{Binding Path=Data.Value, Mode=TwoWay}" />
</DataTemplate>
<DataTemplate x:Key="CheckBoxItem">
<CheckBox
IsChecked="{Binding Path=Data.Value, Mode=TwoWay, Converter={StaticResource StringToBoolean}}" />
</DataTemplate>
...
<ListBox
ItemsSource={Binding DataItems, Mode=OneWay}>
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl
Content="{Binding Path=Data.DataType, Mode=OneWay}"
ContentTemplateSelector="{DynamicResource UserDefinitionTemplateSelector}"/>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
So how do I fix the binding?
Content should be set to {Binding}, since the Content will be the DataContext of the data-templates, hence you should just pass on the current DataContext. If you need to pass specific data to the template selector you can just drill down in the whole object.
There also is a template selector on the level of the ListBox, so you do not really need the internal ContentControl.
(You might also be interested in generic methods of debugging data bindings.)

WPF Element Binding in a within a Resource control doesnt work

I have some xaml that will just copy text from one text box to another:
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src1, Path=Text}" />
<TextBox x:Name="src1" Width="100" Height="30" />
</StackPanel>
Nothing special, works fine. A bit dumb but just an example.
However if I put the StackPanel as a resource in the Window and create dynamically from code, like this:
<Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
</Window.Resources>
.. then the element binding doesnt work anymore.
Why? and how to make it work? Any ideas gratefully received.
The following Xaml should work just fine
<Window ...>
<Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
</Window.Resources>
<StaticResource ResourceKey="MySP"/>
</Window>
You could also use it from code
StackPanel mySP = TryFindResource("MySP") as StackPanel;
if (mySP != null)
{
this.Content = mySP;
}
However, what is the reason for you to have a StackPanel in the Windows Resoures?
If you want to be able to reuse it several times you would have to set x:Shared="False" on the Resource but then you'll get an exception saying something like Cannot register duplicate Name 'src' in this scope the second time you add it.
As far as I'm concerned you should not put that in <Window.Resources>. Only styles, static, dynamic resources and such...
http://msdn.microsoft.com/en-us/library/ms750613.aspx
<Window>
<Window.Resources>
</Window.Resources>
<StackPanel x:Key="MySP" Orientation="Horizontal">
<TextBox Width="100" Height="30" Text="{Binding ElementName=src, Path=Text}"/>
<TextBox x:Name="src" Width="100" Height="30" />
</StackPanel>
<Window>
Having a similar issue, trying to get relative binding to my source control - In my case, I'm creating a designer and need the element as a static so styles can use it's dimensions for centering calculations on a canvas.
Taking a line from [WPF Xaml Namescopes],
ResourceDictionary does not use XAML names or namescopes ; it uses keys instead, because it is a dictionary implementation.
So, directly using ElementName in a resource Dictionary simply does not work, because no name will bind without a NameScope. Also attempted reproducing your situation with Style setters, but no luck - one cannot set an object's name via a Style.
Now, the convoluted solution I cam up with is to
Create a DependencyProperty in the code-behind of the class
you're declaring this resource in.
replace ElementName=Root with RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type namespace:RootClass}} and bind
to said container directly, allowing you to bind to said
DependencyProperty whilst bypassing the names.
If you need bindings to operate between two elements in the same
StaticResource, bind the source to said DependencyProperty as
OneWayToSource or TwoWay, and the destination as OneWay or TwoWay.
1

WPF UserControl Binding Problem

I have the following user control that is embedded within another user control.
<UserControl.Resources>
<DataTemplate x:Key="ContextsTemplate">
<Label Margin="10,0,20,0" Content="{Binding Name}"/>
</DataTemplate>
</UserControl.Resources>
<ItemsControl Name="Contexts"
Background="Transparent"
ItemsSource="{Binding}"
Margin="0,0,0,0"
VerticalAlignment="Center"
AlternationCount="2"
ItemTemplate="{StaticResource ContextsTemplate}">
</ItemsControl>
Here is XAML code for the user control (controlB) above that is embedded in another user control (controlA).
<local:ContextContentsUserControl Height="30" Content="{Binding Contexts}"/>
controlA is displayed on the screen as "(Collection)" but for some reason doesn't show each item in the collections text in the label. Please help.
The problem is here:
Content="{Binding Contexts}"
You meant:
DataContext="{Binding Contexts}"
The reason you got "(Collection)" instead of the content you had defined for controlA is the content you defined in the XAML for ControlA was replaced with your collection. The body of a UserControl's XAML file simply sets its Content property: You replaced it after it was set.
When you declare your ContextContentsUserControl, you are setting its Content property. You need to be setting the DataContext instead:
<local:ContextContentsUserControl Height="30" DataContext="{Binding Contexts}" />

Resources