Reputation: 167
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 TryFindResource
call. 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
Reputation: 33506
Sorry for quick&short¬ 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