This project is read-only.

Problem with DataTriggers on Markers?

Dec 10, 2009 at 1:21 AM
Edited Dec 10, 2009 at 1:46 AM

Hello,

I was wondering if I am doing something wrong or is this is a currently known limitation?

I have a BarChart as defined below.  Its marker DataTemplate is a rectangle whose data is specified through binding to a local Segment data type, also below.

As my data changes, I can see the rectangle bars' extents being updated in the bar chart.  I also have some MouseDown logic in my code which sets IsSelected=true when the rectangle is clicked.  

My problem is that the template's data trigger never fires when IsSelected is set to True in order to change the Rectangle's Fill based in the property change.  If I change the Rectangle's Fill in my codebehind, based on the property notification when IsSelected==true, the rectangle's Fill change is reflected in the chart - so my PropertyChanged notification does work.  The EventTriggers worked just fine, but not the DataTrigger.

I dug around in the D3 source and found examples where visual properties are being affected by the marker's positional changes, using chart.AddPropertyBinding and chart.AddPropertyBindingForNamedPart.  If that's the mechanism that's supported to do DataTriggers, then how do I go about utilizing these methods to change the visual based on the marker's bound values in local:Segment?

Simply doing the following doesn't quite do it - it doesn't automatically get fired like a DataTrigger would; and adding chart.DataSource.RaiseCollectionReset(); after the selection change logic does not seem to have any effect (i.e. using the AddPropertyBindingForNamedPart syntax, the bars are always blue).

chart.AddPropertyBindingForNamedPart<Segment>("bar", Shape.FillProperty, p => {
    if (p.IsSelected)
        return Brushes.Orange;
    else
        return Brushes.Blue;
});

Perhaps I am missing something really basic; please help.

Thank you so much, as always.

E.

 

	<d3:BarChart x:Name="chart" >
		<d3:TemplateMarkerGenerator Custom:SelectiveScrollingGrid.SelectiveScrollingOrientation="Horizontal">
                    <DataTemplate DataType="{x:Type local:Segment}" >
                        <Rectangle x:Name="bar" MouseDown="bar_MouseDown" DataContext="{Binding}"
                                   Width="25" 
                                   Fill="Blue"
                                   StrokeThickness="1"
                                   Stroke="DarkBlue"
					d3:ViewportPanel.Y="{Binding YMin}" 
					d3:ViewportPanel.X="{Binding X}"
					d3:ViewportPanel.ViewportHeight="{Binding YMax}"
					d3:ViewportPanel.ViewportVerticalAlignment="Bottom">
                            <Rectangle.Triggers>
                                <EventTrigger RoutedEvent="Mouse.MouseEnter">
                                    <BeginStoryboard>
                                        <Storyboard TargetProperty="(Rectangle.Opacity)">
                                            <DoubleAnimation To="0.5" Duration="0:0:0.2" />
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                                <EventTrigger RoutedEvent="Mouse.MouseLeave">
                                    <BeginStoryboard>
                                        <Storyboard TargetProperty="(Rectangle.Opacity)">
                                            <DoubleAnimation To="1" Duration="0:0:0.2" />
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                            </Rectangle.Triggers>
                        </Rectangle>
                        <DataTemplate.Triggers>
                            <DataTrigger Binding="{Binding Path=IsSelected}" Value="True">
                                <Setter Property="Shape.Fill" Value="Orange" TargetName="bar" />
                            </DataTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </d3:TemplateMarkerGenerator>
	</d3:BarChart>

    public class Segment : INotifyPropertyChanged {

        private double yMin = 0.0;
        public double YMin { get { return yMin; } set { yMin = value; OnPropertyChanged(new PropertyChangedEventArgs("YMin")); } }

        private double yMax = 1.0;
        public double YMax { get { return yMax; } set { yMax = value; OnPropertyChanged(new PropertyChangedEventArgs("YMax")); } }

        private double x = 1.0;
        public double X { get { return x; } set { x = value; OnPropertyChanged(new PropertyChangedEventArgs("X")); } }

        private double xWidth = 1.0;
        public double XWidth { get { return xWidth; } set { xWidth = value; OnPropertyChanged(new PropertyChangedEventArgs("XWidth")); } }

        private object tag = null;
        public object Tag { get { return tag; } set { tag = value; OnPropertyChanged(new PropertyChangedEventArgs("Tag")); } }

        private bool selected = false;
        public bool IsSelected { get { return selected; } set { selected = value; OnPropertyChanged(new PropertyChangedEventArgs("IsSelected")); } }


        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(PropertyChangedEventArgs e) {
            if (PropertyChanged != null) {
                PropertyChanged(this, e);
            }
        }

    }
