Missing Closing, Maximize and Minimize icons when changing Font Family of TextBlock - c#

I am creating a WPF application which is using ModernWpf to style everything. I am also using those two parameter on my window:
ui:ThemeManager.RequestedTheme="Dark"
ui:WindowHelper.UseModernWindowStyle="True"
It works fine, the window bar is dark now. But as soon as I want to set the Font Family for every TextBlock like that
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="FontFamily" Value="{StaticResource DefaultFont}" />
</Style>
It keeps changing the FontFamily of the ModernWpf Window and the Font doesn't contain the required Icons. Do I have to set a Key and set every TextBlock Style to that key manually or is there another way? Or is it possible to set the Font Family of the window icons?
Thats what the _ [ ] X Buttons look like now:

It's probably a bad idea to define an explicit TextBlock style in App.xaml as it will affect all TextBlock elements in your app.
Anyway, you can still make the caption button appear as expected if you define a custom ControlTemplate for the TitleBarButton where you replace the ContentPresenter with a TextBlock:
<Style x:Key="CustomStyle" TargetType="ui:TitleBarButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ui:TitleBarButton">
<Grid
x:Name="RootGrid"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<TextBlock
x:Name="Content"
Text="{TemplateBinding Content}"
FontFamily="Segoe MDL2 Assets"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Margin="{TemplateBinding Padding}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsActive" Value="False">
<Setter Property="Background" TargetName="RootGrid" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InactiveBackground}" />
<Setter Property="TextElement.Foreground" TargetName="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=InactiveForeground}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="RootGrid" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=HoverBackground}" />
<Setter Property="TextElement.Foreground" TargetName="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=HoverForeground}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="RootGrid" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PressedBackground}" />
<Setter Property="TextElement.Foreground" TargetName="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=PressedForeground}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="RootGrid" Value="{DynamicResource SystemControlDisabledTransparentBrush}" />
<Setter Property="TextElement.Foreground" TargetName="Content" Value="{DynamicResource SystemControlDisabledBaseMediumLowBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You need to set the TitleBar.ButtonStyle attached property of all your windows to your custom style, just like you set the WindowHelper.UseModernWindowStyle:
<Window ... ui:TitleBar.ButtonStyle="{StaticResource CustomStyle}" />

Related

How to change the style of a button when pressed

I am trying to change the style of a button when I click on it while the app is running.
Like I have a button with a grey color, I want it to change to red when I click on it?
I tried to do it with the Click event, however the button still shows the default effect when I hover over the button.
Here is the WPF code I used.
<Style TargetType="Button" x:Key="btnExitDef">
<Setter Property="Background" Value="#FF1E1E1E"/>
<Setter Property="Foreground" Value="#FFD6D6D6"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}"
CornerRadius="0"
BorderThickness="0"
Padding="0"
BorderBrush="#000">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="#fff"/>
</Trigger>
</Style.Triggers>
</Style>
<Button Height="35" Width="45" Name="btnExit" Content="✕" BorderBrush="{x:Null}" Click="btnExit_Click" Style="{StaticResource btnExitDef}"/>
You just need to edit the button's style and add another Trigger for the IsPressed property.
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>

WPF: Move focus to previous control (SHIFT + Tab) when using ControlTemplate with Placeholder Textbox

