This is a migrated thread and some comments may be shown as answers.

Question about timing in RadButton

4 Answers 28 Views
Buttons
This is a migrated thread and some comments may be shown as answers.
Ken
Top achievements
Rank 1
Ken asked on 05 Jul 2019, 12:14 AM

I've created a UserControl, PortStatusButton, that contains nothing but a RadButton for which I've modified its standard style to incorporate my own VisualStateGroup with its VisualStates.  Its purpose is to be a multistate button with logic based on the values of several of its properties.  I have written a custom VisualStateManager to process that logic.  I have placed the PortStatusButton into the ItemTemplate of a RadListBox and bound its properties to data in my Model.

The basic method I've used to incorporate my visual states into the button is to create a separate VisualStateGroup and then have my VisualStateManager watch for a call to GoToStateCore for a CommonStates.Normal VisualState.  I then check to see what state my button should be in and then call GoToState to go to my desired state.  This allows me to have the PortStatusButton show my states, but still let the RadButton control the overall functioning of the button.

It's all working quite well, with one exception.  When the PortStatusButton first appears, it fails to display my correct VisualState, but displays the CommonStates.Normal state instead.  I think I know why and I'd like to ask you to verify that I'm correct and suggest what I might do about it.

The mode of failure is that the first time a property gets set in PortStatusButton, the UpdateButtonAppearance method that is called by its OnPropertyChanged handler fails to obtain a reference to the the current state of my VisualStateGroup because it can't find the child Grid of the RadButton that contains the VisualStateManager.  Thus, it never calls GoToState because it doesn't know where to go, and the RadButton first displays in its CommonStates.Normal state.

The data that is feeding the bound PortStatusButton properties is coming from my ViewModel, which is defined as the DataContext in the main Grid that contains the RadListBox, and which loads the source collection in its constructor.  My suspicion is that as each PortStatusButton is instantiated by the RadListBox, the Grid in the RadButton's control template has not yet been instantiated, so it's not yet there to be found.  By the time the user starts clicking on the buttons, the Grid has been instantiated and the PortStatusButtons will function as they should.

Is that correct?

What would you suggest I do to allow the buttons to display my correct state when they first appear?

I have included excerpts from my code below:

First, excerpt from my PortStatusButton xaml:

    <UserControl.Resources>
        <Style x:Key="PortStatusButtonStyle" TargetType="{x:Type telerik:RadButton}">
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="Background">
                <Setter.Value>
                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                        <GradientStop Color="White" Offset="0"/>
                        <GradientStop Color="Gainsboro" Offset="0.5"/>
                        <GradientStop Color="#FFADADAD" Offset="0.5"/>
                        <GradientStop Color="#FFD4D4D4" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>
            <Setter Property="BorderBrush" Value="#FF848484"/>
            <Setter Property="CornerRadius" Value="1"/>
            <Setter Property="Padding" Value="3"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type telerik:RadButton}">
                        <Grid Name="theStateGroupsRoot" SnapsToDevicePixels="True">
                            <VisualStateManager.CustomVisualStateManager>
                                <local:PortStatusButtonVisualStateManager/>
                            </VisualStateManager.CustomVisualStateManager>
                            <VisualStateManager.VisualStateGroups>
                                <!-- RadButton VisualStateGroups-->
                                <VisualStateGroup x:Name="ConditionStates">
                                    <VisualState x:Name="Inactive">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="InactiveBorder">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <DoubleAnimation Duration="0" To="0.5" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="InactiveBorder"/>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InactiveContent"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ActiveContent"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ConnectedContent"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Active">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ActiveBorder">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <DoubleAnimation Duration="0" To="0.5" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ActiveBorder"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InactiveContent"/>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ActiveContent"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ConnectedContent"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Connected">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility" Storyboard.TargetName="ConnectedBorder">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <DoubleAnimation Duration="0" To="0.5" Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="ConnectedBorder"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="InactiveContent"/>
                                            <DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ActiveContent"/>
                                            <DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ConnectedContent"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <!-- RadButton border declarations -->
                            <Border x:Name="InactiveBorder" BorderBrush="#FF848484" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" Opacity="0">
                                <Border.Background>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="White" Offset="0"/>
                                        <GradientStop Color="#FF7E7E7E" Offset="0.5"/>
                                        <GradientStop Color="#FF747474" Offset="0.5"/>
                                        <GradientStop Color="#FFA0A0A0" Offset="1"/>
                                    </LinearGradientBrush>
                                </Border.Background>
                                <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" Background="{x:Null}" CornerRadius="{TemplateBinding InnerCornerRadius}"/>
                            </Border>
                            <Border x:Name="ActiveBorder" BorderBrush="#FF848484" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" Opacity="0">
                                <Border.Background>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="White" Offset="0"/>
                                        <GradientStop Color="#FFFF7171" Offset="0.5"/>
                                        <GradientStop Color="Red" Offset="0.5"/>
                                        <GradientStop Color="#FFFF9090" Offset="1"/>
                                    </LinearGradientBrush>
                                </Border.Background>
                                <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" Background="{x:Null}" CornerRadius="{TemplateBinding InnerCornerRadius}"/>
                            </Border>
                            <Border x:Name="ConnectedBorder" BorderBrush="#FF848484" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" Opacity="0">
                                <Border.Background>
                                    <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                        <GradientStop Color="White" Offset="0"/>
                                        <GradientStop Color="#FF59FD59" Offset="0.5"/>
                                        <GradientStop Color="Lime" Offset="0.5"/>
                                        <GradientStop Color="#FF96FF96" Offset="1"/>
                                    </LinearGradientBrush>
                                </Border.Background>
                                <Border BorderBrush="White" BorderThickness="{TemplateBinding BorderThickness}" Background="{x:Null}" CornerRadius="{TemplateBinding InnerCornerRadius}"/>
                            </Border>
                            <!-- More RadButton Border declarations -->
                            <ContentPresenter x:Name="Content" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" TextElement.Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                            <ContentPresenter x:Name="InactiveContent" ContentTemplate="{TemplateBinding ContentTemplate}" Content="Inactive" ContentStringFormat="{TemplateBinding ContentStringFormat}" TextElement.Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Opacity="0"/>
                            <ContentPresenter x:Name="ActiveContent" ContentTemplate="{TemplateBinding ContentTemplate}" Content="Active" ContentStringFormat="{TemplateBinding ContentStringFormat}" TextElement.Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Opacity="0"/>
                            <ContentPresenter x:Name="ConnectedContent" ContentTemplate="{TemplateBinding ContentTemplate}" Content="Connected" ContentStringFormat="{TemplateBinding ContentStringFormat}" TextElement.Foreground="{TemplateBinding Foreground}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Opacity="0"/>
                            <Border x:Name="CommonStatesWrapper">
                                <Border x:Name="FocusVisual" BorderBrush="#FFFFC92B" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="{TemplateBinding CornerRadius}" Visibility="Collapsed">
                                    <Border BorderBrush="Transparent" BorderThickness="1" CornerRadius="{TemplateBinding InnerCornerRadius}"/>
                                </Border>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid Name="theGrid">
        <telerik:RadButton Name="theButton" Style="{DynamicResource PortStatusButtonStyle}" Click="TheButton_Click"/>
    </Grid>
</UserControl>

 

And an excerpt from my PortStatusButton code behind:

