Display Header on Textbox only if text is set in UWP XAML - c#

I'm trying to modify the look & feel of the textbox control in a UWP App to what our designers drawn in sketch. That includes to have a header only shown when no text is set in the textbox.
I assume that I have to modify the visibility of the HeaderContentPresenter and its DataTemplate:
<ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />
I wrote an IValueConvert to convert "empty string" to visibility, but I am stuck with the binding. I can't see any way to get the datacontext.
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="{StaticResource DarkGrey}" FontSize="10" FontFamily="TheSansB4SemiLight" Visibility="{Binding Converter={StaticResource EmptyStringToVisibilityConverter}, RelativeSource={RelativeSource Self}}"/>
</DataTemplate>
</Setter.Value>
</Setter>
How can I access the text written on the textbox within the DataTemplate?
Thanks
Update with Code
I want this
MainPage.xaml
Three Textboxes I applied the same style to.
<TextBox Header="Username" PlaceholderText="Username" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,143,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}" />
<TextBox Header="Surname" PlaceholderText="Surname" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,249,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>
<TextBox Header="Name" PlaceholderText="Name" Grid.Column="1" HorizontalAlignment="Left" Height="67" Margin="20,196,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="295" Style="{StaticResource InputField}"/>
Styles.xaml
Basic shareable styles within a ResourceDict, refrenced in App.xaml. When I name the textboxes "InputField", the mechanism starts working, but obviously I have name conflicts and strange behavior.
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
Foreground="{StaticResource DarkGrey}"
FontSize="10"
FontFamily="TheSansB4SemiLight"
Visibility="{Binding Text, Converter={StaticResource EmptyStringToVisibilityConverter}, ElementName=InputField}"
Text="{Binding Header, ElementName=InputField}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
Question
What element within the Template must be named to be accessible through binding? I assume it's one of the elements the textbox control is made of, but I can't find out how.
<Grid><Border x:Name="BorderElement" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="1" CornerRadius="5"/>
<ContentControl x:Name="PlaceholderTextContentPresenter" Grid.ColumnSpan="2" Content="{TemplateBinding PlaceholderText}" Foreground="{ThemeResource TextControlPlaceholderForeground}" IsHitTestVisible="False" IsTabStop="False" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1"/>
<Button x:Name="DeleteButton" AutomationProperties.AccessibilityView="Raw" BorderThickness="{TemplateBinding BorderThickness}" Grid.Column="2" FontSize="{TemplateBinding FontSize}" IsTabStop="False" MinWidth="34" Grid.Row="1" Style="{StaticResource DeleteButtonStyle}" VerticalAlignment="Stretch" Visibility="Collapsed"/>
<ContentPresenter x:Name="HeaderContentPresenter" Grid.ColumnSpan="2" ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Foreground="{ThemeResource TextControlHeaderForeground}" FontWeight="Normal" Margin="0,0,0,2" Grid.Row="0" x:DeferLoadStrategy="Lazy" BorderThickness="0" Visibility="Collapsed" />
<ScrollViewer x:Name="ContentElement" AutomationProperties.AccessibilityView="Raw" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" IsTabStop="False" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" Margin="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" Grid.Row="1" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" ZoomMode="Disabled" Grid.ColumnSpan="2"/>
</Grid>

There are 2 solutions here.
Hide/Show TextBox Header based on Converter and TextBox.Text Length
Hide/Show TextBox Header based on CustomControl and Lost/Gain Focus
1) Hide/Show TextBox Header based on Converter and TextBox.Text Length
You need to Name your TextBox inside DataTemplate so that binding can find the item and Process the value.
Below is a sample of Converter.
public class DataToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Visibility dataVisible = (value.ToString().Length == 0) ? Visibility.Visible : Visibility.Collapsed;
return dataVisible;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
And below is how I use it in a DataTemplate inside ListView.
<ListView ItemsSource="{Binding }">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<TextBox x:Name="textBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="100" TextWrapping="Wrap" >
<TextBox.Header>
<TextBlock Text="Header String" Visibility="{Binding Text, Converter={StaticResource DataToVisibilityConverter}, ElementName=textBox}"/>
</TextBox.Header>
</TextBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Final Result
Edit
After you updated your question things got more simpler.
2. Hide/Show TextBox Header based on CustomControl and Lost/Gain Focus
You need to create a custom control to handle events that are common across for your textblock.
So as per your image, When you get focus, you show Header and lose focus, you hide it since there is text in placeholder.
Below is a custom control that does exactly that.
public sealed class MyTextBox : TextBox
{
public MyTextBox()
{
this.DefaultStyleKey = typeof(TextBox);
this.GotFocus += MyTextBox_GotFocus;
this.LostFocus += MyTextBox_LostFocus;
}
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e)
{
this.Header = "";
}
private void MyTextBox_GotFocus(object sender, RoutedEventArgs e)
{
this.Header = this.PlaceholderText;
}
}
And the usage will be
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:MyTextBox PlaceholderText="User Name" x:Name="txtUserName" Grid.Row="0" Margin="10"/>
<local:MyTextBox PlaceholderText="Email" x:Name="txtEmail" Grid.Row="1" Margin="10"/>
<local:MyTextBox PlaceholderText="Password" x:Name="txtPassword" Grid.Row="2" Margin="10"/>
</Grid>

