I've created style for button in my WPF app:
<Style x:Key="buttonStyle" TargetType="Button">
<Setter Property="Border.BorderBrush" Value="Black" />
<Setter Property="Border.BorderThickness" Value="1" />
<Setter Property="Border.Background" Value="White" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="100" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then I'm putting this style to Button control:
<Button Style="{StaticResource buttonStyle}" Grid.Row="1" Grid.Column="1" Content="Press"/>
When I'm running my app, I see grey shadow under and right of the button. Futhermore when I look in Visual Studio view I don't see shadow. How can I delete this effect? Anyone an idea or an completly other approach?
Are you sure what you're seeing is actually a shadow? It could simply be a blurred edge. WPF's layout system is based on device-independent pixels, so it's possible for an element's edge to reside between two device pixels, in which case it may appear blurred.
You can force device-pixel snapping by setting UseLayoutRounding="True" on a parent element in WPF 4.0 and later; in earlier versions, you can try SnapsToDevicePixels="True".
Related
I try to create a TextBox with a little triangle at the top right corner. I don't really know how to achieve it. I tried to add a polygon to the textbox at this way:
<TextBox
x:Name="PartnerEmail"
TextWrapping="Wrap"
MaxLength="50"
Grid.Column="1"
Grid.Row="12"
Margin="5,1,0,1" >
<TextBox.Style>
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Polygon Points="0,0 10,0 0,10 0,0" Margin="0,2,2,0" HorizontalAlignment="Right" Fill="#fcba03" FlowDirection="RightToLeft"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
But obviously it's not gonna work because I override the default template, so after this, all I have is a polygon and the textbox disappears. Can anyone have a good solution for this?
You are on the right track, but are working a bit too hard...
The thing to keep in mind about UI elements in WPF is that they work in layers. Think of elements as a composite of smaller items that you want to stack in a 3-D space. You aren't just working in a 2 dimensional drawing space anymore like a GDI rendering space of WinForms.
So lets logically think about what you want to do here. Lets try to achieve several goals in our solution:
Lets make a control template that will allow us to reuse our newly minted control.
Lets make the control completely portable in 3 dimensional space i.e. no use of raster images that will distort if we resize our control - lets stick to geometry only for drawing.
Lets make it easy to decide how large to make the triangle in the corner.
Lets decide to allow easy modifications for later, such as triangle placement (you want to change its location), hit testing (should it be part of the text surface or the border?), color (again, border or text?), etc.
Ok, to solve this, I alluded to the idea of objects in layers. Lets divide our drawing surface into a grid:
That means we want to use a Grid control. We want to define two rows and two columns, and we want to TextBox to cover all 4 cells. We will make sure to set the Rowspan and Columnspan to fill the grid.
We will then add a 2nd item, our Polyline triangle to row 1 column 1, and add it after the text box. That way it's Z-order will be on top.
Here then is the control template:
<Window.Resources>
<Style x:Key="TriangleTextBox" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="6*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" Margin="0,0,0,0" Text="{TemplateBinding Text}" BorderThickness="1"/>
<Polygon Grid.Row="0" Grid.Column="1" Points="0,0 10,0 0,10 0,0" Fill="Black" FlowDirection="RightToLeft"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Note that we set the key to "TriangleTextBox".
Now its just a simple matter of using our control template as follows:
<Grid>
<TextBox Style="{StaticResource ResourceKey=TriangleTextBox}" Width="100" Height="20"/>
</Grid>
And there you have it:
You can expand on this from there.
You are essentially replace the entire textbox by nothing but a triangle. You should copy the base template, and modify it from there:
<SolidColorBrush x:Key="TextBox.Static.Border" Color="#FFABAdB3"/>
<SolidColorBrush x:Key="TextBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="TextBox.Focus.Border" Color="#FF569DE5"/>
<Style x:Key="TextBoxStyle1" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
<Setter Property="BorderBrush" Value="{StaticResource TextBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="KeyboardNavigation.TabNavigation" Value="None"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<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">
<!--<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>-->
<Grid>
<Polygon Points="0,0 10,0 0,10 0,0" Margin="0,2,2,0" HorizontalAlignment="Right" Fill="#fcba03" FlowDirection="RightToLeft"/>
<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="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsInactiveSelectionHighlightEnabled" Value="true"/>
<Condition Property="IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="SelectionBrush" Value="{DynamicResource {x:Static SystemColors.InactiveSelectionHighlightBrushKey}}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
As for the template itself, you get it from microsoft, or via visual studio.
Create a user (composite) control which would have the polygon (drawn last) to overlay a textbox in the corner as needed. Usage of a Canvas control for specific placement or within a Grid for general placement in the control.
Here is where I had to put a search icon over a text field in a grid. I set the ZIndex as needed.
Code
<TextBox Grid.ColumnSpan="2"
Panel.ZIndex="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Image Grid.Column="1" Source="../Assets/Search.png"
Panel.ZIndex="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Stretch="Uniform"/>
I'm customizing a ListBoxItem, and I'm struggling with a couple of issues.
The ItemTemplate is a StackPanel of vertical orientation, whose height is set to 120px. But only clicking the label or the image causes the ListBox to select the item, not the open area below the label. How do I get the open area to also cause a change in selection? I'm considering adding a styled Button as ListBoxItem, but I wonder if it can be done any easier.
There's a 1px line between the ListBox container and the ListBoxItem's that I'm not able to get rid of. How do I get rid of it? Here's a screenshot:
XAML:
<ListBox Grid.Row="1" ItemsSource="{Binding MenuButtonInfoList}"
SelectedIndex="{Binding SelectedMainMenuIndex, Mode=TwoWay}">
<ListBox.Resources>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Width" Value="160px" />
<Setter Property="BorderBrush" Value="{StaticResource PrimaryColor}" />
<Setter Property="BorderThickness" Value="0, 1, 0, 0" />
</Style>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="MenuButtonBorder" SnapsToDevicePixels="True"
BorderBrush="{StaticResource PrimaryColor}" BorderThickness="0, 0, 0, 1">
<StackPanel Orientation="Vertical" Height="120px">
<Image Source="{Binding ImageFilePath}" Height="30" Width="30" />
<Label x:Name="MenuButtonLabel" Content="{Binding Label}"
FontSize="{StaticResource Title1FontSize}"
Foreground="{StaticResource PrimaryColor}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="MenuButtonBorder" Property="Background" Value="{StaticResource PrimaryColor}" />
<Setter TargetName="MenuButtonLabel" Property="Foreground" Value="{StaticResource HighlightColor}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.Resources>
</ListBox>
How do I get the open area to also cause a change in selection?
Set Background="Transparent" on the StackPanel in your item template. That will make the entire panel hit testable.
There's a 1px line between the ListBox container and the ListBoxItem's that I'm not able to get rid of. How do I get rid of it?
Override the ListBox template to remove the 1px padding:
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<!-- Padding="1" (removed) -->
<ScrollViewer Padding="{TemplateBinding Padding}"
Focusable="false">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true" />
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false" />
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Place the above in a Setter for the Template property in your ListBox style.
I want to override all my button's style to a borderless grayish button which highlights when hover on.
I wrote like below:
If I remove the template section (I even have no idea of what does it do), the button will have a border even if I have set BorderThickness to 0.
But if I keep the template section, the button will not change its background color at all.
So what can I do to keep both features and why my xaml won't work?
BTW, where can I find a full list of properties/triggers that I can set for certain type of control like button?
<Style TargetType="{x:Type Button}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Background" Value="{StaticResource TitleBrush}" />
<Setter Property="Foreground" Value="{StaticResource WhiteTextBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource HoverBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
Why BorderThickness value not reflected to control?
https://stackoverflow.com/a/16649319/440030
Why your xaml won't work?
Because, you set Template property of button with a simple content presenter, So Button ignore all control property and reflect your template. One way is improve your Template property with a by example label:
<ControlTemplate TargetType="{x:Type Button}">
<Label Content="{TemplateBinding Content}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="Black"/>
</ControlTemplate>
Try something like this it will work
<Grid>
<Grid.Resources>
<ControlTemplate x:Key="buttonTemplate" TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="Rect" Width="100" Height="100" Fill="Aqua"/>
<Viewbox>
<ContentControl Margin="20" Content="{TemplateBinding Content}"/>
</Viewbox>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Rect" Property="Fill" Value="Orange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Grid.Resources>
<Button Template="{StaticResource buttonTemplate}">OK</Button>
</Grid>
I was wondering if anyone has ever run into this situation. Basically what I'm trying to do is override the default listviewitem to customized the the selected background/foreground. I got that working all fine and dandy. Problem is, I noticed that on listviews where I have implemented gridviews the columns are broken. I'm not sure what's going on to break this. My approach to override the default style is used blend to get the full style by editing a copy of template. Modified it as needed. Applied it. This is pretty much what it looks like. Any thoughts?
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<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 ListViewItem}">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Bd" Property="BorderBrush" Value="{DynamicResource CustomBorderBrush}" />
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource CustomBackgroundBrush}" />
<Setter Property="Foreground" Value="{DynamicResource CustomForegroundBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true" />
<Condition Property="Selector.IsSelectionActive" Value="false" />
</MultiTrigger.Conditions>
<Setter TargetName="Bd" Property="Background" 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>
<ListView Grid.Row="0" Grid.Column="0" Margin="15,15,0,0" Name="lstResources" SelectionChanged="lstResources_SelectionChanged">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn x:Name="column1" Header="column1" Width="100" CellTemplate="{StaticResource column1template}"/>
<GridViewColumn x:Name="column2" Header="column2" Width="100" CellTemplate="{StaticResource column2template}" />
<GridViewColumn x:Name="column3" Header="column3" Width="200" CellTemplate="{StaticResource column3template}" WPFUtility:GridViewColumnResize.Width="*"/>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<DataTemplate x:Key="column1template">
<DockPanel>
<TextBlock HorizontalAlignment="stretch" TextTrimming="CharacterEllipsis" >
<TextBlock.Text>
<Binding Path="mycontent"/>
</TextBlock.Text>
</TextBlock>
</DockPanel>
</DataTemplate>
I inspected the control templates for the ListViews in both cases, and came to the conclusion that the styling wasn't working for GridViews because they require a GridViewRowPresenter to correctly layout the row data, rather than the ContentPresenter.
Of course, if you do that, you'll find your normal ListViews which don't use GridViews no longer format correctly, because they require a ContentPresenter.
I wasn't entirely sure of the neatest way around that, but stumbled across this blog post: http://www.steelyeyedview.com/2010/03/contentpresenter-gridviewrowpresenter.html
The gist of which I'll repeat here, in case it gets deleted:
His solution is a neat little hack, and seems to work. It makes use of both presenters, with the ContentPresenter hidden by default (Visibility="Collapsed"), and uses a trigger to make the ContentPresenter visible if the GridViewRowPresenter has no content. Since the GridViewRowPresenter has no content, it won't show anything anyway.
Adapting your Style to include his fix, you'd have something like this (Some code removed for focus):
<Style TargetType="{x:Type ListViewItem}">
<!-- Your Code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border x:Name="Bd"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<Grid>
<GridViewRowPresenter x:Name="gridrowPresenter"
Content="{TemplateBinding Property=ContentControl.Content}" />
<ContentPresenter x:Name="contentPresenter"
Content="{TemplateBinding Property=ContentControl.Content}" Visibility="Collapsed" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="GridView.ColumnCollection" Value="{x:Null}">
<Setter TargetName="contentPresenter" Property="Visibility" Value="Visible"/>
</Trigger>
<!-- Your Code -->
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Look at this!
I really need to get rid of this!
Look at the delcaration!
<ListView Name="list" BorderThickness="0">
How do I fix it?
This gap doesn't only happen to the scrollbar, it also happens to the items, with or WITHOUT any views.
Looks like a problem in the Template, most likely a property of an internal control which is not bound to any of the properties the ListBox exposes to you.
Edit by Vercas: I have found the problem.
This is the template of the ListView:
<ControlTemplate x:Key="ListView" TargetType="ListBox">
<Border Background="{TemplateBinding Control.Background}" BorderBrush="{TemplateBinding Control.BorderBrush}" BorderThickness="{TemplateBinding Control.BorderThickness}" Name="Bd" Padding="1" SnapsToDevicePixels="True">
<ScrollViewer Focusable="False" Padding="{TemplateBinding Control.Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="Border.Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
</Trigger>
<Trigger Property="ItemsControl.IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Just change the Padding of the Border to 0 and you're done.
Here is the result if you don't want to bother finding the property.
<ControlTemplate x:Key="ListView" TargetType="ListBox">
<Border Background="{TemplateBinding Control.Background}" BorderBrush="{TemplateBinding Control.BorderBrush}" BorderThickness="{TemplateBinding Control.BorderThickness}" Name="Bd" Padding="0" SnapsToDevicePixels="True">
<ScrollViewer Focusable="False" Padding="{TemplateBinding Control.Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="Border.Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" />
</Trigger>
<Trigger Property="ItemsControl.IsGrouping" Value="True">
<Setter Property="ScrollViewer.CanContentScroll" Value="False" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Don't forget to add this template to your ListView!
This would be easier to fix in Blend if you have it. if not there is a free trial. You will have to generate a copy of the control template for the scrollviewer that you are using in that list. From there you can edit the template for the ScrollBar.
The scroll bars template is going to be several levels of templates deep once you get to it. ListView Template>ItemsPresenter (In this case a Wrap Panel) Template> ScrollViewer Template > ScrollBar Template.