WPF RibbonTab - cannot bind to RibbonTab Headerstyle - c#

I try to customize the RibbonTabHeader with different colors.
However I'm unable to bind dynamic values to the object RibbonTabHeader. One can customize its style by assigning a style to HeaderStyle. But the get of the binded method BackgroundColor is never called.
Xaml:
<Style TargetType="{x:Type ribbon:RibbonTab}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="ItemsSource" Value="{Binding GroupDataCollection}" />
<Setter Property="Visibility" Value="{Binding IsVisible}" />
<Setter Property="IsEnabled" Value="{Binding IsEnabled}"/>
<Setter Property="HeaderStyle">
<Setter.Value>
<Style TargetType="{x:Type ribbon:RibbonTabHeader}">
<Setter Property="Background" Value="{Binding BackgroundColor}"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="#333333" />
</Style>
Code behind:
public string BackgroundColor
{
get { return "Black"; }
}
When I create the style dynamically in the code behind and assign it to HeaderStyle it is actually working. But this doesn't seem to me as an option as my style will become much more complicated (I have to remove gradients, paint a horizontal rectangle, adjust borders, ..). So it will get complicated to create all dynamically in the code behind.
The other bindings that are directly called in RibbonTab i.e. IsEnabled are actually working.
What is working is this code:
In Xaml:
<Setter Property="HeaderStyle" Value="{Binding Style}"/>
In the code behind:
public Style Style
{
get
{
Style style = new Style(typeof (RibbonTabHeader));
style.Setters.Add(new Setter(RibbonTabHeader.BackgroundProperty, Brushes.Green));
return style;
}
}
Any ideas how I can bind dynamic values to the object RibbonTabHeader ?
I was thinking of defining a style in the xaml and then load it via LoadResource in the codebehind. But I'm not sure if I can clone it later to assign all different colors to different tabs.

After days of research I still didn't find a propper answer to my question.
I nevertheless found a workaround to make it work.
What I finally did is to us a CustomConverter to exploit the tab name that is dynamically bound.
The Converter that is returning a different color based on each tab name looks like this:
public class TabNameToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value.ToString() = "tabName")
return "Red";
return "Black";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
The Xaml looks like this (Content is the name of the Tab, it is replacing the background color)
<UserControl.Resources>
<ResourceDictionary>
<Converters:TabNameToColorConverter x:Key="TabNameToColorConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Style TargetType="{x:Type ribbon:RibbonTabHeader}">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Path=Content,Converter={StaticResource TabNameToColorConverter}}"/>
</Style>

Related

Generic style with specific trigger values

I have several TextBlocks in my UserControl that I want to change to become Bold and have a Red font when a Property is triggered. The thing is that each of them are changed by different properties. I saw a solution here with Tags, but couldn't quite get it to work for me. I apologize if this is a duplicate, but I couldn't find any solution that solved my problem.
My Style looks like this:
<Style x:Key="TextBlockTrigger" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Tag" Value="true">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
And this is the TextBlock I'm trying to use it on:
<TextBlock Name="TextBlock1" x:Uid="TextBlock1" Text="This text should become bold and Red"
Style="{StaticResource TextBlockTrigger}" Tag="{Binding Path=TriggerProperty, UpdateSourceTrigger=PropertyChanged}"/>
I added a button with a codebehind function that reads the Tag, and a breakpoint shows that Tag is set to true, but the text is still regular black.
TriggerProperty is set by a function call in the View Constructor, after InitializeComponent:
public MyWindow()
{
InitializeComponent();
UpdateServerProperties();
}
public void UpdateServerProperties()
{
//other code
if(ServerValue == true)
{
TriggerProperty = true;
OnPropertyChanged("TriggerProperty");
}
}
It's a bit simplified, but the actual code is overly complicated, but results in the same thing. ServerValue gets a value, and I have confirmed TriggerProperty does get updated to true.
The Tag property has an object type. Xaml has no way of knowing that true represents a bool value, so it just assumes you meant it to be a string. Assuming you are setting Tag to a boolean value, your Trigger is evaluating Equals(true, "true"), so the condition fails.
Try using {x:Static} to point to some constant boolean value. I keep a KnownBoxes class around for this sort of thing:
public static class KnownBoxes
{
public static readonly object True = true;
public static readonly object False = false;
// ... more common values ...
}
These values are easily referenced from Xaml, e.g., {x:Static ns:KnownBoxes.True}.
Alternatively, you could use the element syntax:
<Trigger Property="Tag">
<Trigger.Value>
<s:Boolean xmlns:s="clr-namespace:System;assembly=mscorlib">True</s:Boolean>
</Trigger.Value>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
Or you could set Tag to the string "true", though that may cause some confusion when someone else works on your code :).
If you set TriggerProperty to true dynamically at runtime, the class where this property is defined should implement the INotifyPropertyChanged interface and raise the PropertyChanged event for the trigger to trigger.
If you set the Tag property to a hard-coded value of true, your sample markup should work as expected:
<TextBlock Name="TextBlock1" x:Uid="TextBlock1" Text="This text should become bold and Red"
Style="{StaticResource TextBlockTrigger}" Tag="true"/>
You should also set the Value property to a typed bool value in your Style:
<Style x:Key="TextBlockTrigger" TargetType="{x:Type TextBlock}"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<Style.Triggers>
<Trigger Property="Tag">
<Trigger.Value>
<s:Boolean>True</s:Boolean>
</Trigger.Value>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>