Related

Is there any way to show elements from a listview one by one and scroll through them?

Currently in my calendar section, i get events from google calendar, and the items are show like this.
what I want is show this elements, one by one and adjust the height so its fits the element content which you are seeing and then scroll through the list to see the others elements.
And everytime you scroll down the listview is going to readjust the height to fit the new element.
Something like this
Then you scroll
But i dont know who to do this, if i bind the height to the wrapper of the element in the datatemplate just ignore the bind. And you can scroll through the elements without select them, so i cant modify the template for selected items, or bind with the selected item.
Searching i have found some ways to modify the item height to the parent height, but that's not what i want, is the opposite.
my xaml
<ListView Background="Transparent"
x:Name="DatosEvento"
Margin="5"
MinWidth="{Binding ActualWidth, ElementName=Calendar}"
Height="250"
ItemsSource="{Binding Path=Eventos}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel Width="350" Orientation="Horizontal">
<StackPanel Margin="2">
<Border BorderThickness="1" Background="CornflowerBlue" BorderBrush="Black">
<TextBlock Text="Nombre Organizador" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Organizer.DisplayName}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Correo Organizador" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Organizer.Email}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Nombre Evento" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Summary}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Estado" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Status}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Fecha Inicio dd/mm/yyyy" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Start.DateTime}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Fecha actualización" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=Updated, ConverterCulture={x:Static SystemGlobalization:CultureInfo.DefaultThreadCurrentCulture}}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Fecha Fin dd/mm/yyyy" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=End.DateTime}"/>
</StackPanel>
<StackPanel Margin="2">
<Border BorderThickness="1" BorderBrush="Black">
<TextBlock Text="Enlace" Background="Gray"/>
</Border>
<TextBlock Text="{Binding Path=HtmlLink}" Foreground="Blue" TextDecorations="Underline" MouseDown="TextBlockHiperLink_MouseDown" Cursor="Hand"/>
</StackPanel>
<Line X1="10" X2="300" Margin="5" Stroke="Black" StrokeThickness="2"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Simple template:
<ListView SelectedIndex="0">
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentControl HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
ContentTemplate="{TemplateBinding ItemTemplate}"
Content="{TemplateBinding SelectedItem}" />
<ScrollBar Minimum="0"
Value="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=SelectedIndex,Mode=TwoWay}"
Maximum="{Binding RelativeSource={RelativeSource Mode=TemplatedParent},Path=Items.Count,Converter={StaticResource ResourceKey=ToRealCount}}"
Grid.Column="1"
ViewportSize="0.5" />
</Grid>
</ControlTemplate>
</ListView.Template>
<ListView.Items>
<Rectangle Fill="Violet"
Width="100"
Height="100" />
<Rectangle Fill="Aqua"
Width="100"
Height="100" />
<Rectangle Fill="Green"
Width="100"
Height="100" />
<Rectangle Fill="Red"
Width="100"
Height="100" />
</ListView.Items>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListView>
Converter :
public class ToRealCount : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is int count)
{
return count > 0 ? count - 1 : count;
}
return 0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
you might consider looking into this too flipview
I ended using a combobox instead of a list, because i dont want to use something hacky that could end doing weird things and flipview didnt work as intended so is actually the best.
As i have to wait anyway if someone gives a better solution would be very nice.

ListView with ScrollHeader and WrapPanel (ItemsPanel.ItemsPanelTemplate)

