custom control textbox and implicit style - c#

We are trying to make an impliced style for a custom textblock control that we made.
This custom control is base on a text block and adds a few DP and Logic.
When we use the style locally everything works fine. Also when we give the style a key it also works.
This is the custom control inheritince code:
public class HighlightTextBlock : TextBlock
and this is the style:
<Style TargetType="UI:HighlightTextBlock"
x:Name="LocalHighlightTextBlockStyle"
BasedOn="{StaticResource StyleHighlightTextBlockDefault}">
<Setter Property="HighlightedText"
Value="{Binding ElementName=txtSearchBox, Path=Text}"></Setter>
<Setter Property="Background" Value="Tomato"></Setter>
<!--<Setter Property="HighlightedText" Value="{Binding UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type UI:GenericWatchControl}}, Path=SearchTextBoxContent}" />-->
<!--<Style.Triggers>
<Trigger Property="Text" Value="{x:Static ProfilingServerShared:MissingDataValue.NotAvailableText}">
<Setter Property="Foreground" Value="LightGray" />
</Trigger>
</Style.Triggers>-->
</Style>
Thanks all

You should override metadata in HighlightTextBlock static constructor, as such:
public partial class HighlightTextBlock : TextBlock
{
static HighlightTextBlock()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HighlightTextBlock),
new FrameworkPropertyMetadata(typeof(HighlightTextBlock)));
}
}
If you do not do this, by default, the HighlightTextBlock will try to find implicit style for TextBlock, not HighglightTextBlock.

Related

How to bind Style property for LayoutAnchorableItem in XAML file with AvalonDock?

I've created an avalon dock interface in my project, I want to interact with a LayoutAnchorableItem property "Visibility" for example but how implement it into my XAML code ? I couldn't have two Style definitions into my DockingManager.LayoutItemContainerStyle branch...
The line I want to add:
<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}" />
My original XAML code:
<dock:DockingManager DataContext="{Binding DockManagerViewModel}" DocumentsSource="{Binding Documents}" AnchorablesSource="{Binding Anchorables}" >
<dock:DockingManager.Resources>
<!-- add views for specific ViewModels -->
<DataTemplate DataType="{x:Type vmdock:SampleDockWindowViewModel}">
<uscontrol:SampleDockWindowView />
</DataTemplate>
</dock:DockingManager.Resources>
<dock:DockingManager.LayoutItemContainerStyle>
<!--you can add additional bindings from the layoutitem to the DockWindowViewModel-->
<Style TargetType="{x:Type dockctrl:LayoutItem}">
<Setter Property="Title" Value="{Binding Model.Title}" />
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}" />
<Setter Property="CanClose" Value="{Binding Model.CanClose}" />
<Setter Property="IsSelected" Value="{Binding Model.IsSelected}" />
</Style>
</dock:DockingManager.LayoutItemContainerStyle>
Thanks a lot !
If you want to select between multiple styles there is a LayoutItemContainerStyleSelector property on the dockingmanager which uses a Style Selector.
With this style selector you can choose which style to apply depending if the object is a LayoutAnchorableItem or a different type of LayoutItem.
public class MyStyleSelector : StyleSelector
{
public Style DefaultStyle { get; set; }
public Style CustomStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is LayoutAnchorableItem)
{
return CustomStyle;
}
return DefaultStyle;
}
}
If you want to merge the single style setter to the other style you can use the BasedOn property. This works because LayoutAnchorableItem inherits LayoutItem. With this you can make a style based on another style so it inherits all the setters. The resources would look like:
<Style TargetType="{x:Type dockctrl:LayoutItem}" x:Key="DefaultStyle">
<Setter Property="Title" Value="{Binding Model.Title}" />
<Setter Property="CloseCommand" Value="{Binding Model.CloseCommand}" />
<Setter Property="CanClose" Value="{Binding Model.CanClose}" />
<Setter Property="IsSelected" Value="{Binding Model.IsSelected}" />
</Style>
<Style TargetType="{x:Type dockctrl:LayoutAnchorableItem}" BasedOn="{StaticResource DefaultStyle}" x:Key="CustomStyle">
<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}" />
</Style>
<local:MyStyleSelector DefaultStyle="{StaticResource DefaultStyle}" CustomStyle="{StaticResource CustomStyle}" x:Key="MyStyleSelector" />
Now you can fill in the docking manager with the new style selector.
<dock:DockingManager LayoutItemContainerStyleSelector="{StaticResource MyStyleSelector}" ...
You can leave out the style selector and remove the keys in the resources. Note that these styles will then be applied in all children which is not something you generally want.
Thanks for your answer!
I'm not sure it deals with my problem... what I want is defined a style for an other Target Type, like if I could write :
<Style TargetType="{x:Type dockctrl:LayoutItem}">
[...]
</Style>
<Style TargetType="{x:Type dockctrl:LayoutAnchorableItem}">
[...]
</Style>
But I couldn't write both style directly into my DockingManager.LayoutItemContainerStyle branch. It accepts only one Style definition... how to deals with that ? Thanks