I created a WPF TextBox with Placeholder text by creating a customized Style that overlays one TextBox (as a placholder text) on another TextBox in the ControlTemplate.
Here's the Style definition:
<Style x:Key="TextBoxFormBaseStyle"
BasedOn="{StaticResource {x:Type TextBox}}"
TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<TextBox x:Name="_textSource"
Panel.ZIndex="2"
Background="Transparent"
Text="{Binding Path=Text, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="_textTag"
Panel.ZIndex="1"
Focusable="False"
Text="{TemplateBinding Tag}">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, Source={x:Reference _textSource}}" Value="">
<Setter Property="Foreground" Value="{Binding Path=OpacityMask, Source={x:Reference _textSource}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Grid>
<!-- Move Focus to child control -->
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="_textSource" Property="FocusManager.FocusedElement" Value="{Binding ElementName=_textSource}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A Trigger on Property IsFocused allows focusing the the child TextBox control (named _textSource) when navigating through multiple TextBox's by Tab key.
Navigating to the previous element by pressing SHIFT + Tab is not working. How can I get SHIFT + Tab to set the focus to the previous element?
There are better ways to add a watermark if you want the TextBox to actuallty behave and get focused like a built-in TextBox. Try this style:
<Style x:Key="TextBoxFormBaseStyle" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid>
<TextBox x:Name="_textTag"
Focusable="False"
Text="{TemplateBinding Tag}"
BorderThickness="0">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Foreground" Value="Transparent" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Text, RelativeSource={RelativeSource AncestorType=TextBox}}" Value="">
<Setter Property="Foreground" Value="{Binding Path=OpacityMask, RelativeSource={RelativeSource AncestorType=TextBox}}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="#FF569DE5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Sample usage:
<TextBox OpacityMask="Red" Tag="Red Watermark" Text="text" Style="{StaticResource TextBoxFormBaseStyle}" />
<TextBox OpacityMask="Green" Tag="Green Watermark" Text="text" Style="{StaticResource TextBoxFormBaseStyle}" />

Access Element in ControlTemplate from Style

I want to Style an Border inside a ControlTemplate. But I don't know how to access it. My Style looks like this:
<Style x:Key="RedCell" TargetType="DataGridCell" BasedOn="{StaticResource MYDGCellStyle}">
<Setter Property="Foreground" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridCell">
<Grid>
<Grid Grid.ZIndex="86" x:Name="CellContenGrind" Background="{TemplateBinding Background}" />
<Border Grid.ZIndex="87" x:Name="ContentBorder" BorderBrush="White" Background="Crimson" CornerRadius="25">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<Grid Grid.ZIndex="88" x:Name="CellGridFocused"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" TargetName="CellGridFocused" Value="{DynamicResource Brush_DataGridCellFocused}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" TargetName="CellContenGrind" Value="{DynamicResource Brush_DataGridSelected}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ungelesen}" Value="0">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
I need the Background="{TemplateBinding Background}" on my CellContentGrind because of some other stuff, so I can't just move that to my Border.
If ungelesen = 0, I want the Background of my ContentBorder to be Green. How can I do that?
Why not move the Style to the Border itself?
<Border Grid.ZIndex="87"x:Name="ContentBorder" BorderBrush="White" CornerRadius="25">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Crimson" />
<Style.Triggers>
<DataTrigger Binding="{Binding ungelesen}" Value="0">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
Note that Background is set with Setter now to make overridable with DataTrigger.

Text Trimming on DataGrid Template Column

