Default template for ComboBox: How does IsEditable switch TextBox and Contentpresenter - c#

I am using ComboBox in WPF but there is one behavior that I don't understand: here is the link of the default style/template of ComboBox . When IsEditable is True, the TextBox shows and the user can type text to search. I didn't quite understand this so I was checking the code:
<ContentPresenter x:Name="ContentSite"
IsHitTestVisible="False"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Margin="3,3,23,3"
VerticalAlignment="Stretch"
HorizontalAlignment="Left">
</ContentPresenter>
<TextBox x:Name="PART_EditableTextBox"
Style="{x:Null}"
Template="{StaticResource ComboBoxTextBox}"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Margin="3,3,23,3"
Focusable="True"
Background="Transparent"
Visibility="Hidden"
IsReadOnly="{TemplateBinding IsReadOnly}" />
It seems there are two visual components overlapping and IsEditable switches between them by setting the Visibility. This link verifies that: "When IsEditable is equal to false, the ComboBox uses a ContentPresenter to display the currently selected item; when IsEditable is equal to true, a TextBox is used for this purpose instead. Note that a TextBox only displays plain text, and that a ComboBoxItem may include non-plain text content, such as images." My question is, in the TextBox, where does it define the text? The Template (ComboBoxTextBox) doesn't assign the Text. I was asking because I am trying to make the TextBox to display the same info (if that's ever possible) as the ContentPresenter (i.e., as when IsEditable is False), whose template I understand is binding to the Template I assign.

here is a bare basic template for textbox for you with just the text editing part
so setting IsReadOnly="True" will make it like a ContentPresenter otherwise it is editable like a textbox
<TextBox Text="edit me">
<TextBox.Template>
<ControlTemplate TargetType="TextBox">
<ScrollViewer x:Name="PART_ContentHost" />
</ControlTemplate>
</TextBox.Template>
</TextBox>
you can customize it for your needs

Related

How `TextControlPlaceholderForeground` resource doesn't appy to TextBox

I got started learning Universal Windows Apps. For my demo application, I am trying to change the placeholder foreground of a TextBox control. To set its color, I set a resource key called TextControlPlaceholderForeground (as per this documentation) on the same XAML page definition that contains the control. However, the placeholder color of TextBox is not set. It looks as if no placeholder text is set when the control is not in a focused state. When it receives focus, the placeholder becomes visible, but still not the color that is set to it. Here is my XAML:
<Page
x:Class="MyApp.AuthenticationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources>
<SolidColorBrush x:Key="TextControlPlaceholderForeground" Color="Green" />
</Page.Resources>
<Grid>
<Border
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,10"
Background="White"
Padding="10"
CornerRadius="10"
Width="300"
>
<StackPanel Orientation="Vertical">
<!-- The placeholder of the TextBox below doesn't become green -->
<TextBox x:Name="emailInput" PlaceholderText="Email" Margin="0,0,0,10" />
<!-- However, the placeholder of this PasswordBox becomes green -->
<PasswordBox x:Name="passwordInput" PlaceholderText="Password" Margin="0,0,0,10" />
<Button HorizontalAlignment="Stretch" Content="LOG IN" Click="onLoginSubmit" Style="{StaticResource AccentButtonStyle}"/>
<HyperlinkButton FontSize="11" Foreground="Black" Content="Forgot your password?" HorizontalAlignment="Center" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="Don't have an account yet?" Foreground="Black" FontSize="11" Padding="0,0,5,0"/>
<HyperlinkButton Padding="0" FontWeight="Normal" >
<TextBlock Text="Create new account" Foreground="Black" TextDecorations="Underline" FontSize="11"/>
</HyperlinkButton>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Page>
An observation: When I try to set TextBox's placeholder text color via Style tags, it works.
<Style x:Key="TextBoxStyle" TargetType="TextBox">
<Setter Property="PlaceholderForeground" Value="Green" />
</Style>
I didn't understand what happens here. I know I am missing something very small but couldn't see it.
First, let me explain why the second approach works. There is a little bit of difference between the PasswordBox and the TextBox about how they defined the Placeholder Text color. Both of the PasswordBox and the TextBox has a PlaceholderTextContentPresenter element in their styles which is a TextBlock. But they have different value for the Foreground property when using this TextBlock.
TextBox:
<TextBlock x:Name="PlaceholderTextContentPresenter" Foreground="{Binding PlaceholderForeground, RelativeSource={RelativeSource Mode=TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForeground}}"
PasswordBox
<TextBlock x:Name="PlaceholderTextContentPresenter" Foreground="{ThemeResource TextControlPlaceholderForeground}"
You could see that the PasswordBox is directly using the TextControlPlaceholderForeground that you defined as the color but the TextBox is using a binding and the binding source is the PlaceholderForeground property. So when you set this property, the TextBox will show the color as you want.
So back to the first question, if you want to change the foreground of the Placeholder Text, you will need to create a default style of the TextBox and change the binding of the Foreground of the PlaceholderTextContentPresenter element to use the TextControlPlaceholderForegrounddirectly like what the **PasswordBox ** did.

How to change dependency property behavior in UserControl