I have a ListView in my page. and i using of ScrollHeader with Mode="Fade"
And for my ItemsPanelTemplate i use of WrapPanel control.
So, my listview items with ScrollHeader content in the form of horizontally aligned together.
I want to be ScrollHeader content be top and stretch horizontally and listview items below ScrollHeader content and form be vertically
My code:
<ListView ScrollViewer.VerticalScrollMode="Auto" ScrollViewer.HorizontalScrollMode="Disabled">
<ListView.Header>
<controls:ScrollHeader VerticalAlignment="Top" Mode="Fade">
<StackPanel>
<Grid Margin="20,20,20,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Ellipse x:Name="imageProfile" extensions:Mouse.Cursor="Hand" Width="75" Opacity=".90">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding UserDetail.ProfilePicUrl}"/>
</Ellipse.Fill>
</Ellipse>
<Grid Margin="10,0,0,0" Grid.Column="1" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid VerticalAlignment="Top" Margin="5,0,5,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="panelPostCount" Opacity=".70" HorizontalAlignment="Center">
<TextBlock x:Name="txblblCountPost" Text="{Binding UserDetail.MediaCount, FallbackValue='0'}" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock x:Name="txblblPosts" Text="Posts" Margin="5,0,5,0" FontSize="13" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel x:Name="panelFollowers" Opacity=".70" Grid.Column="1" Orientation="{Binding Orientation, ElementName=panelPostCount, Mode=TwoWay}" HorizontalAlignment="Center" Tapped="panelFollowers_Tapped">
<TextBlock Text="{Binding UserDetail.FollowerCount, FallbackValue='0'}" FontSize="{Binding FontSize, ElementName=txblblCountPost, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Text="Followers" Margin="5,0,5,0" FontSize="{Binding FontSize, ElementName=txblblPosts, Mode=OneWay}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Opacity=".70" Grid.Column="2" Orientation="{Binding Orientation, ElementName=panelPostCount, Mode=TwoWay}" HorizontalAlignment="Center">
<TextBlock Text="{Binding UserDetail.FollowingCount, FallbackValue='0'}" FontSize="{Binding FontSize, ElementName=txblblCountPost, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Text="Following" Margin="5,0,5,0" FontSize="{Binding FontSize, ElementName=txblblPosts, Mode=OneWay}" FontWeight="Bold" VerticalAlignment="Center"/>
</StackPanel>
</Grid>
<Button x:Name="btnEditProfile" Margin="5,10,5,0" Grid.Row="1" extensions:Mouse.Cursor="Hand" HorizontalAlignment="Stretch" Content="Edit Profile" BorderThickness=".5" Opacity=".65" Style="{ThemeResource ButtonRevealStyle}" Click="btnEditProfile_Click"/>
</Grid>
</Grid>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="15,10,0,0">
<FontIcon Opacity=".80" Visibility="{Binding UserDetail.IsVerified, Converter={StaticResource StringNullOrEmptyToVisiblityConverter}, FallbackValue='Collapsed'}" Glyph="" VerticalAlignment="Center" FontSize="15"/>
<TextBlock Text="{Binding UserDetail.FullName}" Margin="5,0,0,0" Visibility="{Binding UserDetail.FullName, Converter={StaticResource StringNullOrEmptyToVisiblityConverter}, FallbackValue='Collapsed'}" Opacity=".65" FontWeight="Bold"/>
</StackPanel>
<TextBlock Text="{Binding UserDetail.Biography}" Visibility="{Binding UserDetail.Biography, Converter={StaticResource StringNullOrEmptyToVisiblityConverter}, FallbackValue='Collapsed'}" TextWrapping="Wrap" TextAlignment="Left" Opacity=".65" HorizontalAlignment="Left" Margin="15,5,0,0"/>
<HyperlinkButton Content="{Binding UserDetail.ExternalUrl}" Visibility="{Binding UserDetail.ExternalUrl, Converter={StaticResource StringNullOrEmptyToVisiblityConverter}, FallbackValue='Collapsed'}" NavigateUri="{Binding UserDetail.ExternalUrl}" Opacity=".75" HorizontalAlignment="Left" Margin="15,5,0,0"/>
<Grid Height="1" Background="White" Opacity=".10" Margin="10,15,10,0"/>
<Grid Margin="35,10,35,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<FontIcon x:Name="btnGridView" Grid.Column="0" extensions:Mouse.Cursor="Hand" Opacity=".80" Glyph="" Foreground="{ThemeResource SystemAccentColor}" Tapped="btnGridView_Tapped"/>
<FontIcon x:Name="btnSingleView" Grid.Column="1" extensions:Mouse.Cursor="Hand" Opacity=".80" Glyph="" Tapped="btnSingleView_Tapped"/>
<FontIcon Grid.Column="2" extensions:Mouse.Cursor="Hand" Opacity=".80" Glyph=""/>
</Grid>
</StackPanel>
</controls:ScrollHeader>
</ListView.Header>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanel />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<templates:InstaMediaTenplate />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0,15,0,0"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
I found two ways to accomplish this.
The first is to restyle the ListView to stack the header and items.
<ListView ScrollViewer.VerticalScrollMode="Auto" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.Template>
<ControlTemplate TargetType="ListView">
<Border Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<ScrollViewer x:Name="ScrollViewer" AutomationProperties.AccessibilityView="Raw" BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}" HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}" HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}" IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}" IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}" IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}" IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}" IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}" TabNavigation="{TemplateBinding TabNavigation}" VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}" VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}" ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}">
<StackPanel>
<ContentPresenter Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}"/>
<ItemsPresenter Padding="{TemplateBinding Padding}"/>
</StackPanel>
</ScrollViewer>
</Border>
</ControlTemplate>
</ListView.Template>
</ListView>
It's important to have the ItemsPresenter not bind the Header property to the header of the ListView.
The second it to change your layout to have the ScrollViewer and header outside of the ListView and using a FadeHeaderBehavior like such
<ScrollViewer ScrollViewer.VerticalScrollMode="Auto" ScrollViewer.HorizontalScrollMode="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<interactivity:Interaction.Behaviors>
<behaviors:FadeHeaderBehavior x:Name="FadeBehavior"/>
</interactivity:Interaction.Behaviors>
<StackPanel>
<StackPanel x:Name="Header" Loaded="Header_Loaded">
<!-- Header content here -->
</StackPanel>
<ListView>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
</StackPanel>
</ScrollViewer>
Due to a weird issue with binding the HeaderElement property you need to set it when the header element is loaded
private void Header_Loaded(object sender, RoutedEventArgs e)
{
FadeBehavior.HeaderElement = (UIElement)sender;
}

