I notice that the TextElement.Foreground is not working but the Background = Red and FontWeight = Bold is working fine. Any idea why Foreground is not applying the custom style?
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="ChkDayResource" Style="{DynamicResource CheckBoxBlueStyle}"
IsChecked="{Binding Path=IsSelected}"
Tag="{Binding Path=., RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}, AncestorLevel=1}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Click="ChkDayResource_Click" Content="{Binding Path=DayName}">
</CheckBox>
<!--<TextBlock Width="Auto" Text="{Binding Path=DayName}" IsHitTestVisible="True"/>-->
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="0"
Margin="-1,0,-1,0"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal" Margin="10,0,10,0">
<ContentPresenter x:Name="content" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="Red" />
<Setter Property="TextElement.Foreground" Value="Yellow" />
<Setter Property="TextElement.FontWeight" TargetName="content" Value="Bold" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
Is it due to the CheckBoxBlueStyle I applied to the CheckBox? If yes, how can I override the styling?
The default CheckBox Style is at fault. Inspecting it for example in Blend reveals:
<Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<!-- other property setters -->
</Style>
So you need to assign a new Foreground directly or via style if you don't want the default one.
Possible solution:
<CheckBox Name="ChkDayResource" Foreground="{Binding Path=(TextElement.Foreground),RelativeSource={RelativeSource AncestorType=StackPanel}}"
Related
So I have a style for my TextBox as shown
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="20">
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<StackPanel>
<!-- ScrollViewer to restore the default textbox template that is gone because of
us now overriding it -->
<Border CornerRadius="2" Padding="2" Background="White" BorderBrush="Black" BorderThickness="1">
<ScrollViewer Margin="0" x:Name="PART_ContentHost" Background="Gray" />
</Border>
<ItemsControl FontSize="10" ItemsSource="{TemplateBinding Validation.Errors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
When there is an error, I'd like the text box to show a red background, with the error messages appearing underneath it. I've set up a trigger to change the TextBox's background when there's an error, but it seems that since I override the TextBox's normal template so that I can show the error messages, the background of the textbox itself doesn't change (it stays gray). I realize that this is because I used a ScrollViewer to restore the textbox appearance, but now how can I somehow refer to this ScrollViewer from my trigger?
It's more for learning purposes.
You need to better use ControlTemplate.
Possible example:
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="20"/>
<Setter Property="Background" Value="Gray"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<StackPanel>
<!-- ScrollViewer to restore the default textbox template that is gone because of
us now overriding it -->
<Border x:Name="PART_Border" CornerRadius="2"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<ItemsControl FontSize="10" ItemsSource="{TemplateBinding Validation.Errors}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="PART_Border" Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
but now how can I somehow refer to this ScrollViewer from my trigger?
Move the trigger from the Style to the ControlTemplate and use the TargetName property of the Setter to refer to the ScrollViewer or the parent Border element inside the template, e.g.:
<ControlTemplate TargetType="TextBox">
<StackPanel>
<Border ...>
<ScrollViewer Margin="0" x:Name="PART_ContentHost" Background="Gray" />
</Border>
<ItemsControl ... />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter TargetName="PART_ContentHost" Property="Background" Value="Red"/>
...
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I have a button on my WPF application (MVVM Pattern) which is responsive to a click event. Basically if you click on this button its background becomes LightGreen (the default color is LightGray). I already achieved the desired behavior using the following code:
<Button Grid.Column="2" Grid.Row="6" Grid.ColumnSpan="3" Grid.RowSpan="2" Content="{Binding FirstSchedule.Message}" Command="{Binding FirstScheduleButtonClick}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="LightGray"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding FirstScheduleButtonSelected}" Value="True">
<Setter Property="Background" Value="LightGreen"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
In which FirstScheduleButtonSelected is a ViewModel property defined by:
private bool _firstScheduleButtonSelected;
public bool FirstScheduleButtonSelected
{
get { return _firstScheduleButtonSelected; }
set { _firstScheduleButtonSelected = value; NotifyPropertyChanged("FirstScheduleButtonSelected"); NotifyPropertyChanged("Background"); }
}
Now I need to make this button's borders rounded. I already tried this solution How to create/make rounded corner buttons in WPF?, and I ended up with:
<Button Grid.Column="2" Grid.Row="6" Grid.ColumnSpan="3" Grid.RowSpan="2" Content="{Binding FirstSchedule.Message}" Command="{Binding FirstScheduleButtonClick}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="15" Background="LightGray" BorderThickness="1" Padding="2">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="LightGray"></Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding FirstScheduleButtonSelected}" Value="True">
<Setter Property="Background" Value="LightGreen"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Now my borders are, in fact, rounded but when I click the button it does not becomes green.
Q: How can I modify this button in order to make its borders rounded and keep my already functioning behavior of changing its color on click?
Here's what I'd do:
<Window.Resources>
<!-- ... -->
<Style x:Key="GreenToggleButtonStyle" TargetType="ToggleButton">
<Setter Property="Background" Value="LightGray" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border
CornerRadius="15"
Background="{TemplateBinding Background}"
BorderThickness="1"
Padding="2"
>
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter
Property="Background"
Value="LightGreen"
/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ... -->
</Window.Resources>
...
<ToggleButton
Grid.Column="2"
Grid.Row="6"
Grid.ColumnSpan="3"
Grid.RowSpan="2"
Content="{Binding FirstSchedule.Message}"
IsChecked="{Binding FirstScheduleButtonSelected}"
Style="{StaticResource GreenToggleButtonStyle}"
/>
I've got a combobox and it's populated with a bunch of checkboxes. I wish the user to be able to click multiple times before the combobox closes (or is closed by the user themselves). The problems right now is that each time a checkbox is clicked, the combobox closes, forcing the user who wants to select multiple options to re-open it several times.
I've found other questions on the same subject but those apply to Silverlight, Qt etc. comparing the tags.
I've tried setting StayOpenOnEdit but that didn't do the trick. As far I could see, there's no property addressing my issue. That creates a suspicion that I might be barning up the wrong component all together.
How do I prevent the combobox to close automatically after a click in a checkbox in it?
Is there a more suitable component for such task and, if so, what's its name?
Please note that even if the answer to #2 is "yes", I'm still curious of #1 for purely academic reasons.
I've played with toggle button and list box, as someone suggested in a post. However, the that led only to an always fully shown list of all the checkboxes with some greyish thing behind it (which I'm assuming is the toggler). Perhaps I did something less clever in the mark-up.
<ToggleButton HorizontalAlignment="Left"
Margin="550,62,0,0"
VerticalAlignment="Top"
Width="100">
<ListBox x:Name="listBox1"
HorizontalAlignment="Left"
Height="100"
VerticalAlignment="Top" Width="100">
<CheckBox x:Name="checkBox3" Content="CheckBox"/>
<CheckBox x:Name="checkBox4" Content="CheckBox"/>
</ListBox>
</ToggleButton>
The point is to achieve something like this but it needs to be a standard WPF control (the concatenated line of all selected items is nice but not a must). Also, I've read the complaints that the binding and handling is not fully developed yet and I feel a bit suspicious.
Using Exapander Control you can achieve multple item selection without closing popup after single selection.
For understanding please run this code separately.
xaml
<Window.Resources>
<ControlTemplate x:Key="ComboboxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid Background="{Binding Background,RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="35" />
</Grid.ColumnDefinitions>
<Border x:Name="Border" Grid.ColumnSpan="2" Background="Transparent" BorderBrush="Black" BorderThickness="{Binding BorderThickness,RelativeSource={RelativeSource TemplatedParent}}"/>
<Path x:Name="Arrow" Grid.Column="1" Opacity="1" Stroke="Black" StrokeThickness="1.5" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 6 6 L 12 0" />
<Path x:Name="Arrow_checked" Opacity="0" Grid.Column="1" Fill="Black" Stroke="Black" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center" Data="M 0 0 L 6 6 L 12 0 Z" />
<ContentPresenter TextElement.FontFamily="Segoe Ui Dark" TextElement.FontSize="18" TextElement.Foreground="Black" VerticalAlignment="Center" Grid.Column="0" Margin="10,0,0,0" HorizontalAlignment="Left" RecognizesAccessKey="True" SnapsToDevicePixels="True" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="Gray" />
<Setter TargetName="Border" Property="BorderThickness" Value="1.2" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Opacity" Value="1" TargetName="Arrow"/>
<Setter Property="Opacity" Value="0" TargetName="Arrow_checked"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter Property="Opacity" Value="0" TargetName="Arrow"/>
<Setter Property="Opacity" Value="1" TargetName="Arrow_checked"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="Gray" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style TargetType="{x:Type Expander}">
<Setter Property="FontFamily" Value="Segoe Ui Dark"></Setter>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="MaxHeight" Value="200"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Expander}">
<DockPanel>
<ToggleButton x:Name="HeaderSite" Height="35" Background="{TemplateBinding Background}" ContentTemplate="{TemplateBinding HeaderTemplate}" ContentTemplateSelector="{TemplateBinding HeaderTemplateSelector}" Content="{TemplateBinding Header}" DockPanel.Dock="Top" Foreground="Black" FontStyle="{TemplateBinding FontStyle}" FontFamily="Segoe UI Dark"
IsChecked="{Binding IsExpanded, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Template="{StaticResource ComboboxToggleButton}" />
<Border BorderThickness="0,4.5,0,0" BorderBrush="Transparent">
<Border x:Name="bod" BorderBrush="Transparent" SnapsToDevicePixels="True" BorderThickness="1">
<ContentPresenter x:Name="ExpandSite" Focusable="false" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Visibility="Collapsed" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
</Border>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsExpanded" Value="true">
<Setter Property="Visibility" TargetName="ExpandSite" Value="Visible"/>
<Setter Property="BorderBrush" TargetName="bod" Value="Black"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="Gray"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="itemstyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid x:Name="Border" Height="40" SnapsToDevicePixels="true">
<Grid.Background>
<SolidColorBrush Color="Transparent" />
</Grid.Background>
<ContentPresenter Name="cmb_name" TextElement.FontFamily="Segoe Ui Dark" TextElement.FontSize="18" TextElement.Foreground="Black" Margin="10,0,0,0" VerticalAlignment="Center"></ContentPresenter>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="Background" Value="Gray"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{DynamicResource customBlueBrush}" BorderThickness="1" Margin="1,2,2,2" >
<Rectangle Fill="{DynamicResource customBlueBrush}" Opacity="0.1"></Rectangle>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="CheckBox" >
<Setter Property="SnapsToDevicePixels" Value="True"></Setter>
<Setter Property="OverridesDefaultStyle" Value="True"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid x:Name="ab" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle VerticalAlignment="Center" Height="20" Width="20" Fill="White" HorizontalAlignment="Left"></Rectangle>
<Grid x:Name="checkGrid" VerticalAlignment="Center" Height="20" Width="20" Background="Black" HorizontalAlignment="Left">
<Viewbox Height="13" Width="13">
<Path x:Name="Check" SnapsToDevicePixels="True" UseLayoutRounding="True" Width="18.7969" Height="16.3094" Canvas.Left="0" Canvas.Top="1.52588e-005" Stretch="Fill" Fill="White" Data="F1 M 0.731262,8.75935L 0.106262,8.08437C 0.0354614,7.9948 0,7.8979 0,7.79375C 0,7.66875 0.0479736,7.5573 0.143799,7.45937L 1.94067,5.77187C 2.02606,5.69893 2.12708,5.66249 2.24377,5.66249C 2.30212,5.66249 2.36096,5.67397 2.42035,5.69685C 2.47974,5.71977 2.52814,5.75417 2.56567,5.79997L 7.5188,11.1406L 16.0438,0.165604C 16.1417,0.055191 16.2584,1.52588e-005 16.3938,1.52588e-005C 16.4979,1.52588e-005 16.5896,0.0322723 16.6688,0.0968475L 18.6313,1.60939C 18.6709,1.64272 18.7084,1.69011 18.7438,1.75154C 18.7792,1.813 18.7969,1.8698 18.7969,1.92189C 18.7969,2.03435 18.7646,2.1385 18.7,2.23439L 7.74377,16.3094L 0.731262,8.75935 Z " />
</Viewbox>
</Grid>
<Grid Background="Transparent" Grid.Column="1" IsHitTestVisible="True" HorizontalAlignment="Stretch">
<TextBlock VerticalAlignment="Center" FontSize="18" FontFamily="Segoe Ui Dark" Foreground="Black" TextTrimming="CharacterEllipsis">
<ContentPresenter></ContentPresenter>
</TextBlock>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Opacity" Value="1" TargetName="Check"></Setter>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Opacity" Value="0" TargetName="Check"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FocusVisualStyle">
<Setter.Value>
<Style TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{DynamicResource customBlueBrush}" SnapsToDevicePixels="True" BorderThickness="1" Margin="-5,1,3,1" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="gd" >
<Expander Width="500">
<Expander.Header>
<ListBox Background="Transparent" IsHitTestVisible="False" BorderBrush="Transparent" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Disabled" BorderThickness="0" ItemsSource="{Binding ElementName=lst,Path=SelectedItems}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"></WrapPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ContentData}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander.Header>
<Expander.Content>
<ListBox Background="Transparent" ItemContainerStyle="{StaticResource itemstyle}" HorizontalContentAlignment="Stretch" x:Name="lst" SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox x:Name="checkBox" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" Content="{Binding ContentData}"></CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander.Content>
</Expander>
</Grid>
c# code
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
ObservableCollection<Customer> custdata = new ObservableCollection<Customer>();
custdata.Add(new Customer() { ContentData = "content1" });
custdata.Add(new Customer() { ContentData = "content2" });
custdata.Add(new Customer() { ContentData = "content3" });
custdata.Add(new Customer() { ContentData = "content4" });
custdata.Add(new Customer() { ContentData = "content5" });
custdata.Add(new Customer() { ContentData = "content6" });
lst.ItemsSource = custdata;
}
}
public class Customer
{
public string ContentData { get; set; }
}
Result
Konrad,
I also decided to use a ComboBox in this manner, because the code was just dead simple. The easiest way I have found to keep the ComboBox popup open is to wire-up to the PreviewMouseDown event of the control in the ComboBox's item template. handle the behavior yourself, and then mark the mouse event handled. Works great for me. In the sample below, each object in FilterItems is a simple view model with a Text property and an IsChecked property.
<ComboBox IsEditable="True" IsReadOnly="False" ItemsSource="{Binding FilterItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}"
PreviewMouseDown="FilterComboBox_PreviewMouseDown"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And then my event handler is:
private void FilterComboBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var cb = sender as CheckBox;
if (cb != null)
{
cb.IsChecked = !cb.IsChecked;
e.Handled = true;
}
}
I have a WPF button style below:
<Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{Binding Width}" Height="{Binding Height}">
<Border Name="container" Background="{Binding Background}" CornerRadius="{Binding CornerRadius}">
<TextBlock Margin="10" FontFamily="Arial" FontWeight="Bold" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" FontSize="{Binding TextSize}" Foreground="White" Text="{Binding Text}" TextWrapping="Wrap"/>
</Border>
<Border Name="overlay" Background="Transparent" CornerRadius="{Binding CornerRadius}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="overlay" Property="Opacity" Value="0.6" />
<Setter TargetName="overlay" Property="Background" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is that, although all of the buttons of my application have the same style, some of them need to have a border around it. Therefore I would like to know if it is possible to access the container border of my style to set its thickness and color? If so how can I do this?
Edit:
I've mixed the suggestions of madd0 and Josh and created a DataTrigger inside my style with a binding to a property that tells me if the button should or should not have a border.
The final code is below:
<Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Width="{Binding Width}" Height="{Binding Height}">
<Border Name="container" Background="{Binding Background}" CornerRadius="{Binding CornerRadius}">
<TextBlock Margin="10" FontFamily="Arial" FontWeight="Bold" TextAlignment="Left" HorizontalAlignment="Left" VerticalAlignment="Top" Width="Auto" FontSize="{Binding TextSize}" Foreground="White" Text="{Binding Text}" TextWrapping="Wrap"/>
</Border>
<Border Name="overlay" Background="Transparent" CornerRadius="{Binding CornerRadius}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=HasBorder}" Value="true">
<Setter TargetName="container" Property="BorderThickness" Value="{Binding BorderThickness}" />
<Setter TargetName="container" Property="BorderBrush" Value="{Binding BorderBrush}" />
</DataTrigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="overlay" Property="Opacity" Value="0.6" />
<Setter TargetName="overlay" Property="Background" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thank you all for your help,
Komyg
I think if you extend the Button class and give it a new boolean DependencyProperty, essentially all you need to do is give your Border a name, then in ControlTemplate.Triggers, Trigger off that boolean property to make the border as you need it in your special cases.
It would be similar to what you already have with the IsPressed ControlTemplate trigger.
I don't think you'll really need to access your control template properties. Since Button already has border properties, you ought to just set those properties on the button directly. Then, add the border to your ControlTemplate, binding its properties to that of the button.
This is my combo-box.
<ComboBox Height="45" HorizontalAlignment="Left" Margin="184,66,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="216">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding FullName}" Width="150" />
<Label Content="{Binding Title}" Width="100"/>
<Label Content="{Binding BranchName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
How can I change it so that only the FullName appears in the textbox portion of the combobox while all three columns still appear in the drop-down portion?
Unfortunately, the SelectionBoxItemTemplate is a readonly property, so we have to do a bit more work. By doing the ItemTemplate to be how you want the item to appear when selected, you can edit the ItemContainerStyle to provide a ControlTemplate that includes the other fields you want to display.
<ComboBox Height="45" HorizontalAlignment="Left" Margin="184,66,0,0" Name="ComboBox1" VerticalAlignment="Top" Width="216">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding FullName}" Width="150" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}">
<StackPanel Orientation="Horizontal">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
<Label Content="{Binding Title}" Width="100"/>
<Label Content="{Binding BranchName}" />
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" />
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
For the ComboBoxItem template, I just modified the default one, so it should be fully functional.
If the ComboBox's IsEditable property is set to True, you can set the "TextSearch.TextPath" property of the ComboBox to the property name you want to show. So in your case:
<ComboBox IsEditable="True" TextSearch.TextPath="FullName" .../>
Instead of using the read-only SelectionBoxItemTemplate property I created a new (attached, writable) property and used that one in my style. I also added a trigger to my style to not break all the comboboxes that are not using my new attached property...
Use it like this:
<ComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
<controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
<DataTemplate DataType="{x:Type ...}">
... Template for the selection box ...
</DataTemplate>
</controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type ...}">
... Template for the popup ...
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You just have to add this class to your project:
public class ComboBoxSelectionBoxAltTemplateBehaviour
{
public static readonly DependencyProperty SelectionBoxAltTemplateProperty = DependencyProperty.RegisterAttached(
"SelectionBoxAltTemplate", typeof (DataTemplate), typeof (ComboBoxSelectionBoxAltTemplateBehaviour), new PropertyMetadata(default(DataTemplate)));
public static void SetSelectionBoxAltTemplate(DependencyObject element, DataTemplate value)
{
element.SetValue(SelectionBoxAltTemplateProperty, value);
}
public static DataTemplate GetSelectionBoxAltTemplate(DependencyObject element)
{
return (DataTemplate) element.GetValue(SelectionBoxAltTemplateProperty);
}
}
and change your ComboBox style to use the SelectionBoxAltTemplate attached property if set (or because I could not set a trigger to "not null", I set it back to the default SelectionBoxItemTemplate if the attached one is null):
The ContentPresenter inside the ControlTemplate of the ComboBox Style:
<ContentPresenter Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" />
And the Trigger to provide backwards compatibility to ComboBoxed without the attached Property:
<ControlTemplate.Triggers>
<Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
</Trigger>
...
</ControlTemplate.Triggers>
Full Style:
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Background" Value="{StaticResource ComboBoxBackground}"/>
<Setter Property="BorderBrush" Value="{StaticResource ComboBoxBorder}"/>
<Setter Property="Margin" Value="6"/>
<Setter Property="Padding" Value="3,3,5,3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border Name="Border" Grid.ColumnSpan="2" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"/>
<ToggleButton Name="ToggleButton2" Focusable="False" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Grid.ColumnSpan="2" Background="Transparent"/>
<!-- Allows clicking anywhere on the combobox, not only the visible button on the right -->
<ToggleButton Focusable="false" Grid.Column="1" x:Name="ToggleButton" IsChecked="{Binding Path=IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" Style="{StaticResource ComboBoxToggleButton}"/>
<ContentPresenter HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="ContentSite" VerticalAlignment="Center" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" IsHitTestVisible="False" />
<TextBox Visibility="Hidden" HorizontalAlignment="Left" Margin="{TemplateBinding Control.Padding}" x:Name="PART_EditableTextBox" Style="{x:Null}" VerticalAlignment="Center" Focusable="True" Background="Transparent" />
<Popup IsOpen="{TemplateBinding IsDropDownOpen}" Placement="Bottom" x:Name="Popup" Focusable="False" AllowsTransparency="True" PopupAnimation="Slide">
<Grid MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{TemplateBinding ActualWidth}" x:Name="DropDown" SnapsToDevicePixels="True">
<Border x:Name="DropDownBorder" Background="{StaticResource ComboBoxBackground}" BorderBrush="{StaticResource ComboBoxBorder}" BorderThickness="1" Padding="0,4">
<ScrollViewer SnapsToDevicePixels="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" CanContentScroll="True" Style="{x:Null}" >
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Border>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="controls:ComboBoxSelectionBoxAltTemplateBehaviour.SelectionBoxAltTemplate" Value="{x:Null}">
<Setter Property="ContentTemplate" Value="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}" TargetName="ContentSite" />
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="MinHeight" Value="95" TargetName="DropDownBorder" />
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false" />
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Visibility" Value="Visible" TargetName="PART_EditableTextBox" />
<Setter Property="Visibility" Value="Hidden" TargetName="ContentSite" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true" SourceName="ToggleButton2">
<Setter Property="Background" Value="{StaticResource ComboBoxMouseOver}" />
</Trigger>
<Trigger Property="HasItems" Value="False">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However this might not work with ItemTemplateSelctors, only with one single template - but you could easily add an attached property "SelectionBoxAltTemplateSelector" which provides the selector and passes that one to the style.
There is a pretty good answer to your question here if you don't want to change the ComboBoxes style: https://stackoverflow.com/a/2277488/1070906
It uses a Trigger in the DataTemplate which looks if there is a ComboBoxItem somewhere above in the visual tree, which is not the case in the selection box.
You could override the ComboBox and change the SelectionBoxItemTemplate directly.
public class SelectionComboBox : ComboBox
{
#region Properties
#region Dependency Properties
public DataTemplate AltSelectionBoxItemTemplate
{
get { return (DataTemplate)GetValue(AltSelectionBoxItemTemplateProperty); }
set { SetValue(AltSelectionBoxItemTemplateProperty, value); }
}
public static readonly DependencyProperty AltSelectionBoxItemTemplateProperty =
DependencyProperty.Register("AltSelectionBoxItemTemplate", typeof(DataTemplate), typeof(SelectionComboBox), new UIPropertyMetadata(null, (s, e) =>
{
// For new changes...
if ((s is SelectionComboBox) && ((SelectionComboBox)s).Presenter != null && (e.NewValue is DataTemplate))
((SelectionComboBox)s).Presenter.ContentTemplate = (DataTemplate)e.NewValue;
// Set the new value
((SelectionComboBox)s).AltSelectionBoxItemTemplate = (DataTemplate)e.NewValue;
}));
#endregion
#region Internals
#region Elements
ContentPresenter Presenter { get; set; }
#endregion
#endregion
#endregion
#region Constructors
#endregion
#region Methods
#region Overrides
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Presenter = this.GetTemplateChild("contentPresenter") as ContentPresenter;
// Directly Set the selected item template
if (AltSelectionBoxItemTemplate != null) Presenter.ContentTemplate = AltSelectionBoxItemTemplate;
}
#endregion
#endregion
}
Once you define the control, you could style it.
<controls:SelectionComboBox ItemsSource="{Binding ...}" SelectedItem="{Binding ..., Mode=TwoWay}">
<controls:SelectionComboBox.AltSelectionBoxItemTemplate>
<DataTemplate>
<!-- My Template Goes Here... -->
</DataTemplate>
</controls:SelectionComboBox.AltSelectionBoxItemTemplate>
</controls:SelectionComboBox>