Displaying "NaN" in a cell depending on the datasource value

I have a Style on my data grid cell defined as follows:
<Style TargetType="{x:Type Editors:XamNumericEditor}" x:Key="MyVisibleStyle" BasedOn="{StaticResource InPointStyle}">
<Setter Property="Mask" Value="-nnnnnnnnnnn.nnnn"/>
<Setter Property="Format" Value="#,##0.000;-#,##0.000" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataItem.IsPermissioned}" Value="False" >
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
So depending on the value of DataItem.IsPermissioned I set the Visibility of the cell.
How can I change this so that if IsPermissioned=false then I display "NaN" in the cell?
Did you try creating a converter, which implements IValueConverter?
In the binding you would use it like
Binding="{Binding Path=Something, Converter={StaticResource yourConverter}}"
and your converter would be something like
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (validation on the value)
{ return "NaN" } }
You need to alternate the content property instead. So use the part below or add your own content depending on the cell pattern.
<Setter Property="Content" Value="NaN" />

Get Valdiation error in Tooltip if there is an error, else show explanatory Tooltip (using TextBox Style)

Update: Found a Solution, see below!
I'm try to implement the following behavior:
There are several TextBoxes in a UserControl. Each TextBox-ToolTip should show a specific string, which is located in a Resource.resx-file.
If the Valdiation of this TextBox returns an error, the returned error string will be shown in the ToolTip.
This should be done using a Style. My current status is, that I can show the specific Validation.Errors AND a default ToolTip, which is the same for every TextBox which uses the Style.
So my Style is :
<Style TargetType="{x:Type TextBox}">
<Setter Property="ToolTip" Value="ExampleToolTip"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
With this Style, i get the behavior described above.
Now I want the Style part
<Style TargetType="{x:Type TextBox}">
<Setter Property="ToolTip" Value="ExampleToolTip"/>
...
</Style>
to be TextBox-Specific.
I tried to write an Attatched Property for my TextBoxes, so that I can define a second string, which should be used as the standard ToolTip.
The Attached Property Code ist shown below:
public class TextBox2 : DependencyObject
{
public static void SetToolTipStandard(TextBox target, string value)
{
target.SetValue(ToolTipStandardProperty, value);
}
public static string GetToolTipStandard(TextBox target)
{
return (string)target.GetValue(ToolTipStandardProperty);
}
public static DependencyProperty ToolTipStandardProperty = DependencyProperty.RegisterAttached(
"ToolTipStandard",
typeof(string),
typeof(TextBox),
new PropertyMetadata());
}
Now I want to set a TextBox2.ToolTipStandard property on my TextBoxes in XAML, and the TextBox-Style should take this Property to set the default ToolTip-Text. I tried several combinations of Bindings but with no success. Is there a way to achieve this behavior?
An idea, you can put a hidden border in the ControlTemplate with a BorderThickness of 0 around the input control which provides the error tooltip. If there is an error you set the visibility to visible and the error tooltip is shown.
It's just an idea, but it may work.
I managed to get the desired behaviour by naming all my TextBoxes and adding multiple Triggers to my TextBox-Style. Code follows:
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<!--Here are the static resource strings for the normal-state ToolTip-->
<Trigger Property="Name" Value="TextBox1">
<Setter Property="ToolTip" Value="{x:Static properties:UIStrings.TextBox1_ToolTip_String}"/>
</Trigger>
<Trigger Property="Name" Value="TextBox2">
<Setter Property="ToolTip" Value="{x:Static properties:UIStrings.TextBox2_ToolTip_String}"/>
</Trigger> ...
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>