WPF Style with multiple contents

I have created a style that creates a Label as a circle with the text in the middle.
<Style x:Key="RoundedLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Grid Height="Auto" Width="Auto" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Ellipse x:Name="cp" Margin="0,0,0,0" Fill="{TemplateBinding Background}" Height="{TemplateBinding Width}" Width="{TemplateBinding Width}" Stroke="Black" StrokeThickness="2" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Content>
<Border Padding="10">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ContentPresenter.Content>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Its used in this way:
<Label Style="{StaticResource RoundedButtonStyle}" Content="{Binding CountValue}" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Red" BorderBrush="Red" BorderThickness="3" Height="100" Width="100" FontSize="20" FontWeight="Bold" />
This works fine.
However I want to add further information to this label by having two text fields in different locations.
The first one already exists and displays in the centre of the ellipse.
Id like to add one which displays underneath the Ellipse.
Id like to be able to implement it in pure xaml if it is possible and use it something like this where the binding to SecondLabelText shows under the Ellipse:
<Label Style="{StaticResource RoundedButtonStyle}" Content="{Binding CountValue}" SecondContent="{Binding SecondLabelText}" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Red" BorderBrush="Red" BorderThickness="3" Height="100" Width="100" FontSize="20" FontWeight="Bold" />
I can add the label into the style, but how do i set two separate contents?
Don't know if this is any use to you; went away and created this as a custom user control as a bit of a practice, You can create a new user control, setting 2 properties to receive the 2 contents:
using System.Windows;
using System.Windows.Controls;
namespace Custom_Control_Elipse_2_labels
{
/// <summary>
/// Interaction logic for EllipseWithTwoLabels.xaml
/// </summary>
public partial class EllipseWithTwoLabels : UserControl
{
public static readonly DependencyProperty Content1Property = DependencyProperty.Register("Content1", typeof(string), typeof(EllipseWithTwoLabels));
public static readonly DependencyProperty Content2Property = DependencyProperty.Register("Content2", typeof(string), typeof(EllipseWithTwoLabels));
public EllipseWithTwoLabels()
{
InitializeComponent();
DataContext = this;
}
public string Content1
{
get => (string) GetValue(Content1Property);
set => SetValue(Content1Property,value);
}
public string Content2
{
get => (string)GetValue(Content2Property);
set => SetValue(Content2Property, value);
}
}
}
The .xaml for the user control being
<Grid>
<Label Content="{Binding Content1}" Style="{StaticResource RoundedButtonStyle}" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Red" BorderBrush="Red" BorderThickness="3" Height="100" Width="100" FontSize="20" FontWeight="Bold" ></Label>
<Label Content="{Binding Content2}" Margin="0,150,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" ></Label>
</Grid>
You can then just import it into any view to use it with: (
xmlns:local="clr-namespace:Custom_Control_Elipse_2_labels"
And use with the xaml:
<local:EllipseWithTwoLabels Height="300" Width="300" Content1="Content #1" Content2="Content #2"/>
Is one way to get it done :)
It provides something like this:
You can use for example Tag property of label to store extra data
<Label Style="{StaticResource RoundedLabelStyle}" Content="Content" Tag="Content#2"/>
In template just bind to Tag property
<ContentPresenter Content="{TemplateBinding Tag}"/>
Better approach is to use AttachedProperty. This allow you declare as many extra content as you want, without creating new type of control
public class LabelExtension
{
public static readonly DependencyProperty SecondContentProperty = DependencyProperty.RegisterAttached(
"SecondContent", typeof(object), typeof(LabelExtension));
public static void SetSecondContent(UIElement element, object value)
{
element.SetValue(SecondContentProperty, value);
}
public static object GetSecondContent(UIElement element)
{
return (object)element.GetValue(SecondContentProperty);
}
}
Set it on control
<Label Style="{StaticResource RoundedLabelStyle}" Content="Content" local:LabelExtension.SecondContent="Content#2"/>
Use from within template
<ContentPresenter Content="{TemplateBinding local:LabelExtension.SecondContent}"/>
Complete label style example
<Style x:Key="RoundedLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Grid Height="Auto" Width="Auto" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Ellipse x:Name="cp" Margin="0,0,0,0" Fill="{TemplateBinding Background}" Height="{TemplateBinding Width}" Width="{TemplateBinding Width}" Stroke="Black" StrokeThickness="2" />
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Content>
<Border Padding="10">
<StackPanel>
<ContentPresenter Content="{TemplateBinding Content}"/>
<ContentPresenter Content="{TemplateBinding Tag}"/>
<ContentPresenter Content="{TemplateBinding local:LabelExtension.SecondContent}"/>
</StackPanel>
</Border>
</ContentPresenter.Content>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Cant set 3 toggle button text content which use same style