public partial class PortStatusButton : UserControl
    {
        // Constants
 
        //   ConditionStates VisualStateGroup constants
 
        public const string cConditionVisualStateGroup = "ConditionStates";
 
        public const string cActiveVisualState = "Active";
        public const string cInactiveVisualState = "Inactive";
        public const string cConnectedVisualState = "Connected";
 
        //   CommonStates VisualStateGroup constants
 
        public const string cCommonGroupVisualStateGroup = "CommonStates";
 
        public const string cNormalVisualState = "Normal";
 
        // Properties
 
        private bool IsSwitchOn { get; set; }
 
        public int PortNumber { get { return (int)GetValue(PortNumberProperty); } set { SetValue(PortNumberProperty, value); } }
        public static readonly DependencyProperty PortNumberProperty = DependencyProperty.Register("PortNumber", typeof(int), typeof(PortStatusButton), new PropertyMetadata(OnPortNumberChanged));
        public int RuleIndex { get { return (int)GetValue(RuleIndexProperty); } set { SetValue(RuleIndexProperty, value); } }
        public static readonly DependencyProperty RuleIndexProperty = DependencyProperty.Register("RuleIndex", typeof(int), typeof(PortStatusButton), new PropertyMetadata(OnRuleIndexChanged));
 
        // Constructors
 
        public PortStatusButton()
        {
            IsSwitchOn = false;
            InitializeComponent();
        }
 
        // Methods
 
        private void UpdateButtonAppearance(PortStatusButton control)
        {
            // Rules:
            //   +------------+-----------+--------++===========+
            //   | PortNumber | RuleIndex | Switch ||   Button  |
            //   |  Assigned  | Assigned  |   On   ||   State   |
            //   +------------+-----------+--------++===========+
            //   |     0      |     0     |   0    || Inactive  |
            //   +------------+-----------+--------++===========+
            //   |     0      |     0     |   1    || Inactive  |
            //   +------------+-----------+--------++===========+
            //   |     0      |     1     |   1    || Inactive  |
            //   +------------+-----------+--------++===========+
            //   |     0      |     1     |   0    || Inactive  |
            //   +------------+-----------+--------++===========+
            //   |     1      |     1     |   0    || Inactive  |
            //   +------------+-----------+--------++===========+
            //   |     1      |     1     |   1    || Connected |
            //   +------------+-----------+--------++===========+
            //   |     1      |     0     |   1    ||  Active   |
            //   +------------+-----------+--------++===========+
            //   |     1      |     0     |   0    || Inactive  |
            //   +------------+-----------+--------++===========+
            //
            //   Boolean equations:
            //     Inactive  = Not(PortNumber Assigned) + ((PortNumber Assigned) * (Not(Switch On))
            //     Active    = (PortNumber Assigned) * NOT(RuleIndex Assigned) * (Switch On)
            //     Connected = (PortNumberAssigned) * (RuleIndex Assigned) * (Switch On)
 
            bool IsPortNumberAssigned = PortNumber > PortConstants.UnassignedPortNumber;
            bool IsRuleIndexAssigned = RuleIndex > PortConstants.UnassignedRuleIndex;
            string NewStateName = cInactiveVisualState;
 
            if (IsPortNumberAssigned)
            {
                if (IsSwitchOn)
                {
                    if (IsRuleIndexAssigned)
                    {
                        NewStateName = cConnectedVisualState;
                    }
                    else
                    {
                        NewStateName = cActiveVisualState;
                    }
                }
            }
 
            FrameworkElement Root = VisualStateManagerHelper.FindVisualStateGroupsRoot(control.theButton);
             
            if(Root != null)
            {
                VisualStateGroup Group = VisualStateManagerHelper.GetVisualStateGroup(cConditionVisualStateGroup, Root);
                VisualState CurrentState = Group.CurrentState;
                bool StateWasChanged = false;
 
                if (CurrentState == null)
                {
                    StateWasChanged = VisualStateManager.GoToState(control.theButton, NewStateName, false);
                    CurrentState = Group.CurrentState;
                }
                if (NewStateName != CurrentState.Name)
                {
                    StateWasChanged = VisualStateManager.GoToState(control.theButton, NewStateName, false);
                }
            }
        }
 
        private static void OnPortNumberChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PortStatusButton Me = (d is PortStatusButton) ? (PortStatusButton)d : null;
            if (Me != null)
            {
                Me.UpdateButtonAppearance(d as PortStatusButton);
            }
        }
 
        private static void OnRuleIndexChanged (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            PortStatusButton Me = (d is PortStatusButton) ? (PortStatusButton)d : null;
            if (Me != null)
            {
                Me.UpdateButtonAppearance(d as PortStatusButton);
            }
        }
 
        private void TheButton_Click(object sender, RoutedEventArgs e)
        {
            PortStatusButton Ancestor = ZWVisualTreeHelper.FindParent<PortStatusButton>(sender as DependencyObject);
            IsSwitchOn = !IsSwitchOn;
            UpdateButtonAppearance(Ancestor);
        }
    }

 

And the custom VisualStateManager:

public class PortStatusButtonVisualStateManager : VisualStateManagerHelper
{
    // Methods
 