Is it possible to pass a parameter (a Binding) to a WPF Style

I have a WPF style that I am applying to list box items. Currently, each item is bound to a KeyValuePair. I display the Key in a TextBlock inside the ListBoxItem:
<TextBlock Name="Label" Text="{Binding Key}" />
What I want to do is make the Style generic so that if the data is not a KeyValuePair, (maybe just a string), I can bind the data correctly to the TextBlock.
Is there a way to pass a parameter to a Style or DataTemplate or make the data binding generic?
My style:
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" Padding="2" SnapsToDevicePixels="true" CornerRadius="4" BorderThickness="1">
<TextBlock Name="Label" Text="{Binding Key}" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectionGradient}" />
</MultiTrigger>
<ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Let me give you a quick example. Let's say your TextBox contains a number, and you want it to be with a red Background if the number is negative, or with a green background if >= 0
Here is the style you'd write:
<TextBlock Text="{Binding Key}" >
<TextBlock.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{Binding Key, Converter={StaticResource MyConverterResource}" />
</Style>
</TextBlock.Style>
</TextBlock>
Here is the converter you'd write:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double source = (double)value;
return source < 0 ? Brushes.Red : Brushes.Green;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// We don't care about this one here
throw new NotImplementedException();
}
}
And, to access the converter in your xaml, you just have to do the following, assuming your converter is in the namespace MyNamespace:
<Window xmlns:my="clr-namespace:MyNamespace">
<Window.Resources>
<my:MyConverter x:Key="MyConverterResource">
</Window.Resources?
<!-- Your XAML here -->
</Window>
(of course you can put this in any Resources, may it be a UserControl or whatever )
This will allow you to call your converter by writing {StaticResource MyConverterResource}
And here, you'd have a conditional style, the converter deciding which color to set as a background according to one parameter, in my case, the value itself (but it can be whatever you want)

How to dynamically set the Row's text to bold, using MVVM, C# and no Code-Behind?

I'm developing a WPF Page using .NET, MVVM, no code-behind, using PropertyChanged. In this Page, I have a DataGrid with a lot of columns.
Into the DB, one of the Columns, let's call it HIGHLIGHT, will have values S or N. If value = S, the entire line will be Bold, or ExtraBold. Case N = Normal.
I made this work using this code in XAML:
<Style x:Key="TextRowStyle" TargetType="{x:Type TextBlock}" >
<Style.Triggers>
<DataTrigger Binding="{Binding Slab.Highlight}" Value="S">
<Setter Property="FontWeight" Value="ExtraBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
But doing in this way, I'll have to put in EVERY COLUMN, this code to make it work (Notice the ElementStyle):
<DataGridTextColumn Header="Test" Binding="{Binding SlabSeq}" ElementStyle="{StaticResource TextRowStyle}"/>
What do I need:
Each table of my DB has several Columns, over 60, 70, and i'm searching for a way to make this more easy, like the StaticResource TextRowStyle i've made...
Another thing i've made, it was a Converter:
public class HighlightConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (System.Convert.ToChar(value).Equals("S"))
return FontWeights.ExtraBold;
else
return FontWeights.Normal;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return FontWeights.ExtraBold;
}
}
I'd tried to make a converter inside those fields, using:
<Page.Resources>
<vm:HighlightConverter x:Key="HighlightConverter"/>
</Page.Resources>
and into the Grid:
<TextBlock FontWeight={Binding Slab.Highlight, Converter={StaticResource HighlightConverter}}"/>
Does anyone have any idea of how I can make this work?
Best regards,
Gustavo.
why you dont create a "local" style in your DataGrid.Resources. did i get it right that the entire row has to be bold (S) or normal (N)?
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}" >
<Setter Property="FontWeight" Value="Normal"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Slab.Highlight}" Value="S">
<Setter Property="FontWeight" Value="ExtraBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
with the code above all cells would be normal, but if a datarow has Slab.Highlight=S all cells would be bold. the code is not tested. maybe you have to add TextBlock.Fontweight or something like that.
What about applying the style implicitly by dropping the x:Key? That should make it apply everywhere in the grid when placed in the DataGrid.Resources.
This might work. Sorry, it's untested as I'm nowhere near an IDE.
<DataGrid>
<DataGrid.Resources>
<Style TargetType="DataGridTextColumn">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontWeight" Value="{Binding Slab.Highlight, Converter={StaticResource HighlightConverter}}"/>
</Style>
</Style.Resources>
</Style>
</DataGrid.Resources>
</DataGrid>

Categories