Dec 11, 2009 at 11:08 PM

As a follow-up in looking into this, I modified the BarChartPage which is part of NewMarkersSample as follows:

1. Named the rectangle 

2. Changed the rectangle fill to be a constant color - i.e. not using the HeightToFillConverter

3. Added a DataTrigger, bound to YMax, such that if YMax==4, if should turn the Fill to Blue.

Result: Fill color does not change from orange, and the data trigger never executes.

Thanks!

Code:

 

<d3:BarChart Name="barChart">
    <d3:TemplateMarkerGenerator>
        <DataTemplate>
            <Rectangle x:Name="Bar" Width="30" Fill="Orange"
                       d3:ViewportPanel.Y="{Binding YMin}" 
                       d3:ViewportPanel.X="{Binding X}"
                       d3:ViewportPanel.ViewportHeight="{Binding YMax}"
                       d3:ViewportPanel.ViewportVerticalAlignment="Bottom">
                <d3:LiveToolTipService.ToolTip>
                    <d3:LiveToolTip BorderBrush="DarkGray" Background="{d3:SelfBinding Path=Owner.Fill, Converter={StaticResource lighterConverter}}"
                                    TextBlock.Foreground="{d3:SelfBinding Path=Owner.Fill, Converter={StaticResource backToForeConv}}">
                        <TextBlock>
                            <TextBlock.Text>
                                <MultiBinding Converter="{StaticResource tooltipConverter}">
                                    <Binding Path="X"/>
                                    <Binding Path="YMin"/>
                                    <Binding Path="YMax"/>
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </d3:LiveToolTip>
                </d3:LiveToolTipService.ToolTip>
            </Rectangle>
            <DataTemplate.Triggers>
                <DataTrigger Binding="{Binding YMax}" Value="4">
                    <Setter TargetName="Bar" Property="Fill" Value="Blue" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </d3:TemplateMarkerGenerator>
</d3:BarChart>


Dec 12, 2009 at 4:19 PM

Hi,

probably in your case trigger's value was not applied to property because property has already had an implicitly set value, which has a higher priority, than style setters and triggers.

Here is my code that works:

 

		<d3:ChartPlotter Name="plotter">
			<d3:BarChart Name="barChart" ItemsSource="{Binding}" BoundsUnionMode="Bounds">
				<d3:TemplateMarkerGenerator>
					<DataTemplate>
						<Rectangle Width="30"
								   d3:ViewportPanel.Y="{Binding YMin}" 
								   d3:ViewportPanel.X="{Binding X}"
								   d3:ViewportPanel.ViewportHeight="{Binding YMax}"
								   d3:ViewportPanel.ViewportVerticalAlignment="Bottom">
							<Rectangle.Style>
								<Style TargetType="Rectangle">
									<Setter Property="Fill" Value="Orange"/>
									<Style.Triggers>
										<DataTrigger Binding="{Binding YMax}" Value="3.0">
											<Setter Property="Fill" Value="Blue"/>
										</DataTrigger>
									</Style.Triggers>
								</Style>
							</Rectangle.Style>

						</Rectangle>
					</DataTemplate>
				</d3:TemplateMarkerGenerator>
			</d3:BarChart>
		</d3:ChartPlotter>

Best regards,

Mikhail.

 

Dec 16, 2009 at 5:01 AM
thecentury wrote:

Hi,

probably in your case trigger's value was not applied to property because property has already had an implicitly set value, which has a higher priority, than style setters and triggers.

Here is my code that works:

...

Best regards,

Mikhail.

 

 

Interesting.  Thanks for your snippet; it does work as you've written it, but I guess I am still unsure why my syntax does not work.  It works in other scenarios...  Oh well.  I'll take it.  Thanks!

 

Jan 10, 2010 at 8:26 PM

Hi Mikhail,

In your above example, I ran into a problem where, in the style, if I start out by having the rectangle Visibility set to Hidden and then set it to Visible in the DataTrigger, the rectangle does not become visible unless the chart (or the actual rectangle) changes size.  When it's resizes, then it shows up.  Is that a bug?  For now, as a workaround to achieve the same effect, I just set the Fill to Transparent by default, and then set its color to something else in the trigger, when I want it to show.  Thoughts?

Thank you,

E.

 

<Rectangle.Style>
	<Style TargetType="Rectangle">
		<Setter Property="Visibility" Value="Hidden"/>
		<Setter Property="Fill" Value="Orange"/>
		<Style.Triggers>
			<DataTrigger Binding="{Binding YMax}" Value="3.0">
				<Setter Property="Visibility" Value="Visible"/>
			</DataTrigger>
		</Style.Triggers>
	</Style>
</Rectangle.Style>