i have three toggle buttons which use a singe style resource on wpf, now i cant set their content, content is declared more than once error, each toggle button has a different content.
Each toggle button uses an image as background also.
<UserControl.Resources>
<Style x:Key="Chromeless" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border BorderThickness="0" Width="197" Height="60">
<ContentPresenter TextElement.FontFamily="{TemplateBinding TextElement.FontFamily}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Margin="0,0,0,0"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Image x:Key="page1Pressed" Source="/graph_engine;Component/cucaracha/LD3/button_1.png" Height="60" Width="197" />
<Image x:Key="page1" Source="/graph_engine;Component/cucaracha/LD3/button_1_pressed.png" Height="60" Width="197" />
<Image x:Key="page2Pressed" Source="/graph_engine;Component/cucaracha/LD3/button_2.png" Height="60" Width="197" />
<Image x:Key="page2" Source="/graph_engine;Component/cucaracha/LD3/button_2_pressed.png" Height="60" Width="197" />
<Image x:Key="page3Pressed" Source="/graph_engine;Component/cucaracha/LD3/button_3.png" Height="60" Width="197" />
<Image x:Key="page3" Source="/graph_engine;Component/cucaracha/LD3/button_3_pressed.png" Height="60" Width="197" />
</UserControl.Resources>
<Viewbox Stretch="Fill" StretchDirection="Both">
<Grid Height="60" Name="grid1" Width="591" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="197" />
<ColumnDefinition Width="197" />
<ColumnDefinition Width="197" />
</Grid.ColumnDefinitions>
<ToggleButton Style="{StaticResource Chromeless}" Name="page1" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Foreground="{x:Null}" Checked="page1_Checked" Unchecked="page1_Unchecked">
<DynamicResource ResourceKey="page1"/>
</ToggleButton>
<ToggleButton Grid.Column="1" Style="{StaticResource Chromeless}" Name="page2" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Foreground="{x:Null}" Unchecked="page2_Unchecked" Checked="page2_Checked">
<DynamicResource ResourceKey="page2"/>
</ToggleButton>
<ToggleButton Grid.Column="2" Style="{StaticResource Chromeless}" Name="page3" Background="Transparent" BorderBrush="Transparent" BorderThickness="0" Foreground="{x:Null}" Checked="page3_Checked" Unchecked="page3_Unchecked">
<DynamicResource ResourceKey="page3"/>
</ToggleButton>
</Grid>
</Viewbox>
Do you mean you can't change the image when you pressed down the toggle button? You only set a image to the content of toggle button. You can use Visual State Manager to change the background like this
If you just want to set the image and failed. Please check the image path.

