WPF ControlTemplate TargetType match - wpf

I have a component that is derived from a ComboBox. In it, I created a dependency property "myDependencyProp" as the following code. However, in XAML, when I refer to this property, VS says the type is not available in ComboBox and don't run the application. When I set the ControlTemplate TargetType to my:myComboBox, it works correctly, however, VS displays a warning: "myComboBox ControlTemplate TargetType does not match templated type ComboBox". The application runs correctly, but I don't like this warning. How can I fix it? Thanks! (Obs: my: is just a ficticious alias to myNameSpaces).
C#:
namespace myNameSpace
{
public partial class myComboBox : ComboBox
{
public static readonly DependencyProperty myDependencyPropProperty =
DependencyProperty.Register("myDependencyProp", typeof(bool),
typeof(myComboBox), new UIPropertyMetadata(false));
public myComboBox()
{
InitializeComponent();
}
public bool IsFirstItemTip
{
get { return (bool)GetValue(myDependencyPropProperty); }
set { SetValue(myDependencyPropProperty, value); }
}
}
}
XAML:
<ComboBox.Resources>
<Style x:Key="myComboBox" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type my:myComboBox}">
<Grid>
<ToggleButton
...
</ToggleButton>
<ContentPresenter
...
</ContentPresenter>
<Popup
...
</Popup>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="SelectedIndex" Value="0"/>
<Condition Property="myDependencyProp" Value="True"/>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter ... />
</MultiTrigger.Setters>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.Resources>

change this line
<Style x:Key="myComboBox" TargetType="{x:Type ComboBox}"> to <Style x:Key="myComboBox" TargetType="{x:Type my:myComboBox}"> to get rid of the warning. The warning is telling you that your style will implicitly get applied to all ComboBoxes but the control template is intended for those types which are derived from myComboBox

Thanks for the answers, but I solved the problem by doing the following:
First, I returned all TargetTypes to {x: Type ComboBox}, then all warnings are gone.
After, instead of using the MultiTrigger as previously presented, I used MultiDataTrigger, as the code below:
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=SelectedIndex, RelativeSource={RelativeSource Self}}" Value="0"/>
<Condition Binding="{Binding Path=myDependencyProp, RelativeSource={RelativeSource Self}}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter .../>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
That is it!

Related

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

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();
}

WPF: disable ListViewItem base on model view property

So I have this ModelView property that changed when my application do my stuff:
private bool isPlay;
public bool IsPlay
{
get { return isPlay; }
set
{
isPlay = value;
NotifyPropertyChanged("IsPlay");
}
}
And when this IsPlay is True I want my ListViewItem IsEnabled = False so this is my Style my ListView using:
<Style TargetType="{x:Type ListView}">
<Setter Property="TextBlock.TextAlignment" Value="Center"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="SelectionMode" Value="Single"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="True"/>
<Setter Property="AlternationCount" Value="2"/>
<!-- ListViewItem -->
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Height" Value="32"/>
<Setter Property="BorderBrush" Value="transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<!--<Setter Property="IsSelected" Value="{Binding IsSelected}" />-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<GridViewRowPresenter Content="{TemplateBinding Content}"
Columns="{TemplateBinding GridView.ColumnCollection}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=IsPlay}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="Gray" Opacity="0.1"/>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>
The problem is that the ListViewItem IsEnabled not changed so I bind this IsPlay property into some TextBlock to see is its change its state properly (True to False or vice versa) and this working fine but my ListView Style not.
Update
Here the declaration of my View Model (only one instance):
<Window.DataContext>
<viewmodel:ViewModelBase/>
</Window.DataContext>
The ListViewItem.DataContext is bound to the data items of your list, so you can't directly access properties that exist at the level of the ListView.
You can use a relative binding in this case:
<Condition Binding="{Binding DataContext.IsPlay,RelativeSource={RelativeSource AncestorType=ListView,Mode=FindAncestor}}" Value="True"/>

MultiDataTrigger Not respecting all conditions

I have an "On" "Off" switch that I've created and I cannot get the MultiDataTrigger to respect all Conditions.
<UserControl.Resources>
<Style TargetType="Button" BasedOn="{StaticResource NoDevExpressStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Height="30" Width="81" CornerRadius="15" BorderThickness="1" Background="White">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<!-- On Not Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.On}" />
<Condition Binding="{Binding SwitchClicked, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}" Value="False"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OnInactiveColorBrush}"></Setter>
</MultiDataTrigger>
<!-- On Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.On}" />
<Condition Binding="{Binding SwitchClicked, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OnColorBrush}"></Setter>
</MultiDataTrigger>
<!-- OFF Not Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.Off}" />
<Condition Binding="{Binding Path=SwitchClicked}" Value="False"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OffInactiveColorBrush}"></Setter>
</MultiDataTrigger>
<!-- Off and selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.Off}" />
<Condition Binding="{Binding Path=SwitchClicked}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OffColorBrush}"></Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button x:Name="Button" Click="ButtonBase_OnClick" />
It seems to respecting the "State" Property which is a dependency property that is set once and never changed. However the SwitchClicked is not respecting which is a property inside the control.
private bool _switchClicked;
public bool SwitchClicked
{
get => _switchClicked;
set => _switchClicked = value;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (Button.Command != null)
{
if (Button.Command.CanExecute(Button.CommandParameter))
{
SwitchClicked = !SwitchClicked;
SetValue(IsActiveProperty, !IsActive);
Button.Command.Execute(null);
}
}
}
Can someone please tell me how to insure that all of my conditions are met?
I have tried binding with relitive source to self and user control, however the SwitchClicked property is in the codebehind and there is no ViewModel.
You have to implement INotifyPropertyChanged either in your view model (or I suppose your code-behind), otherwise setting your property will not notify
being changed and the binding in your trigger will not update.
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public bool SwitchClicked
{
get => _switchClicked;
set
{
if (_switchClicked != value)
{
_switchClicked = value;
OnPropertyChanged(nameof(SwitchClicked));
}
}
}