datatrigger on enum to change image

I've got a button with a fixed background image and would like to show a small overlay image on top of it. Which overlay image to chose depends on a dependency property (LapCounterPingStatus) of the according viewmodel.
This is what I got so far:
<Button>
<Grid>
<Image Stretch="None"> <!-- Background Image -->
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="/Images/Pingn.png"/>
</Style>
</Image.Style>
</Image>
<Image Stretch="None" Panel.ZIndex="1"> <!-- Small Overlay Image -->
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_UNKNOWN">
<Setter Property="Source" Value="/Images/RefreshOverlayn.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_FAILURE">
<Setter Property="Source" Value="/Images/ErrorOverlayn.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="PingStatus.PING_SUCCESS">
<Setter Property="Source" Value="/Images/CheckmarkOverlayn.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Grid>
</Button>
Relevant parts of my viewmodel
public class ConfigurationViewModel
{
public enum PingStatus { PING_UNKNOWN, PING_SUCCESS, PING_FAILURE };
public PingStatus LapCounterPingStatus
{
get { return _lapCounterPingStatus; }
set
{
_lapCounterPingStatus = value;
RaisePropertyChanged(LapCounterPingStatusPropertyName);
}
}
}
Right now, no overlay image at all is displayed. What could be wrong?
UPDATE
Trace window of my IDE is showing System.ArgumentException and System.FormatException.
Could the problem source be a unknown type of enumeration PingStatus im the XAML?
You need 2 things to get this working:
1 - Add an xmlns reference in the root element of your XAML file, to the namespace where your Enum is defined:
<UserControl ...
xmlns:my="clr-namespace:YourEnumNamespace;assembly=YourAssembly">
2 - in the Value property of the DataTrigger, use the {x:Static} form:
<DataTrigger Binding="{Binding Path=LapCounterPingStatus}" Value="{x:Static my:PingStatus.PING_UNKNOWN}">
Notice that the Enum type must be prefixed with the xmlns prefix you defined above.
Edit:
If your Enum is declared inside a class you need to use the syntax:
{x:Static namespace:ClassName+EnumName.EnumValue}
for example:
{x:Static my:ConfigurationViewModel+PingStatus.PING_UNKNOWN}
Complete worked example for WPF + MVVM.
Tested on MSVC 2017.
In the view:
<TextBlock Text="Some text to be colored by an enum">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusIcon}" Value="{x:Static my:StatusIcon.Warning}">
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusIcon}" Value="{x:Static my:StatusIcon.Error}">
<Setter Property="Foreground" Value="Red}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
If using ReSharper, and if the DataContext is set up properly, there will be intellisense when you hit the . after StatusIcon, i.e. it will show the properties of the enum which are Debug, Info, Warning or Error.
If using ReSharper, it will suggest the following update to the namespace in the header for the XAML file(its
good like that):
xmlns:my="clr-namespace:Class.Path.MyViewModel;assembly=MyAssembly"
And the VieModel:
public enum StatusIcon
{
Debug,
Info,
Warning,
Error
}
public class MyViewModel
{
public StatusIcon StatusIcon { get; }
}
We also use Fody for automated binding.
You can simply set enum value as DataTrigger Value... Tested on MSVC 2017.
<TextBlock Text="Some text to be colored by an enum">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusIcon}" Value="Warning">
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger>
<DataTrigger Binding="{Binding StatusIcon}" Value="Error">
<Setter Property="Foreground" Value="Red}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

Why does this DataTrigger not work?

