heltonbiker
heltonbiker

Reputation: 27575

Style DataTriggers working at design time, but not at runtime - why?

(before anyone asks, the title is right: I have code working only at design time in Blend, while the most common by far would be the opposite :o )

While designing a DataTemplate in Expression Blend, I can see my DataTriggers working fine, my sample ViewModel generates a random value for the level of a battery, and both border width and background color display accordingly, either in the BatteryLevelTemplate itself, and another panel containing a lot of devices with their respective (random) battery level, with a design-time DataContext.

Here is a screenshot from Expression Blend:

enter image description here

And here a screenshot from the running application. Notice that, while both use exactely the same class as DataContext (but as design time in Blend), at runtime only the default RedBattery color setter is applied, even if the value itself (which also affects width) varies:

enter image description here

And here are the relevant code parts:

                    <Border.Width>
                        <MultiBinding Converter="{StaticResource NormalValueConverter}" FallbackValue="10">
                            <Binding Path="NívelBateria"/>
                            <Binding Path="ActualWidth" ElementName="BatteryChargeContainer"/>
                        </MultiBinding>
                    </Border.Width>
                    <Border.Style>
                        <Style TargetType="Border">
                            <Setter Property="Background">
                                <Setter.Value>
                                    <SolidColorBrush Color="{StaticResource BatteryRed}"/>
                                </Setter.Value>
                            </Setter>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding NívelBateria, Converter={StaticResource ValorMaiorQue}, ConverterParameter=0.25}" Value="True">
                                    <Setter Property="Background">
                                        <Setter.Value>
                                            <SolidColorBrush Color="{StaticResource BatteryOrange}"/>
                                        </Setter.Value>
                                    </Setter>                                       
                                </DataTrigger>
                                <DataTrigger Binding="{Binding NívelBateria, Converter={StaticResource ValorMaiorQue}, ConverterParameter=0.5}" Value="True">
                                    <Setter Property="Background">
                                        <Setter.Value>
                                            <SolidColorBrush Color="{StaticResource BatteryYellow}"/>
                                        </Setter.Value>
                                    </Setter>                                       
                                </DataTrigger>
                                <DataTrigger Binding="{Binding NívelBateria, Converter={StaticResource ValorMaiorQue}, ConverterParameter=0.75}" Value="True">
                                    <Setter Property="Background">
                                        <Setter.Value>
                                            <SolidColorBrush Color="{StaticResource BatteryGreen}"/>
                                        </Setter.Value>
                                    </Setter>                                       
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Border.Style>

============

        <DockPanel x:Name="PainelSetupsSensores" Background="#FFB8E6E8"/>
        <DockPanel x:Name="PainelSensoresDisponiveis" Background="#FFF0F0F0"
            Grid.RowSpan="2" Grid.Column="1" 
            DataContext="{Binding ReceiverAtivo}"
            d:DataContext="{d:DesignInstance Type=local:ReceiverSimulado, IsDesignTimeCreatable=True}">
            <ScrollViewer>
                <ItemsControl  ItemsSource="{Binding Sensores}" Margin="10">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </ScrollViewer>
        </DockPanel>

====================

class ValorMaiorQue : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double valor = (double)value;
        double limite = double.Parse((string)parameter);

        return valor > limite;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

UPDATE (using the valuable Snoop tip by Contango):

I "snooped" the BatteryCharge (Border) element, and found out an interesting thing:

enter image description here

My doubt now is how I am supposed to use Snoop (or anything else) to find out why the DataTrigger is not being applied.

Upvotes: 2

Views: 1149

Answers (2)

heltonbiker
heltonbiker

Reputation: 27575

I discovered the problem "accidentally", and here goes the explanation:

  1. I installed Snoop, TriggerTracing and also WPF Inspector to check properties applied by my DataTriggers, and found out the comparison provided by the DataConverter was always False;
  2. Then I put a breakpoing inside the DataConverter, to discover that, for example, the string "0.75" provided by ConverterParameter was being Double.Parsed as 75.0;
  3. Then I realized that my current language is pt-BR, and the decimal separator is comma instead of dot. Then I changed the converter, adding an InvariantCulture parameter to Double.Parse. And now it works!

    double limite = double.Parse((string)parameter, CultureInfo.InvariantCulture);

Upvotes: 3

Contango
Contango

Reputation: 80192

Your run time DataContext is not being set correctly, so your code can't bind to the correct properties at runtime.

Note that the run time DataContext is completely separate to the design time DataContext, and uses different XAML to setting the runtime DataContext.

I would recommend using Snoop to fix the problem, you can use it to flag up binding errors due to a bad runtime DataContext, see my answer here:

ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext"

Upvotes: 0

Related Questions