locked
Strange behavior of Datacontext and Datatrigger, please help RRS feed

  • Question

  • Hi, i'm new to WPF and in some test i found something that cannot understand. What i'm gonna do is to display red and green circle in Label content to represent the value of Bool. The color changes to green when true, and red when false. The Bool value come from my class, which INotifyPropertyChanged implemented, and binds to Label's datacontext property via Binding, then use datatrigger in style to change the color. When there's only one pair (Bool - Label), everything works fine, but when more than one (2,3,4....), the dots just DISAPPEARED, that's very strange and want to know why that happens. The code I wrote is below:

    /* ---------------------------------------------------XAML----------------------------------*/

    <Window x:Class="WPFTest.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="400" Width="600">
        <Window.Resources>
            <Ellipse x:Key="GreenDot" Width="10" Height="10" Fill="Green"/>
            <Ellipse x:Key="RedDot" Width="10" Height="10" Fill="Red"/>

            <Style x:Key="LableStyle" TargetType="{x:Type Label}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="True">
                        <Setter Property="Content" Value="{StaticResource GreenDot}"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="False">
                        <Setter Property="Content" Value="{StaticResource RedDot}"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>

        <Grid>
            <Button x:Name="btn1" Content="Bool True" Width="75" Height="22" Click="btn1_Click" Margin="10,10,432,288"/>
            <Button x:Name="btn2" Content="Bool False" Margin="90,10,352,288" Width="75" Height="22" Click="btn2_Click"/>
            <Button x:Name="btn3" Content="Bool True" Margin="307,10,135,288" Width="75" Height="22" Click="btn3_Click" />
            <Button x:Name="btn4" Content="Bool False" Margin="387,10,55,288" Width="75" Height="22" Click="btn4_Click" />
            <Label x:Name="lb1" BorderBrush="Black" BorderThickness="1" Style="{StaticResource LableStyle}" Margin="55,129,362,91" />
            <Label x:Name="lb2" BorderBrush="Black" BorderThickness="1" Style="{StaticResource LableStyle}" Margin="354,129,63,91"/>
        </Grid>
    </Window>


    /* ---------------------------------------------------CODE----------------------------------*/

        public partial class MainWindow : Window
        {
            BoolTest bt = new BoolTest();

            public MainWindow()
            {
                InitializeComponent();
                lb1.SetBinding(DataContextProperty, new Binding("B1") { Source = bt });
                lb2.SetBinding(DataContextProperty, new Binding("B2") { Source = bt });
            }

            private void btn1_Click(object sender, RoutedEventArgs e)
            {
                bt.B1 = true;
            }

            private void btn2_Click(object sender, RoutedEventArgs e)
            {
                bt.B1 = false;
            }

            private void btn3_Click(object sender, RoutedEventArgs e)
            {
                bt.B2 = true;
            }

            private void btn4_Click(object sender, RoutedEventArgs e)
            {
                bt.B2 = false;
            }
        }

        public class BoolTest : INotifyPropertyChanged
        {
            private bool _b1;
            public bool B1
            {
                get { return _b1; }
                set
                {
                    _b1 = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("B1"));
                }
            }

            private bool _b2;
            public bool B2
            {
                get { return _b2; }
                set
                {
                    _b2 = value;
                    if (PropertyChanged != null)
                        PropertyChanged(this, new PropertyChangedEventArgs("B2"));
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;
        }
    }
    • Edited by HD.Ning Wednesday, May 14, 2014 5:40 AM
    Wednesday, May 14, 2014 5:35 AM

Answers

  • Hi,  Ellipse is UIElement, and there is only one instance of one UIElement in one VisualTree, so it cannot exist in different UI content, if you set it to Label1.Content first and then set ti to Label2.Content, it will be remove from Label2.Content automatically. 

    You can try to use template:

            <Style x:Key="LableStyle" TargetType="{x:Type Label}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="True">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <Ellipse Width="10" Height="10" Fill="Green"/>
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="False">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <Ellipse Width="10" Height="10" Fill="Red"/>
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>

    • Proposed as answer by Magnus (MM8)MVP Wednesday, May 14, 2014 9:08 AM
    • Marked as answer by HD.Ning Wednesday, May 14, 2014 10:25 AM
    Wednesday, May 14, 2014 7:15 AM

All replies

  • Hi,  Ellipse is UIElement, and there is only one instance of one UIElement in one VisualTree, so it cannot exist in different UI content, if you set it to Label1.Content first and then set ti to Label2.Content, it will be remove from Label2.Content automatically. 

    You can try to use template:

            <Style x:Key="LableStyle" TargetType="{x:Type Label}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="True">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <Ellipse Width="10" Height="10" Fill="Green"/>
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding DataContext, RelativeSource={RelativeSource Self}}" Value="False">
                        <Setter Property="ContentTemplate">
                            <Setter.Value>
                                <DataTemplate>
                                    <Ellipse Width="10" Height="10" Fill="Red"/>
                                </DataTemplate>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>

    • Proposed as answer by Magnus (MM8)MVP Wednesday, May 14, 2014 9:08 AM
    • Marked as answer by HD.Ning Wednesday, May 14, 2014 10:25 AM
    Wednesday, May 14, 2014 7:15 AM
  • Thanks a lot, need to better understand WPF :-)
    Wednesday, May 14, 2014 10:26 AM