Yes, the datatrigger is inside a style. Now that that issue is behind us, I'm interested to know why the following code is nonfunctional.
I should see a blue background for the data grid but the style is ignored. What am I doing wrong? Note I've named the Window element "root".
<Window x:Class="DataGridTriggerTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="root">
<Grid>
<DataGrid ItemsSource="{Binding SomeData}" >
<DataGrid.Style>
<Style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=root, Path=SomeCondition}" Value="true">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="RowBackground" Value="Red"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=root, Path=SomeCondtion}" Value="false">
<Setter Property="Background" Value="Blue"></Setter>
<Setter Property="RowBackground" Value="Blue"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding}" Header="Data"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
And here is the code:
public partial class MainWindow : Window
{
public bool SomeCondition { get; set; }
public List<string> SomeData { get; set; }
public MainWindow()
{
InitializeComponent();
DataContext = this;
SomeData = new List<string> { "hello", "world" };
}
}
XAML Booleans are Case-Insensitive, however, I believe you need to use "False" and "True" when using it in the Value property.
You have a few issues. The first is that you need to either implement the INotifyPropertyChanged interface and raise the PropertyChanged event on the SomeCondition setter property, or make SomeCondition a DependencyProperty. Without doing that, your UI will never know that the property value has changed.
The second is that I believe that datatriggers won't occur if the value is the same as the default value. So, the false trigger will never occur because the boolean default is false. I think that it's expected that you will set the default style values to match the default value of the property.. false in this case... like this:
<Style TargetType="DataGrid">
<Setter Property="Background" Value="Blue" />
<Setter Property="RowBackground" Value="Blue" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=root, Path=SomeCondition}" Value="true">
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="RowBackground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
The default is blue when the property is false, and changes when the property is true.
Finally, you should use an ObservableCollection instead of a List for SomeData.

DynamicResource throws an exception

I have to apply the following style to my ListViewItem:
<UserControl.Resources>
<local:Look x:Key="ListViewItemLook" Background="Magenta"/>
<Style x:Key="ListViewItemStyle" TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{Binding Source={DynamicResource ListViewItemLook}, Path=Background}"/>
</Trigger>
</Style.Triggers>
</Style>
But i get an exception, i try to change:
<Setter Property="Background" Value="{Binding Path=Background}"/>
And add to the Style:
<Setter Property="DataContext" Value="{DynamicResource ListViewItemLook}"/>
But is does not work. I can't bind to a StaticResource because I need to set the BackGround property run-time.
What have I to do? Thanks.
If you want both local:Look and the setter to refer to the same color, perform a small refactor:
Pull the color out into a separate SolidColorBrush and make both items refer to it:
<SolidColorBrush x:Key="SelectedListViewItemBackground" Color="Magenta" />
<local:Look x:Key="whatever" Background="{StaticResource SelectedListViewItemBackground}" />
<Setter Property="Background" Value="{StaticResource SelectedListViewItemBackground}" />
If you're trying to do something else, I can't figure out what it is because the question doesn't make sense.
As far as I am aware, DynamicResource extension uses the DependencyProperty mechanism (pretty much like a binding). Therefore you cannot set Source property of a Binding object with DynamicResource because it is not a DependencyProperty.
In addition, if you want to change the Background property of Look but not the Look itself in resources; then setting Look as a static resource to the binding property should not be a problem. Of course Background property of Look class should either trigger a PropertyChanged event or be a DependencyProperty itself.

How to set different background according to one property?

I have ListView which ItemSource bindend to ObservableCollection<Period> where Period is
public class Period : INotifyPropertyChanged
{
//some stuff
//
public Status PeriodStatus
{
get;
set;
}
#region PropertyChangedEventHandler members
public void SendPropertyChanged(string name)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
public enum Status
{
None,
Added,
Deleted,
Edited
}
And i want set background of each ListViewItem in this order : added-green/deleted-red/edited-yellow/none-default . Found at here many solutions, but didn't provide my solution. If there is exist question , please comment and i will close this
[EDIT]
I wanted to use DataTemplate in this way: create template which create binding with Background property and Status in Period which uses converter. But didn't know how to keep rest of design
Please use DataTriggers for the Background property in the Style of ListViewItem (example in this question: you don't need the converter, use the enum values instead of integer values).
<ListView>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Added">
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Deleted">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Edited">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
<Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
The simplest one is to use Triggers in your ListView.ItemContainerStyle.
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Added">
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Deleted">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=PeriodStatus}" Value="Edited">
<Setter Property="Background" Value="Yellow" />
</DataTrigger>
<Style.Triggers>
<Style>
<ListView.ItemContainerStyle>
This is a sample code: you might need to add a namespace with your enum to use it in XAML.
I find it more easy to have a dedicated PeriodStatusColor readonly property, less 'pure xaml', yes, but less code, and all code in the same place. So this property just returns color for current PeriodStatus. On PeriodStatus change, raise also a PeriodStatusColor PropertyChanged. Use static frozen color, and maybe use a PeriodStatus --> Color static Dictionnary to have clean code.

Categories

Resources