I have a WPF user control which contains a TextBox and some buttons. Now I want to change the way in which the VerticalContentAlignment property of the user control is handled in order to change only the vertical content alignment of the contained text box and not of the user control itself. So how can I override the VerticalContentAlignment dependency property of the user control in order to achieve my desired behavior?
You could write a ControlTemplate for the UserControl that simply ignores the VerticalContentAlignment property. Then bind the TextBox's VerticalAlignment to the VerticalContentAlignment property of the UserControl, e.g. by a RelativeSource Binding.
<UserControl ...>
<UserControl.Template>
<ControlTemplate TargetType="UserControl">
<ContentPresenter
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</UserControl.Template>
<Grid Background="AliceBlue">
<TextBlock
Text="Hello"
VerticalAlignment="{Binding VerticalContentAlignment,
RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</UserControl>
Test case:
<Grid>
<local:MyUserControl
Height="100"
VerticalAlignment="Center"
VerticalContentAlignment="Bottom"/>
</Grid>

WPF RowValidation column of DataGrid placement

Is it possible to place RowValidation column not at the left side? For example, at the right side or somewhere else?
How it looks:
How I want it to looks:
While I can't confirm this 100%, I don't think that your requirements are possible. There is a DataGrid.RowValidationErrorTemplate property which enables you to define a custom template to display when validation errors occur, but it does not enable you to specify the placement of it. According to the DataGrid.RowValidationErrorTemplate Property page on MSDN:
The following example replaces the default row validation feedback with a more visible indicator. When a user enters an invalid value, a red circle with a white exclamation mark appears in the row header.
<DataGrid.RowValidationErrorTemplate>
<ControlTemplate>
<Grid Margin="0,-2,0,-2"
ToolTip="{Binding RelativeSource={RelativeSource
FindAncestor, AncestorType={x:Type DataGridRow}},
Path=(Validation.Errors)[0].ErrorContent}">
<Ellipse StrokeThickness="0" Fill="Red"
Width="{TemplateBinding FontSize}"
Height="{TemplateBinding FontSize}" />
<TextBlock Text="!" FontSize="{TemplateBinding FontSize}"
FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center" />
</Grid>
</ControlTemplate>
</DataGrid.RowValidationErrorTemplate>

Wpf combobox with checkboxes in it

I am trying to implement a combobox with checkboxes in it. All the articles/resources I found on Google/SO suggest adding a bool to my business object. But I am looking to create a reusable control.
So I created a custom control inherited from combobox and changed the control in the popup with a itemscontrol.
Here is my XAML (for brevity adding just the xaml for popup)
<Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
<Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}">
<Border x:Name="DropDownBorder" Background="{StaticResource BackgroundBrush}" BorderThickness="1" BorderBrush="{StaticResource BorderBrush}" />
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<ItemsControl ItemsSource="{Binding ItemsSource,RelativeSource={RelativeSource AncestorType=local:CheckedComboBox}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding}" x:Name="PART_Checkbox" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Popup>
As expected it shows a combobox with checkboxes. But I am not able to figure out how to keep track of the checked items?
I was thinking of listening to checked events but when I tried getting the Checkbox in my code-behind, FindName was returning null.
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.Template != null)
{
var v = Template.FindName("PART_Checkbox",this);
Debug.Assert(v != null);
}
}
Thanks.
Inherit from ListBox
Bind the CheckBox to ListBoxItem.IsSelected in the template of the items (set it in default style via ItemContainerStyle).
Set SelectionMode to Multiple.
SelectedItems then contains your selection. You may also want to bind your selection area to something like a comma-separated list of the SelectedItems (can be done via a converter for example).

WPF Custom "Toolbox" Selector control

I am trying to create a custom control, which behaves a bit like one of the "rows" in the toolbox in Expression Blend.
When closed, it displays the first of its items, and when the user holds the mouse down over it for a second or so, it "expands" to reveal the other items in a popup excluding the item which is currently selected (the item which was clicked on still remains visible, but is not grouped with the others).
I have managed to make a control which inherits from ContentControl displays one item and then reveals the others when expanded, but the item which is displayed first does not change when one is clicked.
The template is as follows:
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter Content="{TemplateBinding MainItem}" /> // The item that is seen even when not expanded
<Popup Name="Popup" Placement="Right" IsOpen="{TemplateBinding IsExpanded}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
<Border Name="SubmenuBorder" SnapsToDevicePixels="True" BorderThickness="0" >
<StackPanel Orientation="Horizontal" IsItemsHost="True" /> // The items only seen when expanded
</Border>
</Popup>
</Grid>
At the moment, I have to manually set the MainItem in XAML and it is not a member of the Items property of the ContentControl.
Is there a way to achieve this functionality where all items are part of the Items collection and are automatically not shown when an IsSelected property or something is set, and are then shown where the current MainItem is shown when this is the case?
I tried changing it to a Selector and using a filter on the Items to achieve this and binding the first ContentPresenter to the SelectedItem but it didn't seem to work.
If possible, the order of the items only visible when the control is expanded should be the same as the order in which they are laid out in XAML, with the currently selected item missing.
Thanks
try using the ItemContainerStyle to set the visibility of the selected item to 'Hidden'. You'd need a BoolToVisibilityConverter (you can write one easily or get one from the WPFToolkit) to get the correct value
<ContentPresenter Content="{Binding ElementName=selector, Path=SelectedItem}" />
<ComboBox x:Name="selector">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Visibility" Value="{Binding Path=IsSelected, Converter={StaticResource boolToVisibilityConverter}}" />
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SomeProperty}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>ComboBoxItem
I managed to solve the problem by using a VisualBrush to create a preview of the item without having to move it within the visual tree.

Categories