How does the Content property work - wpf

Both syntaxes below works. My question is how does WPF know Content is what I specified?
<Button>
<Button.Content>
my button
</Button.Content>
</Button>
<Button>
my button <!--how does wpf know this is the content-->
</Button>
similarly, how does wpf know I'm now adding ListBoxItems
<ListBox>
<!--ListBox.Items-->
<ListBoxItem Content="item 1" /> <!--XAML automatically knows I'm specifying items-->
item 2
<ListBoxItem Content="item 3" />
<!--/ListBox.Items-->
</ListBox>
So in ContentControl, the default property is Content, and with ItemsControl, the default is Items, and with TextBox, the default is TextBox.
How does this 'default' work?
How do I create this 'default property' when I creat a Custom Control?

ContentPropertyAttribute tells xaml parser which property to use for direct content.
ContentControl, from which Button inherits, is marked with attibute [ContentProperty("Content")]. Similarly, ItemsControl (ancestor of ListBox) is marked with [ContentProperty("Items")].

Related

Binding TabControl.ItemsSource to ViewModels whose View's are a TabItem

I need some TabItems to have a customized Header.
For example, given the following (working) XAML:
<TabControl>
<TabItem>
<TabItem.Header>
<Button>Header 1</Button>
</TabItem.Header>
<Label>Content 1</Label>
</TabItem>
<TabItem>
<TabItem.Header>
<Label>Header 2</Label>
</TabItem.Header>
<Grid>
<TextBlock>Content 2</TextBlock>
</Grid>
</TabItem>
</TabControl>
I would like to extract the tab items into their own Views + ViewModels. The TabItem's View should still be a TabItem, so that I can configure the Header per tab item instead of setting TabControl.ItemTemplate and using a DataTemplateSelector to achieve different headers per tab item.
At the same time I'd need to be able to bind the selected tab item view model to a property ActiveItem. => The underlying view-model for the TabControl is a Conductor.Collection.OneActive<T> (only the selected tab should be activated).
If there's an alternative to using TabItem as view-type, but still achieving the Header and Content to be specified in the same view, it would be acceptable, too.
You should be able to achieve this by binding a TabControlViewModel to the TabControl, and that VM should have an ObservableCollection of TabViewModels (maybe a base class or interface). You would bind your collection of TabViewModels to the TabControl's ItemsSource. Here is my implementation, but using the Telerik TabControl (should be same for MS):
<telerik:RadTabControl x:Name="RadTabControl"
Grid.Row="0"
Align="Justify"
ContentTemplateSelector="{StaticResource LoggerDataTemplateSelector}"
IsContentPreserved="True"
IsDefaultItemSelected="True"
ItemsSource="{Binding LogHistory}"
SupressSelectedContentTemplateReapplying="False">
<telerik:RadTabControl.ItemContainerStyle>
<!-- Allow IsSelected to be bound to view models-->
<Style BasedOn="{StaticResource RadTabItemStyle}"
TargetType="{x:Type telerik:RadTabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</telerik:RadTabControl.ItemContainerStyle>
<telerik:RadTabControl.ItemTemplate>
<!-- Define what is shown in the header -->
<DataTemplate>
<Grid Height="30">
<TextBlock VerticalAlignment="Center"
Text="{Binding Title}" />
</Grid>
</DataTemplate>
</telerik:RadTabControl.ItemTemplate>
</telerik:RadTabControl>
Be aware, MS doesn't have something like IsContentPreserved, so switching tabs that have a lot of data to show will be rather timely. There are also a couple other properties not in MS TabControl, however the important properties should all be there. In this case, your TabViewModel should have a IsSelected property and Title property.

Override tab navigation for XAML ListBox

I want a list of items that are selectable, so naturally I chose ListBox. However the tab/arrowing behavior is not what I want. I need the tab behavior of ItemsControl without the arrow key functionality. So if I have three UIElements like:
ButtonA
ListBox
ButtonB
then the tab order will be:
ButtonA, ListBoxItem1, ... ListBoxItemN, ButtonB, ButtonA, ....
Unfortunately the default ListBox tab navigation makes the tab order:
ButtonA, ListBoxSelectedItem, ButtonB, ButtonA, ....
All is well if I use an ItemsControl, but ItemsControl has no selected item logic.
setting the KeyboardNavigation.TabNavigation in the ListBox to "Continue" should do it :
<StackPanel>
<Button Content="ButtonA"/>
<ListBox KeyboardNavigation.TabNavigation="Continue">
<ListBoxItem Content="One"/>
<ListBoxItem Content="Two"/>
<ListBoxItem Content="Three"/>
<ListBoxItem Content="Four"/>
</ListBox>
<Button Content="ButtonB"/>
</StackPanel>

