Home > Enterprise >  How to bind foreground property of a control to another control foreground
How to bind foreground property of a control to another control foreground

Time:11-23

Im trying to overcome a problem with a hyperlinkbutton control when it is added to a listview as an item.

The control is displaying just fine when the item is not selected (the default foreground is black), but as soon as the listview item is selected its contents background turn to dark blue, making any black text inside very hard to read, for this reason, any textblock controls inside the listview item, has its foreground property (text color) automatically changed to white (this is default system behavior), the problem is, the same thing does not happen to a hyperlink button which is inside of that said listview item.

Please note that the Hyperlink Button has a transparent background when not selected just like a textblock but the foreground color remains black at all times regardless if the listview item that contains it, is selected or not.

To overcome this, i have tried to create a textblock inside the hyperlinkbutton content to display the text and bind its foreground property to another textblock inside the listview item that changes its foreground properly by default, the binding however doesnt seem to work, it only sets the value once and then never updates again, whenever the list view item gets selected or deselected the binding does not reflect the current foreground value of the source control foreground property.

I have provided to you an example code below to reproduce my issue.

        <ListView x:Name="Employees_List_View" BorderThickness="1" BorderBrush="{ThemeResource SystemControlForegroundBaseMediumLowBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
              SelectionMode="Extended" ItemsSource="{x:Bind contact_data_model.contacts}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid Height="50" Margin="0 7 0 7">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="50"/>
                        <ColumnDefinition Width="80"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="20"/>
                        <RowDefinition Height="30"/>
                    </Grid.RowDefinitions>
                    <TextBlock FontWeight="Bold" FontSize="16" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding Name}"></TextBlock>
                    <TextBlock  x:Name="TelelphoneNumberTextBlock" FontSize="12" Grid.Row="1" Grid.Column="1" Text="{Binding TelephoneNumber}" VerticalAlignment="Center"/>
                    <HyperlinkButton  VerticalAlignment="Center" HorizontalAlignment="Center" Click="HyperlinkButton_Click" FontSize="12" Grid.Row="1" Grid.Column="2">
                        <TextBlock Opacity="1" Foreground="{Binding Foreground, ElementName=TelelphoneNumberTextBlock, Mode=OneWay}" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12" Grid.Row="1" Grid.Column="2" Text="{Binding EmailInfo}"/>
                    </HyperlinkButton>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

My question is probably very simple to fix, but i've searched around alot without any success finding a solution to this specific issue.

The only way out of this situation i could come up with is by setting the textblock that it is inside the hyperlinkbutton opacity to 0 (so the hyperlinkbutton autosize still works as intended) and create a new textblock on top of the button with the same text and position as the button itself, example:

...
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12" Grid.Row="1" Grid.Column="2" Text="{Binding EmailInfo}"/>
<HyperlinkButton VerticalAlignment="Center" HorizontalAlignment="Center" Click="HyperlinkButton_Click" FontSize="12" Grid.Row="1" Grid.Column="2">
    <TextBlock Opacity="0" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12" Grid.Row="1" Grid.Column="2" Text="{Binding EmailInfo}"/>
</HyperlinkButton>
...

This solution is ugly and hackish. Im positive there is a more elegant solution to this problem but everything i've searched for does not touch this very specific issue and my xaml knowledge is extremely limited.

What would be the proper XAML solution to this issue?

CodePudding user response:

Update:

Here are the steps to change the foreground color of the HyperlinkBtn when the ListViewItem is selected.

  1. you need to give a name to the HyperlinkButton like HyperlinkBtn so that you could find the HyperlinkButton in the ListView.SelectionChanged event.
  2. Create a variable that is used to save the last selected HyperlinkButton.
  3. You could get the selected item in the SelectionChanged event and now you could change its foreground color as you want.
  4. Change the previously selected item color back to the normal one.

Here is the code that you could refer to:

 public HyperlinkButton previousItem { get; set; }

  private void Employees_List_View_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var _Container = Employees_List_View.ContainerFromItem(Employees_List_View.SelectedItem);
        //get the hyperlink button
        var HyperlinkBtn = (HyperlinkButton)FindMyChildByName(_Container, "HyperlinkBtn");
        // change the foreground as you want
        HyperlinkBtn.Foreground = new SolidColorBrush(Colors.Green);
        if (previousItem != null) 
        {
            //change the previous one back to red color if there is a previous one
            previousItem.Foreground= new  SolidColorBrush(Colors.Red);
        }
        //record the new Item as previousItem
        previousItem = HyperlinkBtn;
    }


    public static DependencyObject FindMyChildByName(DependencyObject parant, string ControlName)
    {
        int count = VisualTreeHelper.GetChildrenCount(parant);

        for (int i = 0; i < count; i  )
        {
            var MyChild = VisualTreeHelper.GetChild(parant, i);
            if (MyChild is FrameworkElement && ((FrameworkElement)MyChild).Name == ControlName)
                return MyChild;

            var FindResult = FindMyChildByName(MyChild, ControlName);
            if (FindResult != null)
                return FindResult;
        }

        return null;
    }

For your scenario, I'd suggest you overwrite the default style of the HyperlinkButton. You could change the Foreground color of the HyperlinkButton in the style to whatever you want. You could also change other behaviors of the HyperlinkButton inside the style.

Here is the example that I made, I've changed the Foreground color for different states. Like red in normal, yellow when pointer over, blue when pressed.

Xaml:

  <Page.Resources>
    <Style x:Key="HyperlinkButtonStyle1" TargetType="HyperlinkButton">
        <Setter Property="Background" Value="{ThemeResource HyperlinkButtonBackground}"/>
        <Setter Property="BackgroundSizing" Value="OuterBorderEdge"/>
        <Setter Property="Foreground" Value="Red"/>
        <Setter Property="BorderBrush" Value="{ThemeResource HyperlinkButtonBorderBrush}"/>
        <Setter Property="BorderThickness" Value="{ThemeResource HyperlinkButtonBorderThemeThickness}"/>
        <Setter Property="Padding" Value="{StaticResource HyperlinkButtonPadding}"/>
        <Setter Property="HorizontalAlignment" Value="Left"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
        <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
        <Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}"/>
        <Setter Property="FocusVisualMargin" Value="-3"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="HyperlinkButton">
                    <ContentPresenter x:Name="ContentPresenter" AutomationProperties.AccessibilityView="Raw" 
                                      BackgroundSizing="{TemplateBinding BackgroundSizing}"
                                      Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}"
                                      BorderBrush="{TemplateBinding BorderBrush}"
                                      ContentTemplate="{TemplateBinding ContentTemplate}"
                                      Content="{TemplateBinding Content}" CornerRadius="{TemplateBinding CornerRadius}" 
                                      ContentTransitions="{TemplateBinding ContentTransitions}" 
                                      HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Padding="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup x:Name="CommonStates">
                                <VisualState x:Name="Normal"/>
                                <VisualState x:Name="PointerOver">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Yellow"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPointerOver}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPointerOver}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Pressed">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="Blue"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundPressed}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushPressed}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState x:Name="Disabled">
                                    <Storyboard>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonForegroundDisabled}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBackgroundDisabled}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                        <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
                                            <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource HyperlinkButtonBorderBrushDisabled}"/>
                                        </ObjectAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </ContentPresenter>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

And use it like this:

 <HyperlinkButton Style="{StaticResource HyperlinkButtonStyle1}"  VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12" Grid.Row="1" Grid.Column="2" Content="{Binding EmailInfo}">
  • Related