Basically, I don't understand what the real difference here is:
The Microsoft code for TabItem uses:
<ContentPresenter ContentSource="Header" ... />
So, when would one use the Content property instead of (or in addition to) ContentSource?
This property should only be used when
the ContentPresenter is in a template.
When a template contains a
ContentPresenter with ContentSource
set to "Abc", the Content,
ContentTemplate, and
ContentTemplateSelector properties of
the ContentPresenter are automatically
aliased to Abc, AbcTemplate, and
AbcTemplateSelector, respectively.
Beginning with the .NET Framework
version 3.5 Service Pack 1, setting
ContentSource to "Abc" also causes the
ContentStringFormat property to be
aliased to AbcStringFormat.
The two most useful values for this
property are "Content" and "Header".
(MSDN)
ContentSource apparently sets more properties at once for convenience.
Practically, The declaration:
<ContentPresenter ContentSource="Header" />
Performs the following initialization.
<ContentPresenter Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}"
ContentStringFormat="{TemplateBinding HeaderStringFormat}" />
It does this for each property separately only if the dependency property exists on the templated control.
Related
I have a WPF combo box which ItemsSource is ObservableCollection<User>, where User has string Name and bool IsChecked.
Also I have
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsChecked}" Width="20" />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
, which nicely shows check boxes before each name and allows me to check/uncheck users.
What I need is to make combo box selected item to show not the selected user but all checked usernames separated by comma, ideally (if resultant string is too long) with ellipsis in the middle, i. e. "Alice, Bart...mew, John".
Possible?
A little bit tricky to implement, but certainly can be done.
First of all you'll want to replace the drop-down bar/button with a TextBlock, so you'll need to modify the control's template. Place the edit cursor anywhere inside the parent ComboBox declaration so that the control appears in the Properties panel at the bottom right. In properties, find Miscellaneous -> Template at the bottom, then click on the little down-arrow to the right and select "Convert to new resource". This will template out the control so that you can start editing it.
Next, find the ToggleButton's ContentPresenter inside the ControlTemplate. You'll want to change it's Content binding to point to a property in your view models which I'll call Names:
<!--<ContentPresenter x:Name="contentPresenter" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{TemplateBinding SelectionBoxItem}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>-->
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" Content="{Binding Names}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
The next change is to also add a binding to your CheckBox Command property called "NamesChangedCommand" which will be called whenever the user changes the state of a CheckBox:
<CheckBox IsChecked="{Binding IsChecked}" Width="20" Command="{Binding RelativeSource={RelativeSource AncestorType=ComboBox}, Path=DataContext.NamesChangedCommand}" />
Then back in your view model, all you have to do is implement this command and have it generate the new name list. Here's how you'd do that in MVVM Toolkit:
[RelayCommand]
public void NamesChanged()
{
this.Names = String.Join(", ",
this.Items
.Where(item => item.IsChecked)
.Select(item => item.Name));
}
[ObservableProperty]
private string names = "";
Result:
I was going to suggest the xceed checkcombobox
https://github.com/xceedsoftware/wpftoolkit/wiki/CheckComboBox
But I notice it's not free for commercial use any more. You could build something of your own based on their code though.
https://github.com/xceedsoftware/wpftoolkit/blob/master/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.Toolkit/CheckComboBox/Implementation/CheckComboBox.cs
The xaml will be in that repo as well.
I have a ControlTemplate for a Telerik Tile and I am overriding like below:
<ControlTemplate TargetType="{x:Type ctrl:Tile}">
<Border>
<local:UserControl>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</local:UserControl>
</Border>
</ControlTemplate>
My user control looks like:
<DockPanel>
<!-- some content -->
<ContentPresenter/>
</DockPanel>
The ControlTemplate does not display the content of the UserControl.
If I change my control template to:
<ControlTemplate TargetType="{x:Type ctrl:Tile}">
<Border>
<StackPanel>
<local:UserControl/>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</StackPanel>
</Border>
</ControlTemplate>
It will find the content and place it appropriately. It seems like the ControlTemplate cannot find the content once it's nested inside my UserControl. Is there anything I could be doing wrong?
Note that these ControlTemplate Items are appearing in an ItemsPresenter.
You're treating the UserControl as if it is a basic ContentControl (like a Button) which is a little different than what it actually is. Using Button as an example, when you add a child (i.e. a TextBlock) into a Button element that's actually setting that TextBlock as the Button's Content property. The way it gets rendered is through the Button's ControlTemplate, which includes a ContentPresenter to inject Content into. The Visual Tree ends up like this:
<Button>
-start Template
<Border>
<ContentPresenter>
-start Content
<TextBlock>
So far that's basically the model your code is following. The problem is that you're using a (still ContentControl derived) UserControl instead, which rather than using a ControlTemplate is most often defined with a XAML+code-behind model, where the XAML defines the Content of the UserControl. (It is possible to switch these models and template a UserControl or make a Button derived class with XAML+code-behind but not common)
If you want to both define the look of your UserControl in XAML as normal and still be able to inject other content you can add another DependencyProperty that mirrors the setup of the Content property and set your content to that. This approach is used with HeaderedContentControl derivatives (i.e. Expander) which essentially has 2 content properties, Content and Header. Using the new property would look like this:
<Border>
<local:UserControl>
<local:UserControl.OtherContent>
<ContentPresenter Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
</local:UserControl.OtherContent>
</local:UserControl>
</Border>
And then inside the UserControl's XAML you need to explicitly set up the ContentPresenter Bindings (you only get them for free inside templates of ContentControls):
<DockPanel>
<!-- some content -->
<ContentPresenter Content="{Binding Path=OtherContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"/>
</DockPanel>
If you want a ContentTemplate, ContentTemplateSelector, or ContentStringFormat you'll also need to add properties and bindings for those.
What property do I need to set so that the text near the cursor within a WPF textbox is always visible. I have to set the width of a one line textbox and once I type outside of the width I can't see what I'm typing I can set ScrollViewer.CanContentScroll = True but that makes the height increase, which is better than the former but I would prefer the text near the cursor to be visible like common WinForms textbox behavior.
Any ideas? I figured a WPF guru or maybe even novice could answer this question faster than it would take me to experiment/google
Thanks in advance
The behavior you are describing should be how it operates by default. To test this I just created a TextBox...
<TextBox Width="50" Height="22" ></TextBox>
...began typing and the most recent character I typed is where the cursor was living and therefore what was visible.
Perhaps you have a style or something applied that is negating this default behavior.
Aaron McIver's answer points at the styles, and OP comments that their global style setting TextWrapping="WrapWithOverflow" was their problem. I had a similar problem but with a different solution. In my case the problem was that the style of ScrollViewer had just
<ScrollContentPresenter Margin="1" />
for the ScrollContentPresenter. The default template has
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0" />
Of the missing properties, the really important one is CanContentScroll.
I am not knowing what to use to override an existing template (either ContentTemplate, Content). Imagine I have the following:
<Style x:Key="ComboBoxItemStyle1" TargetType="ComboBoxItem">
.
.
<ControlTemplate TargetType="ComboBoxItem">
.
.
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}"/>
What is the difference between "ContentTemplate" and "Content" in the above markup (also, what is good for what).
Also, where can I find more information on "TemplateBinding"?
thanks
Content is generally used for your data. It will be set to the DataContext of the your ContentTemplate DataTemplate. The benefit of Content vs. ContentTemplate is the ability to change the ContentTemplate to display the same content in a different fashion.
Template Binding
I am creating a custom control Toolbox that is derived from ItemsControl. This toolbox is supposed to be filled with icons coming from the database. The definition looks like this:
public class Toolbox : ItemsControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new ToolboxItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return (item is ToolboxItem);
}
}
Toolboxitem is derived from ContentControl.
public class ToolboxItem : ContentControl
{
static ToolboxItem()
{
FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(ToolboxItem), new FrameworkPropertyMetadata(typeof(ToolboxItem)));
}
}
Since the number of icons stored in a database is not known I want to use the data template:
<DataTemplate x:Key="ToolBoxTemplate">
<StackPanel>
<Image Source="{Binding Path=url}" />
</StackPanel>
</DataTemplate>
Then I want the Toolbox to use the template.
<Toolbox x:Name="NewLibrary" ItemsSource="{Binding}" ItemTemplate="ToolBoxtemplate">
</Toolbox>
I'm using ADO.NET entity framework to connect to a database. The code behind:
SystemicsAnalystDBEntities db = new SystemicsAnalystDBEntities();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
NewLibrary.ItemsSource = from c in db.Components select c;
}
However, there is a problem. When the code is executed, it displays the object from the database (as the ItemSource property is set to the object from the database) and not the images. It does not use the template. When I use the static images source it works in the right way
I found out that I need to override the PrepareContainerForItemOverride method.But I don't know how to add the template to it.
Thanks a lot for any comments.
Additional Information
Here is the ControlTemplate for ToolboxItem:
<ControlTemplate TargetType="{x:Type s:ToolboxItem}">
<Grid>
<Rectangle Name="Border"
StrokeThickness="1"
StrokeDashArray="2"
Fill="Transparent"
SnapsToDevicePixels="true" />
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="true">
<Setter TargetName="Border"
Property="Stroke"
Value="Gray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
ToolboxItem is overriding the default style for ContentControl. You haven't posted the overridding style (from generic.xaml), but I suspect your problem is with the template defined in that style. Your ToolboxItem template needs to contain a ContentPresenter, e.g.:
<Style TargetType="{x:Type local:ToolboxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ToolboxItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Alternatively, if you don't need to do anything special in the ToolboxItem UI, just remove the DefaultStyleKeyProperty.OverrideMetadata call.
Note that you do not need to override PrepareItemForContainerOverride.
You have correctly implemented the methods. The problem is, as I suspected, in your ToolBoxItem ControlTemplate which you posted recently. If it had used an ordinary <ContentPresenter /> you would have been fine. You ran into ContentPresenter's "magic" properties which are only set automatically if you don't set any of them.
Here is the problem code in your ControlTemplate:
<ContentPresenter
Content="{TemplateBinding ContentControl.Content}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
The problem is that you are setting the Content property but not setting the ContentTemplate property. ContentPresenter has custom code that automatically creates bindings for its Content, ContentTemplate, and ContentTemplateSelector properties, but only if the Content property is not set manually.
In your case, the Content property is being set manually, so the automatic mechanism is disabled and so ContentTemplate is null.
Although it would be possible to manually set all three automatic properties like this:
<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
Your best choice is to omit them entirely and just accept ContentPresenter's default behavior, like this:
<ContentPresenter
Margin="{TemplateBinding Padding}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
Note: NET Framework 3.5 adds a fouth automatically-bound property, ContentStringFormat, which would be missed if you manually bound the three properties instead of letting ContentPresenter do it for you automatically.
Is this code directly copied from your project? If so, the following
ItemTemplate="ToolBoxtemplate"
should be:
ItemTemplate="{StaticResource ToolBoxTemplate}"
I'm not entirely sure, but you might need to set the ContentTemplate of your Toolbox container explicitly in PrepareContainerForItemOverride, since you may have overridden the behavior that automatically sets the template. You may need to set it as a Binding, as I'm not sure if the containers are re-generated if the ItemTemplate changes.
It looks like your problem could be that your url property is not exposed within ToolBoxItem. When your items are bound directly to ToolBox the url property is directly exposed to the DataTemplate.
In order for your example to work, ToolBoxItem would need to have:
public ImageTypeHere url { get; private set; }
If this is really a simple implementation it would probably benefit you more to use (or at least derive from) a ListBox and use a custom DataTemplate and Style for your ListBoxItems rather than creating your own control.