    protected override bool GoToStateCore(FrameworkElement control, FrameworkElement stateGroupsRoot, string stateName, VisualStateGroup group, VisualState state, bool useTransitions)
    {
        bool IsGood = false;
        string NewStateName = "";
        bool ShouldTransitionToNewState = false;
 
        if ((group != null) && (state != null))
        {
            PortStatusButton Me = ZWVisualTreeHelper.FindParent<PortStatusButton>(control);
 
            // The basic premise on how this works is that the ConditionGroup visual states override the Normal state of the CommonStates group.
            //
            // The RadButton CommonGroup visual states control the button in its "steady state" condition.  Normal is its default visual state and the button is in this state unless something else it happening.
            // The other Common states handle it when disabled, pressed, or the mouse is hovering over it.  These are all mutually exclusive and form the base appearance of the button.
            //
            // The other state groups likewise each contain mutually exclusive visual states, but they modify the appearance of the button when it's any of the Common states.  So, the final appearance of the
            // button is the result of the Common state it in, as modified by the states of the other groups.
            //
            // I wanted to take advantage of this already-existing functionality and tie into the Common group, but I felt it was important to keep my states separate because I wasn't sure how they would "play"
            // with the other groups when in a non-normal state.  After all, I'm not sure yet how I want the button to appear when it's disabled, for instance.  Will it depend on the state of the Condition?
            //
            // What I arrived at is that I want to override the CommonGroup.Normal state with one of the ConditionGroup states, but leave the other CommonGroup states alone to do their own thing.  So, here
            // we will examine the requested new state to see if it is CommonGroup.Normal, and if it is, we'll chage the state to the current state of the ConditionGroup.
 
            if ((group.Name == PortStatusButton.cCommonGroupVisualStateGroup) && (state.Name == PortStatusButton.cNormalVisualState))
            {
                VisualStateGroup NewGroup = GetVisualStateGroup(PortStatusButton.cConditionVisualStateGroup, stateGroupsRoot);
                VisualState NewState = null;
                if (NewGroup != null)
                {
                    NewState = NewGroup.CurrentState;
                    if (NewState == null)
                    {
                    }
                    else
                    {
                        NewStateName = NewState.Name;
                        ShouldTransitionToNewState = true;
                    }
                }
            }
            IsGood = base.GoToStateCore(control, stateGroupsRoot, stateName, group, state, useTransitions);
            if (ShouldTransitionToNewState)
            {
                IsGood = GoToState(control, NewStateName, false);
            }
        }
        return IsGood;
    }
}

 

And an excerpt from the control containing the RadListBox:

    <UserControl.Resources>
        <Style x:Key="DraggableListBoxItem" TargetType="telerik:RadListBoxItem" BasedOn="{StaticResource RadListBoxItemStyle}">
            <Setter Property="telerik:DragDropManager.AllowCapturedDrag" Value="True" />
        </Style>
    </UserControl.Resources>
    <Grid IsSharedSizeScope="True" Width="Auto">
        <Grid.DataContext>
            <vm:InputPortViewModel/>
        </Grid.DataContext>
        <Grid.RowDefinitions>
            <RowDefinition SharedSizeGroup="ConfiguratorMajorHeaderRowGroup"/>
            <RowDefinition SharedSizeGroup="ConfiguratorHeaderRowGroup"/>
            <RowDefinition SharedSizeGroup="ConfiguratorListRowGroup"/>
            <RowDefinition SharedSizeGroup="ConfiguratorFooterRowGroup"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" Text="Input Ports" HorizontalAlignment="Center"/>
        <Border Grid.Row="0" BorderBrush="Black" BorderThickness="0,0,1,1"/>
        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition SharedSizeGroup="InputPortsStatusButtonColumnGroup"/>
                <ColumnDefinition SharedSizeGroup="InputPortNumberColumnGroup"/>
                <ColumnDefinition SharedSizeGroup="InputPortHyphenColumnGroup"/>
                <ColumnDefinition SharedSizeGroup="InputPortNameColumnGroup"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Border Grid.Column="1" Grid.ColumnSpan="6" BorderBrush="Black" BorderThickness="0,0,1,0"/>
            <TextBlock Grid.Column="1" Text="Status" HorizontalAlignment="Center"/>
            <TextBlock Grid.Column="2" Text="#" HorizontalAlignment="Center"/>
            <TextBlock Grid.Column="3" Text=""/>
            <TextBlock Grid.Column="4" Text="Name" HorizontalAlignment="Center"/>
        </Grid>
        <telerik:RadListBox Grid.Row="2" Name="PortListBox" ItemsSource="{Binding Ports}" ItemContainerStyle="{StaticResource DraggableListBoxItem}">
                <telerik:RadListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition SharedSizeGroup="InputPortsStatusButtonColumnGroup"/>
                            <ColumnDefinition SharedSizeGroup="InputPortNumberColumnGroup"/>
                            <ColumnDefinition SharedSizeGroup="InputPortHyphenColumnGroup"/>
                            <ColumnDefinition SharedSizeGroup="InputPortNameColumnGroup"/>
                        </Grid.ColumnDefinitions>
                        <local:PortStatusButton Grid.Column="0" x:Name="thePortStatusButton"
                                                PortNumber="{Binding PortNumber}"
                                                RuleIndex="{Binding RuleIndex}"/>
                        <TextBlock Grid.Column="1" Name="thePortNumber"
                                   Text="{Binding PortNumber}" FontWeight="Bold"
                                   HorizontalAlignment="Right" Margin="5,0,0,0"/>
                        <TextBlock Grid.Column="2" Text="-"
                                   HorizontalAlignment="Center" Margin="5,0,5,0"/>
                        <TextBlock Grid.Column="3" Name="theDisplayName"
                                   Text="{Binding DisplayName}" Margin="0,0,5,0"/>
                    </Grid>
                </DataTemplate>
            </telerik:RadListBox.ItemTemplate>
            <telerik:RadListBox.DragDropBehavior>
                <telerik:ListBoxDragDropBehavior />
            </telerik:RadListBox.DragDropBehavior>
            <telerik:RadListBox.DragVisualProvider>
                <telerik:ListBoxDragVisualProvider />
            </telerik:RadListBox.DragVisualProvider>
        </telerik:RadListBox>
        <StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Center">
            <TextBlock Name="theCount" Text="{Binding Ports.Count}"/>
            <TextBlock Text="Input Ports" Margin="5,0,0,0"/>
        </StackPanel>
        <Border Grid.Row="3" BorderBrush="Black" BorderThickness="0,0,1,1"/>
    </Grid>
