user3565590
user3565590

Reputation: 167

Dynamic Conditional Formatting in WPF

I am working on an engineering program that has all it's calculations written in VB.net in a separate project and we are now putting it behind a WPF UI.

I've run into an issue with changing String Formats between units. For example: In Imperial Units you have a value of 4,966 lbf and converted it's 22.1 kN. You can see that it is necessary to have a different format between the 2 as they are different orders of magnitude.

What is currently set up in the program is conditional coloring (black for normal number, red for error, yellow for warning) these are set up through styles in a resource dictionary.

<Style x:Key="GlobalUserEditedTextBox" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="TextBox">
     <Setter Property="Foreground" Value="{DynamicResource EditableTextColor}"/>
     <Setter Property="FontWeight" Value="Bold"/>
</Style>
<Style x:Key="GlobalErrorTextBox" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="TextBox">
     <Setter Property="Foreground" Value="{DynamicResource ErrorTextColor}"/>
     <Setter Property="FontWeight" Value="Normal"/>
</Style>

In the program the style is chosen using a Converter and MultiBinding. ValueShow.TensionStatusShow is an enum value coming out of the VB calculation code:

<TextBlock HorizontalAlignment="Stretch" TextAlignment="Center" Text="{Binding Path=ValueShow.TensionShow}">
    <TextBlock.Style>
        <MultiBinding Converter="{StaticResource styleConverter}">
            <MultiBinding.Bindings>
                <Binding RelativeSource="{RelativeSource Self}"/>
                <Binding Path="ValueShow.TensionStatusShow"/>
            </MultiBinding.Bindings>
        </MultiBinding>
    </TextBlock.Style>
</TextBlock>

And then the MultiValueConverter:

public class StyleConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            FrameworkElement targetElement = values[0] as FrameworkElement;
            Style _newStyle;
            try
            {
                if (values[1] == null || values[1] == DependencyProperty.UnsetValue)
                    return null;

                if ((String)values[1] == StatusColor.ErrorValue.ToString())
                {
                    if (values[0].GetType() == typeof(TextBox))
                        _newStyle = (Style)targetElement.TryFindResource("GlobalErrorTextBox");
                    else if (values[0].GetType() == typeof(TextBlock))
                        _newStyle = (Style)targetElement.TryFindResource("GlobalErrorTextBlock");
                    else
                        _newStyle = null;
                } 
                else if
                {
                    if (values[0].GetType() == typeof(TextBox))
                        _newStyle = (Style)targetElement.TryFindResource("GlobalWarningTextBox");
                    else if (values[0].GetType() == typeof(TextBlock))
                        _newStyle = (Style)targetElement.TryFindResource("GlobalWarningTextBlock");
                    else
                        _newStyle = null;
                }
                return _newStyle;
            }
            catch (Exception)
            {
                if (values[0].GetType() == typeof(TextBox))
                    return (Style)targetElement.TryFindResource("GlobalUnEditableTextBox");
                else if (values[0].GetType() == typeof(TextBlock))
                    return (Style)targetElement.TryFindResource("GlobalUnEditableTextBlock");
                else
                    return null;
            }
        }

What I've tried: So the problem here is that I want to keep the string formatting "rules" out of the VB calculation methods unlike ValueShow.TensionStatusShow. Currently we have 2 resource Dictionaries (Imperial and Metric) that hold the strings for unit labels. I've tried setting up the different string formats there so it will update when the program changes units.

Imperial Resource:

<s:String x:Key="UnitsStringFormatlbfkN">F0</s:String>
<Style TargetType="TextBox" x:Key="GlobalErrorTextBoxlbkNFormatting" BasedOn="{StaticResource GlobalErrorTextBox}">
        <Setter Property="Text" Value="{Binding Path=., Mode=TwoWay, StringFormat={StaticResource UnitsStringFormatlbfkN}}" />
    </Style>

Metric Resource

<s:String x:Key="UnitsStringFormatlbfkN">F1</s:String>
<Style TargetType="TextBox" x:Key="GlobalErrorTextBoxlbkNFormatting" BasedOn="{StaticResource GlobalErrorTextBox}">
        <Setter Property="Text" Value="{Binding Path=., Mode=TwoWay, StringFormat={StaticResource UnitsStringFormatlbfkN}}" />
    </Style>

Then I would pass lbkNFormatting as a third parameter in the multibinding and append it to the TryFindResourcecall. This apparently didn't work though, It would successfully load the resource but it ignored the string format. I tested by adding background colors to the Metric resource which loaded fine, but again, the string format was ignored.

I also tried modifying the style in the MultiValueConverter by programatically adding the string formatting but ran into the IsSealed property which I can't seem to defeat

Upvotes: 3

Views: 1816

Answers (1)

quetzalcoatl
quetzalcoatl

Reputation: 33506

Sorry for quick&short&not full and indirect response, but I wanted to drop you an often overlooked solution. I sometimes use it when some binding or styling gets too complex and starts failing and seems impossible to trace why, or when I see that I could benefit from extra decoupling.

Almost all styles, triggers and complex bindings+MultiValueCoverters, you can rewrite into so-called "Attached behavior".

See this article for some quick glance. Note the two ways, attached properties and extra subelements.

Actually, I like to use the best of both at the same time. Since I wanted to just drop you a note, I've trimmed this answer and moved chatty text to this article.

I know this does not answer to your question about why the Style&Binding does not work, but I still think you may find it helpful. Your styles and bindings seem complex enough to be hard to debug, and I can't focus on that currently :| The problem is that bindings can be very easily broken (detached, overridden) by trying to put a value on wrong scope/level, and even the setters from styles and triggers can unlink them from targets. I sense that this is what's happening, but I won't have more time to help you trace it in near future. So.. good luck and I hope someone manages to give you a better response.

Upvotes: 2

Related Questions