How do you get ListBoxItem.IsMouseCaptured to trigger style changes? - wpf

I'm using the following style:
<Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<TextBlock Padding="10"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}">
<ContentPresenter/>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Disabled}"/>
</Trigger>
<Trigger Property="IsMouseCaptured" Value="True">
<Setter Property="Background" Value="{DynamicResource Button-Background-Hover}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsMouseCaptured" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Button-Background-Hover}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Hover}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
<Condition Property="IsMouseOver" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Button-Foreground}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Hover}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
The issue I'm having is that the IsMouseCaptured trigger never occurs. It keeps the IsMouseOver style even when I clicked it (the background is the same color intentionally, but I've tried changing just to experiment and it doesn't change at all and the foreground certainly doesn't change).
Is there a way to have triggers occur for a specific list box item when it is clicked? All the other triggers are working as expected.

Is there a way to have triggers occur for a specific list box item when it is clicked?
Not using any built-in property.
You could create your own custom ListBoxItem that adds an IsPressed property that you can bind to:
public class CustomListBoxItem : ListBoxItem
{
public static readonly DependencyProperty IsPressedProperty = DependencyProperty.Register(
nameof(IsPressed), typeof(bool), typeof(CustomListBoxItem), new PropertyMetadata(false));
public bool IsPressed
{
get => (bool)GetValue(IsPressedProperty);
set => SetValue(IsPressedProperty, value);
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
IsPressed = true;
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
IsPressed = false;
}
}
Unless you create the ListBoxItem elements explicitly, you will also need a custom ListBox that generates containers of your custom type:
public class CustomListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride() =>
new CustomListBoxItem();
}

Related

WPF add custom attribute for control style in ResourceDictionary

I am making a skin for WPF and store all the styles in ResourceDictionary. I wrote a button template which has many various colors, eg: PrimaryHover, PrimaryActive etc. If I want to add a new style with other colors, I'll need to copy and paste this code and replace colors in whole code. So as not to do this, I want to create custom properties which will contain colors and point them in the triggers. I created a class with properties but it's invisible inside the ResourceDictionary. How to do that?
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Background" Value="{StaticResource Primary}" />
<Setter Property="Foreground" Value="{StaticResource White}" />
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorder}"/>
<Setter Property="BorderThickness" Value="0,1,0,0"/>
<Setter Property="Padding" Value="5,0,5,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid SnapsToDevicePixels="True">
<Border x:Name="BkgBorder" CornerRadius="3" BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Opacity="1">
</Border>
<ContentPresenter Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="{StaticResource PrimaryFocused}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderFocused}" TargetName="BkgBorder"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsEnabled" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource PrimaryHover}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderHover}" TargetName="BkgBorder"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsPressed" Value="True"/>
<Condition Property="IsEnabled" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource PrimaryActive}" TargetName="BkgBorder"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryBorderActive}" TargetName="BkgBorder"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{StaticResource White}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and class
class Buttons
{
public static SolidColorBrush GetHoverColor(DependencyObject obj)
{
return (SolidColorBrush)obj.GetValue(HoverColorProperty);
}
public static void SetHoverColor(DependencyObject obj, SolidColorBrush value)
{
obj.SetValue(HoverColorProperty, value);
}
public static readonly DependencyProperty HoverColorProperty =
DependencyProperty.RegisterAttached("HoverColor", typeof(SolidColorBrush), typeof(MainWindow), new PropertyMetadata(new SolidColorBrush(Colors.Green)));
}
I was able to copy your class into a test project exactly as it is and use it just fine.
Are you maybe neglecting to set up the XAML Namespace? My working example looks like this:
<Window x:Class="CSharpTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CSharpTest">
<Button local:Buttons.HoverColor="Yellow"/>
</Window>
As a seperate note, keep in mind that with the way your proeprty is currently declared, it can be applied to any kind of DependencyObject. If you want to limit so it is only valid on Buttons, then you should do this instead:
public static SolidColorBrush GetHoverColor(Button obj)
{
return (SolidColorBrush)obj.GetValue(HoverColorProperty);
}
public static void SetHoverColor(Button obj, SolidColorBrush value)
{
obj.SetValue(HoverColorProperty, value);
}

WPF Combobox should only open on arrow