</UserControl>

4 Answers, 1 is accepted

Sort by
0
Dilyan Traykov
Telerik team
answered on 09 Jul 2019, 02:42 PM
Hello Ken,

Thank you very much for the detailed description of the issue and the provided code snippets.

Indeed, your suspicion that the GoToState event is not fired at first as the control is still not loaded and its Grid root is null seems to be correct.

My suggestion in this case would be to call the UpdateButtonAppearance method in the button's Loaded event as well:

private void PortStatusButton_Loaded(object sender, RoutedEventArgs e)
{
    this.UpdateButtonAppearance(this);
}

I prepared a small sample project based on the code snippets you provided where handling the Loaded event like this correctly sets the state of the control.

Could you please try this out at your end as well and let me know how it goes? I will be awaiting your reply.

Regards,
Dilyan Traykov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Ken
Top achievements
Rank 1
answered on 09 Jul 2019, 09:21 PM

Hi Dilyan,

To test this, I had to revert back to the version of my app that I was working on at that time, so I'm not sure I completely rebuilt the exact application.  I applied two statements from your sample: the point in the PortStatusButton constructor where you registered the handler, and the PortStatusButton_Loaded method.  Both of them are being executed for all of the buttons, but the buttons still first appear in the Normal visuals state.  Are there other code changes that you made that I need to incorporate?

Meanwhile, I continued on with my coding and one of the things I did along the way was to create a PortListItem user control that contains everything in a list item, including the PortStatusButton.  This resulted in moving the list item definition out of the RadListBox.ItemTemplate and into the main body of the new PortListItem user control.  A by product of this was that the button now initializes correctly, which I assume is because the item's definition is no longer in a template and gets instantiated before PortStatusButton's constructor's call to InitializeComponent returns.  Do I have that right?

Thanks for your help on this.  If you'd like me try something else to see if the PortStatusButton_Loaded method would work, I'd be happy to.  Just let me know.

0
Dilyan Traykov
Telerik team
answered on 10 Jul 2019, 02:22 PM
Hi Ken,

I was unable to build a runnable application using only the code you provided so I had to fill in some gaps to make it work. More specifically, I tried recreating the VisualStateManagerHelper by implementing the two methods required by your code.

I do not believe this is of importance, however, specifically since you're not facing the issue any more. I'm afraid I cannot be sure of the exact reason for this difference in behavior, but your assumption seams reasonable to me.

Please let me know if your issue is now resolved or if I can further assist you in any way.

Regards,
Dilyan Traykov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Ken
Top achievements
Rank 1
answered on 10 Jul 2019, 03:29 PM
Yes, Dilyan, it's resolved.  Thanks for you help.
Tags
Buttons
Asked by
Ken
Top achievements
Rank 1
Answers by
Dilyan Traykov
Telerik team
Ken
Top achievements
Rank 1
Share this question
or