failed to create menuitem click method in resource directory

I'm struggling with this below problem. my style file in resource directory. but it can't apply click method to context menu item. it's showing below this error. please help me how can i achieve this.
Error : "'Failed to create a 'Click' from the text 'OnMenuItemClick'.' Line number '10' and line position '35'."
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:src="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ObjectDataProvider x:Key="date" ObjectType="{x:Type src:DateTime}"/>
<Style x:Key="ContextMenuStyle1" TargetType="{x:Type ContextMenu}">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
</Style>
<ContextMenu x:Key="ListViewContext" Style="{StaticResource ContextMenuStyle1}">
<MenuItem Header="Create" Click="OnMenuItemClick" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"></MenuItem>
</ContextMenu>
<ContextMenu x:Key="GridItemContext" Style="{StaticResource ContextMenuStyle1}">
<MenuItem Header="Modify" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
<MenuItem Header="Delete" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
<Style x:Key="ListViewGrid" TargetType="{x:Type ListView}">
<Setter Property="BorderBrush" Value="#FFDFDFE2"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="#faf2f2"/>
<Setter Property="ContextMenu" Value="{StaticResource ListViewContext}"/>
</Style>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource GridItemContext}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border CornerRadius="0" SnapsToDevicePixels="True" >
<Border Name="InnerBorder" CornerRadius="0" BorderThickness="0,0,0,1" BorderBrush="#FFDFDFE2">
<Grid Background="#FFEFEFEF" Name="Trg" Height="20">
<GridViewRowPresenter />
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Trg" Property="Background" Value="#FFDFDFE2" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Trg" Property="Background" Value="#FFDFDFE2" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When I tried the above code, the error I got was:
Error 1 'ResourceDictionary' root element requires a x:Class attribute
to support event handlers in the XAML file. Either remove the event
handler for the Click event, or add a x:Class attribute to the root
element. Line 10 Position 35
This is how I fixed it:
1) Added x:Class attribute to the ResourceDictionary:
x:Class="WpfApplication4.Dictionary1"
2) Added a C# class file to the project, Code below:
public partial class Dictionary1 : ResourceDictionary
{
public Dictionary1()
{
InitializeComponent();
}
void OnMenuItemClick(object sender, RoutedEventArgs e)
{
}
}
And then, the project built fine.

EventSetter doesn't fire for ListView if ItemContainerStyle is style

A relative n00b to WPF. I have a ListView thus:
<ListView>
<ListView.View>
<GridView>
...
</GridView>
</ListView.View>
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="ItemDoubleClick"/>
</Style>
</ListView.Resources>
</ListView>
And in my app.xaml I have the following styles:
<Style TargetType="{x:Type ListView}">
<Setter Property="ItemContainerStyle" Value="{DynamicResource ListViewItemStyle}"/>
</Style>
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Border x:Name="Border" Padding="4">
<GridViewRowPresenter x:Name="ItemText"
TextBlock.FontSize="14" TextBlock.Foreground="{x:Static SystemColors.ControlDarkDarkBrush}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsSelected" Value="False"/>
</MultiTrigger.Conditions>
<Setter TargetName="ItemText" Property="TextBlock.Foreground" Value="{x:Static SystemColors.WindowTextBrush}"/>
</MultiTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="{x:Static SystemColors.HighlightBrush}"/>
<Setter TargetName="ItemText" Property="TextBlock.Foreground" Value="{x:Static SystemColors.HighlightTextBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But once I'm setting the ItemContainerStyle, the double-click no longer fires. If I remove it, it fires but my ListViewItems are not styled.
What am I missing here?
Your local resource is overridden by the application resources style which changes the ListView's ItemContainerStyle property. I would suggest setting the style directly on the ListView.ItemContainerStyle and basing the style on the existing one:
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}"
BasedOn="{StaticResource {x:Type ListViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="ItemDoubleClick"/>
</Style>
</ListView.ItemContainerStyle>
(This assumes implicit styling, so either remove the key of the style in your application resources or reference it directly using that key in the BasedOn property)
To expand on H.B.'s answer, an element can either have an implicit Style or you can set it's Style property directly (what I call an explicit Style), but not both. As soon as you set the Style property on say a ListViewItem, it will no longer use any implicit Styles you have.
Since the ListView.ItemContainerStyle is just an easy way to set the ListViewItem.Style property, it has the same effect of short-circuiting the implicit Style you have defined.

Resources