I have the following Column in my datagrid :
<DataGridTemplateColumn CanUserReorder="False" CanUserResize="True" Header="">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate />
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellStyle>
<Style TargetType="DataGridCell" BasedOn="{StaticResource DatagridCellHyperlinkStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}" VerticalAlignment="Center">
<TextBlock Width="Auto" Height="Auto" TextTrimming="CharacterEllipsis">
<Hyperlink>
<InlineUIContainer TextDecorations="{Binding Path=TextDecorations, RelativeSource={RelativeSource AncestorType=TextBlock}}" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=TextBlock}}">
<ContentPresenter Width="Auto" Height="Auto" Content="{Binding DataContext.Value, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</InlineUIContainer>
<Hyperlink.Style>
<Style TargetType="Hyperlink" BasedOn="{StaticResource HyperlinkStyle}">
<EventSetter Event="Hyperlink.Click" Handler="Click" />
</Style>
</Hyperlink.Style>
</Hyperlink>
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.CellStyle>
</DataGridTemplateColumn>
The hyperlink works perfectly (with my style also) but the text trimming doesn't work. How can I change my code to make it work ?
The 2 styles attached :
<Style x:Key="DatagridCellHyperlinkStyle" TargetType="{x:Type DataGridCell}">
<Setter Property="Padding" Value="5" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="FontSize" Value="14" />
<Setter Property="FontFamily" Value="Helvetica" />
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Foreground" Value="{StaticResource CouleurBouton}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource ResourceKey=CouleurBouton}"/>
<Setter Property="Foreground" Value="{StaticResource ResourceKey=CouleurFond}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{StaticResource ResourceKey=CouleurBouton}"/>
<Setter Property="Foreground" Value="{StaticResource ResourceKey=CouleurFond}" />
</Trigger>
<DataTrigger Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" Value="True">
<Setter Property="Background" Value="{StaticResource ResourceKey=CouleurBoutonHover}"/>
<Setter Property="Foreground" Value="{StaticResource ResourceKey=CouleurTexteBoutonHover}" />
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="HyperlinkStyle" TargetType="{x:Type Hyperlink}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{DynamicResource CouleurBoutonPressed}" />
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="{DynamicResource CouleurBouton}" />
<Setter Property="TextBlock.TextDecorations" Value="{x:Null}" />
</Style>
Thank you !
You have nothing that will restrict the TextBlock.Width, so the text in it will never wrap, or be trimmed. To fix this problem, you just need to set some kind of Width restriction on it... you could try something like this:
<ControlTemplate>
<Border Padding="{TemplateBinding Padding}" VerticalAlignment="Center">
<TextBlock MaxWidth="250" TextTrimming="CharacterEllipsis">
...
Well, for WPF engine to understand that the trimming is needed it should see that the control cannot be put into the space available. If the control can be resized(AutoSize) it will just increase its dimensions without any trimming.
From MSDN:
Gets or sets the text trimming behavior to employ when content
overflows the content area.
And I can't see anything in your template that suggests that the space limit will be encountered.
So try to set width limit, either on Column, or on the TextBlock. Or restrict the resize in some other way.
<TextBlock Width="Auto" Height="Auto"
MaxWidth="100"
MinWidth="30"
TextTrimming="CharacterEllipsis">

How can I change the property of an Element based on the property of an INotifyPropertyChanged object in an ObservableCollection?

I've got an ObservableCollection<User> full of User objects which implement INotifyPropertyChanged. The collection is set as the DataContext of my Window, which contains a ListBox (whose ItemsSource is also set to the same collection), a number of TextBoxes, and a save Button, standard CRUD setup.
I want to change the background of the save Button (and the background of the row in the ListBox which corresponds to the "current item") if one of the properties of the User objects changes. Should I be looking at styles and triggers?
I have the following Style applied to my save Button, and the User objects have a public bool IsDirty property.
<Style x:Key="PropertyChangedStyle" TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Source=???, Path=IsDirty}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
<Button ... Style="{StaticResource PropertyChangedStyle}">
I think I'm on the right track, but I don't understand how to point the binding to "the current item in an observable list which is set as the datacontext", where "current item" in this case is described by CollectionViewSource.GetDefaultView(ListOfUsers).CurrentItem (where ListOfUsers is my ObservableCollection<User>).
The DataContext of each of the items in your ListBox will be automatically bound to your User instances, so it is not necessary to set the source in your binding. You can bind directly from the style of your ListBoxItems to properties on your User instances.
You can achieve it like this:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ASD_Answer011.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Property1}"/>
<CheckBox IsChecked="{Binding Property2}"/>
</StackPanel>
</DataTemplate>
<Style x:Key="ListBoxItemStyle1" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsDirty}" Value="True">
<Setter Property="Background" Value="Red" />
</DataTrigger>
</Style.Triggers>
<Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
<Setter Property="Padding" Value="2,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource SampleDataSource}}">
<ListBox ItemTemplate="{DynamicResource ItemTemplate}" ItemsSource="{Binding Collection}" ItemContainerStyle="{DynamicResource ListBoxItemStyle1}"/>
</Grid>
</Window>
This is how it looks when the application is running:
WPF supports the idea of a "current item" in a collection, and will track the current item for you. You can write a binding path that refers to a collection's current item.
See the "Current Item Pointers" section on the Data Binding Overview page on MSDN.
I'm thinking that, if your ListBox's ItemsSource is bound to (for example) {Binding ListOfUsers}, then your button could use {Binding ListOfUsers/IsDirty}.
I haven't used this much, but I think you may have to set your ListBox's IsSynchronizedWithCurrentItem property to True to make this work.

Categories

Resources