Is there a chance to change the wpf combobox that it only opens when I click on the arrow on the left side?
Usually you can click anywhere to open it. I dont want that.
Thanks
Usually you can click anywhere to open it. I dont want that.
Then you should create a custom template for the ToggleButton. Yan right-click on the a ComboBox element in design mode in Visual Studio or in Blend and choose Edit Template->Edit a copy.
This will copy the default template into your XAML markup and you can then edit it as per your requirements. Look for a Style with an x:Key of "ComboBoxToggleButton" and modify the ControlTemplate of this one.
I managed to get it working with the following code. Note that I am using Materialdesign and you have to change it a little if you are not using that.
Code in App.xaml
<Style x:Key="MaterialDesignComboBox2" TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignTextBoxBorder}"/>
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}, Path=(TextElement.Foreground)}"/>
<Setter Property="BorderThickness" Value="0 0 0 1"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignComboBoxItemSelectedCollapsedStyle}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
<Setter Property="Padding" Value="0 6 0 6" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true" />
<Setter Property="ScrollViewer.PanningMode" Value="Both" />
<Setter Property="Stylus.IsFlicksEnabled" Value="False" />
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource MaterialDesignValidationErrorTemplate}"/>
<Setter Property="wpf:TextFieldAssist.TextBoxViewMargin" Value="1 0 1 0" />
<Setter Property="Template" Value="{StaticResource MaterialDesignFloatingHintComboBoxTemplate2}" />
<Style.Triggers>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false" />
<!-- designer prefers hard bool -->
<Setter Property="wpf:ComboBoxAssist.ShowSelectedItem" Value="{StaticResource TrueValue}" />
</Trigger>
<!-- designer prefers hard bool -->
<Trigger Property="wpf:ComboBoxAssist.ShowSelectedItem" Value="{StaticResource TrueValue}" >
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignComboBoxItemStyle}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MaterialDesignFloatingHintComboBox2"
BasedOn="{StaticResource MaterialDesignComboBox2}"
TargetType="{x:Type ComboBox}">
<Setter Property="wpf:HintAssist.IsFloating" Value="True"/>
</Style>
Helperclass EditableComboBox
public static class EditableComboBox
{
public static int GetMaxLength(DependencyObject obj)
{
return (int)obj.GetValue(MaxLengthProperty);
}
public static void SetMaxLength(DependencyObject obj, int value)
{
obj.SetValue(MaxLengthProperty, value);
}
public static readonly DependencyProperty MaxLengthProperty = DependencyProperty.RegisterAttached("MaxLength", typeof(int), typeof(EditableComboBox), new UIPropertyMetadata(OnMaxLenghtChanged));
private static void OnMaxLenghtChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if (!(obj is ComboBox comboBox)) return;
comboBox.Loaded +=
(s, e) =>
{
var textBox = comboBox.FindChild("PART_EditableTextBox", typeof(TextBox));
if (textBox == null) return;
textBox.SetValue(TextBox.MaxLengthProperty, args.NewValue);
};
}
}
And the combobox itself in the View
<ComboBox materialDesign:HintAssist.Hint="{lex:Loc ordernumber}" Style="{StaticResource MaterialDesignFloatingHintComboBox2}" Text="{Binding SalesOrderNumber}" Margin="0 0 40 0" FontSize="22" Width="250"
IsEditable="True" DisplayMemberPath="Identifier" SelectedItem="{Binding SelectedSalesOrder}"
ItemsSource="{Binding LastSalesOrders}" IsTextSearchEnabled="False" utility:EditableComboBox.MaxLength="10" x:Name="TbOrder" >
<ComboBox.InputBindings>
<KeyBinding Command="{Binding OpenSalesOrderOrCustomerCommand}" Key="Return" />
</ComboBox.InputBindings>
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="{lex:Loc paste}" Command="{Binding PasteOrdernumberCommand}" />
</ContextMenu>
</ComboBox.ContextMenu>
</ComboBox>

Rectangle IsFocused doesn't work

I have a slider and I want its thumb to change style depending on its state: mouse over or clicked.
I did try changing the thumb's style, but for no apparent reason it did nothing by the way.
So this rectangle, The IsMouseOver works just fine, but as I said, the IsFocused does nothing.
My XAML:
<Style x:Key="SliderRectStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="#FF5B5B5B"/>
<Setter Property="Stroke" Value="#FF5B5B5B"/>
<Setter Property="Opacity" Value="1"/>
<Setter Property="mouseHelper:MouseDownHelper.IsEnabled" Value="True"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Fill" Value="#FFF0A300"/>
<Setter Property="Stroke" Value="#FFF0A300"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="#FFD1A139"/>
<Setter Property="Stroke" Value="#FFD1A139"/>
</Trigger>
</Style.Triggers>
</Style>
You need to set the IsFocusable property of the Rectangle to true and actually focus it by calling its Focus() method to focus it:
<Rectangle x:Name="rect" Style="{StaticResource SliderRectStyle}" Width="100" Height="100" Focusable="True"
PreviewMouseLeftButtonDown="rect_PreviewMouseLeftButtonDown" />
private void rect_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
rect.Focus();
}

Mahapps TextBox style missing after chnage Backgroud color via code behind