CheckBox behavior difference in a tool bar or as a control

I can get a CheckBox control's isChecked property to a bool property when I place it as a control on a user control or a window. However, if I place it in a Toolbar, it is not even visible except maybe thin left or right edges (but the binding still works). If I put in in a ContextMenu, it appears fine as checked or unchecked, but the binding target is not affected.
This is the ContextMenu definition in Window.Resources:
<ContextMenu x:Key="ContentMenu" DataContext="{Binding MyView}">
<CheckBox IsChecked="{Binding Path=IsRed}"/>
</ContextMenu>
and this is the StackPanel that contains the CheckBox and the ToolBar with a CheckBox:
<StackPanel Height="20" Orientation="Horizontal" DockPanel.Dock="Top" DataContext="{Binding MyView}">
<CheckBox IsChecked="{Binding Path=IsRed}"/>
<ToolBar Margin="10,0,0,0">
<CheckBox Width="18" Height="18" IsChecked="{Binding Path=IsRed}"/>
</ToolBar>
</StackPanel>
I would only ask
1) if anyone could suggest a solution to the toolbar issue (a different template, perhaps?),
2) the correct way to define the DataContext of the context menu (which seems to be the actual problem).
I see what you mean, the chekbox looks like a toggle button now. If you look at the controltemplate of the toolbar you see it adjusts the controltemplates of some types of child controls.
There is a trick or bug if you will, if you put the Checkbox in some layoutcontainer such as Stackpanel, Grid etc. , then its controltemplate is not adjusted:
<ToolBar>
<Grid><CheckBox IsChecked="True" VerticalAlignment="Center">Test</CheckBox></Grid>
</ToolBar>

WPF style for panel/border with triggers for children/parent?

Is it possible in XAML to
Define a style for panel that creates a border for each panel?
Change the style of an ancestor item (i.e. the border background) when an item or it's child is selected?
i.e.
<StackPanel Style="{StaticResource WidgetStyle}">
<Label />
<Button />
<StackPanel>
<ListView />
</StackPanel>
</StackPanel>
I would like to have WidgetStyle define a (rounded) border and change the border color, e.g. if the ListView or Button is selected.
Thanks!
I'm afraid the answer to your first question is no! You cannot template Panel controls, they are lookless. What you could do in this. There are a few options, such as create a custom control that include a StackPanel. The answe to this question includes quite a few good ideas:
Changing WPF StackPanel template
For your second question, again no, you cannot style an ancestor based on a style change in a child by styling alone.
You could use ElementName binding to connect some properties of the elements together:
<Border Background={Binding ElementName=MyButton, Path=Tag>>
<StackPanel Style="{StaticResource WidgetStyle}">
<Label />
<Button x:Name="MyButton"/>
<StackPanel>
<ListView />
</StackPanel>
</StackPanel>
</Border>
In the above, the border background is bound to the Tag property of the button 'MyButton' you can then apply a style to the button which sets the Tag property and will thus change the border background.

EditingCommands of the WPF RichTextBox loose its binding when they are not put within a ToolBar

Using the below code in a DataGridTemplateColumn of the DataGrid, my formatting buttons are disabled(grayed out). The formatting buttons are only enabled when they are put in a ToolBar.
When the buttons are put in a ToolBar I do not need the CommandTarget. So when I put them outside a ToolBar some could think it must work with CommandTarget, but it does not, WHY ?
<Button Content="K" CommandTarget="{Binding ElementName=RTFBox}" Command="EditingCommands.ToggleItalic"/>
<Button Content="U" CommandTarget="{Binding ElementName=RTFBox}" Command="EditingCommands.ToggleUnderline" />
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Helper:RichTextBox VerticalScrollBarVisibility="Auto" x:Name="RTFBox" LostFocus="RTFBox_LostFocus" Text="{Binding Notes, UpdateSourceTrigger=PropertyChanged}" >
<Helper:RichTextBox.TextFormatter>
<Helper:RtfFormatter />
</Helper:RichTextBox.TextFormatter>
<RichTextBox.CommandBindings>
<CommandBinding Command="EditingCommands.ToggleUnderline"/>
<CommandBinding Command="EditingCommands.ToggleItalic"/>
</RichTextBox.CommandBindings>
</Helper:RichTextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
ToolBar has FocusManager.IsFocusScope="True" which is by default false.
Just put FocusManager.IsFocusScope="True" inside the panel that holds the buttons.
CommandTarget is to limit the buttons if they are in a IsFocusScope="True" panel - e.g. if you have two RichTextBoxes and you only want the buttons to work on one of them.

Resources