DataGrid inplace editor like MemoEdit based on ComboBox

My goal is creating an inplace editor in WPF datagrid column to edit large texts.
My datasource is a DataTable that can contain data from different tables and fields, that's why i have no any defined types to bind to. In my example it has 1 column named "Test".
Now i wrote some XAML code to define my column:
<ControlTemplate x:Key="ExtendedTemplate">
<StackPanel>
<TextBox Text="{Binding Test}" Width="200" Height="100" AcceptsReturn="True" TextWrapping="Wrap"/>
</StackPanel>
</ControlTemplate>
<DataGrid x:Name="grid" ItemsSource="{Binding}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="TEST Column" Width="200">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsDropDownOpen="True">
<ComboBoxItem Template="{StaticResource ExtendedTemplate}"/>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Test}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Here is my test datasource:
Source = new DataTable("Test");
Source.Columns.Add("Test");
Source.Rows.Add("Item 1 - large amount of text ...");
Source.Rows.Add("Item 2");
Source.Rows.Add("Item 3");
grid.DataContext = Source;
This works fine, but the last thing i need to do is to decorate grid cell while it is in edit mode and i'm typing text in expanded combo:
It is important:
First - combobox isn't binded to any ItemsSource, but single ComboBoxItem exists for any cell and contains text from that cell.
Second - i can't define DataTemplate to SelectedItem because ComboBox.SelectionBoxItemTemplate Property is read only.
Does anybody know how can i replace datatemplate for SelectionBoxItem to something like this?
<DataTemplate>
<TextBlock Text="{Binding Test}"/>
</DataTemplate>
I tried to create custom style for combobox with command "Edit template - Edit a copy...". There is a lot of markup and i don't want to post it here. Here is a small part edited by me.
<ContentPresenter ContentTemplate="{StaticResource SimplestTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Content="{TemplateBinding SelectionBoxItem}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"/>
It doesn't work, because id don't know how to write my "SimplestTemplate" that should bind data to my "Test" field.
I achieved my goal by creating a style, derived from ComboBox, and overriding its ContentPresenter ContentTemplate. It works ok, but i stopped my work on it, because there are many additional features, that need to be implemented (such as resizing popup editor, automatic grid cell refreshing, assigning commands to hotkeys to work with editor with keyboard only etc.). At last i decide to use third-party component (this one).
As an answer to my question i post my final XAML markup:
Resources:
<Window.Resources>
<DataTemplate x:Key="SimplestTemplate">
<Grid>
<TextBlock Background="White" Foreground="Black" Text="{TemplateBinding Content}"/>
</Grid>
</DataTemplate>
<ControlTemplate x:Key="ExtendedTemplate">
<StackPanel>
<TextBox Text="{Binding Test}" MinHeight="100" Height="Auto" MinWidth="{TemplateBinding Width}" Width="{Binding MinWidth}" TextWrapping="Wrap"/>
</StackPanel>
</ControlTemplate>
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ContentPresenter ContentTemplate="{StaticResource SimplestTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
Content="{TemplateBinding help:ComboboxHelper.BindedText}"
ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Grid.ColumnSpan="2"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Grid column template:
<DataGridTemplateColumn Header="TEST Column" Width="200">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsDropDownOpen="True" Style="{StaticResource MyComboBoxStyle}" FocusVisualStyle="{x:Null}" help:ComboboxHelper.BindedText="{Binding Test}">
<ComboBoxItem Template="{StaticResource ExtendedTemplate}"/>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Test}" TextTrimming="CharacterEllipsis"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
To show clipped text in cell i created a simple attached property. It cuts all "\n" symbols in text to make it single-line.
public static class ComboboxHelper
{
public static readonly DependencyProperty BindedTextProperty = DependencyProperty.RegisterAttached("BindedText", typeof(string), typeof(ComboboxHelper));
public static string GetBindedText(DependencyObject obj)
{
string s = (string)obj.GetValue(BindedTextProperty);
s = s.Replace(Environment.NewLine, " ");
return s;
}
public static void SetBindedText(DependencyObject obj, string value)
{
obj.SetValue(BindedTextProperty, value);
}
}

Categories