So this is my TextBox style:
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource MetroTextBox}">
<Setter Property="Foreground" Value="Gainsboro"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Controls:TextBoxHelper.ClearTextButton" Value="True"/>
<Setter Property="Padding" Value="0,1,0,0" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Controls:TextBoxHelper.ClearTextButton" Value="True"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="False" />
<Condition Property="IsMouseOver" Value="False"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Background" Value="#FF0052B2"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="Gainsboro"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="True" />
<Condition Property="IsMouseOver" Value="False"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="Gainsboro"/>
<Setter Property="Background" Value="#FF103766"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsEnabled" Value="True" />
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#FF7AA0CD"/>
<Setter Property="BorderBrush" Value="White"/>
</MultiTrigger.Setters>
</MultiTrigger>
</Style.Triggers>
</Style>
My controller:
<TextBox
Name="Filter"
Width="398"
Height="25"
TextChanged="tbSnifferFilter_TextChanged"
Margin="23,0,0,0"/>
In my application the user need to type inside my TextBox filter (simple string), and via the following method i am checking if this filter is valid or not and change my TextBox Backgroud color to Green or Red:
private void tbFilter_TextChanged(object sender, TextChangedEventArgs e)
{
tbFilter.TextChanged -= tbFilter_TextChanged;
if (tbFilter.Text == "") // In case user click on clear button - return back to default backgroud color.
{
tbFilter.Background = (Brush)new BrushConverter().ConvertFromString("#FF103766");
tbFilter.TextChanged += tbFilter_TextChanged;
return;
}
if (!IsSyntaxCurrect(tbFilter.Text))
tbFilter.Background = Brushes.Salmon;
else
tbFilter.Background = Brushes.MediumSeaGreen;
tbFilter.TextChanged += tbFilter_TextChanged;
}
private bool IsSyntaxCurrect(string filter)
{
try
{
// Do my stuff...
return true;
}
catch (Exception)
{
return false;
}
}
So after add filter (valid ot not valid) and after that user click on TextBox clear button to clean the TextBox the Background color return to the default color but Mouse Over not working and do nothing.
This is because the local Background value that you set in the event handler takes precedence over the value set by your Style. Please refer to MSDN for more information about dependency property value precedence: https://msdn.microsoft.com/en-us/library/ms743230(v=vs.110).aspx
Instead of setting the Background property to a new Brush value when the TextBox is cleared, you could clear the value of the dependency property using the ClearValue method:
private void tbFilter_TextChanged(object sender, TextChangedEventArgs e)
{
tbFilter.TextChanged -= tbFilter_TextChanged;
if (tbFilter.Text == "") // In case user click on clear button - return back to default backgroud color.
{
tbFilter.ClearValue(TextBox.BackgroundProperty); //<--
tbFilter.TextChanged += tbFilter_TextChanged;
return;
}
if (!IsSyntaxCurrect(tbFilter.Text))
tbFilter.Background = Brushes.Salmon;
else
tbFilter.Background = Brushes.MediumSeaGreen;
tbFilter.TextChanged += tbFilter_TextChanged;
}

Change ListBoxItem Style

I want do something like this.
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Style">
<Setter.Value>
<Border Style="{StaticResource BorderStyle}" Width="200" >
</Border>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BorderStyle" TargetType="{x:Type Border}">
<Setter Property="Background" Value="{StaticResource BackBrush}" />
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="0.5" />
<Setter Property="CornerRadius" Value="4" />
<Setter Property="Margin" Value="4" />
<Setter Property="Padding" Value="4" />
</Style>
But it gives next error
Cannot add content of type 'System.Windows.Controls.Border' to an object of type 'System.Object'.
and the code which use it
for (int i = 0; i < 10; i++)
{
ListBoxItem lbItem = new ListBoxItem();
lbItem.Content = "Item" + i;
lb1.Add(lbItem);
}
where "lb1" is my ListBox in xaml form
How can i give ListBoxItemStyle properly?
It looks like you're confused about the semantics of XAML. Until you get more used to XAML it might help to think about it as the C# equivalent. This is essentially what you're doing right now:
Style BorderStyle = new Style();
Border inlineStyle = new Border { Style = BorderStyle };
Style listBoxItemDefaultStyle = new Style();
listBoxItemDefaultStyle.Setters.Add(new Setter(StyleProperty, inlineStyle));
ListBoxItem item = new ListBoxItem { Style = listBoxItemDefaultStyle };
One issue is that you're setting a Style for the ListBoxItem from a Setter inside the Style for your ListBoxItem, which is of course going to cause some sort of problem with recursion. So removing that extra Style from the code we get:
Style BorderStyle = new Style();
Border inlineStyle = new Border { Style = BorderStyle };
ListBoxItem item = new ListBoxItem { Style = inlineStyle };
This is invalid because it's trying to set a Style property (of Type Style) to a Border object. This is essentially what's at the core of the error you're seeing.
What you really want in this case is to change the ListBoxItem ControlTemplate to incorporate your Border Style. This is the default Style modified to use your Border instead of the standard one using TemplateBindings to set its properties:
<Style TargetType="{x:Type ListBoxItem}">
<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 ListBoxItem}">
<Border x:Name="Bd" Style="{StaticResource BorderStyle}" Width="200">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</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>

Resources