Search code examples
wpfuser-controls

WPF sending value to reusable usercontrol


I think this is a beginner's question for intermediate stuff in WFP. I have a user control which acts as a radiobutton using two normal buttons: (buttons changes color to show the current choice)

<UserControl x:Class="UI.UserControls.RadioUC" Loaded="UserControl_Loaded">
<Stackpanel DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">

    <TextBlock Text="{Binding Path=Title}"/>

    <Button x:Name="BtnYes" Content="YES" Click="BtnYes_Click">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="Click">
                <i:InvokeCommandAction Command="{Binding Command}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Button>

        <Button x:Name="BtnNo" Content="NO" Click="BtnNo_Click">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <i:InvokeCommandAction Command="{Binding Command}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
</Stackpanel>
</UserControl>

And the codebehind consists of 2 dependency properties Title & IsYes. When the buttons are clicked using the click events, IsYes property is modified.

public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(RadioUC),
        new FrameworkPropertyMetadata(default(string), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public static readonly DependencyProperty IsYesProperty = DependencyProperty.Register("IsYes", typeof(bool), typeof(RadioUC),
        new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

public bool IsYes {
        get { return (bool)GetValue(IsYesProperty); }
        set { SetValue(IsYesProperty, value); }
    }

public String Title {
        get { return (String)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }

private void BtnYes_Click(object sender, RoutedEventArgs e)
    {
        IsYes = true;
        //BtnYes.Background = new SolidColorBrush(Colors.ForestGreen);
        //BtnNo.Background = new SolidColorBrush(Colors.Gray);
    }

private void BtnNo_Click(object sender, RoutedEventArgs e)
    {
        IsYes = false;
        //BtnNo.Background = new SolidColorBrush(Colors.ForestGreen);
        //BtnYes.Background = new SolidColorBrush(Colors.Gray);
    }

And finally this how I use it:

<uc:RadioUC Title="Is it YES" IsYes="{Binding IsLocalYes, Mode=TwoWay}"/>

When the buttons are clicked individually, the IsYes property of the usercontrol gets modified successfully. There is no problem. The problem is when I modified IsLocalYes programmatically in the hosting window of the usercontrol, this change does not propagate into the usercontrol. So the question is how can I modify IsYes property of the usercontrol when IsLocalYes property changes?


Solution

  • Instead of creating your own UserControl, for what you want to achieve it's better to use a ToggleButton and change the ControlTemplate/Style.

    Read also this article


    Solution:

    ToggleButton-Style:

    <Style x:Key="YesNoToggleButtonStyle" TargetType="ToggleButton" BasedOn="{StaticResource {x:Type ToggleButton}}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        <StackPanel Grid.Column="1" Orientation="Horizontal">
                            <Grid x:Name="PART_GRIDYES" Background="Gray" MinWidth="6" MinHeight="6">
                                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">YES</TextBlock>
                            </Grid>
                            <Grid x:Name="PART_GRIDNO" Background="Gray" MinWidth="6" MinHeight="6">
                                <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center">NO</TextBlock>
                            </Grid>
                        </StackPanel>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="true">
                            <Setter TargetName="PART_GRIDYES" Property="Background" Value="ForestGreen"></Setter>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="false">
                            <Setter TargetName="PART_GRIDNO" Property="Background" Value="ForestGreen"></Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    

    Use it like this:

    <ToggleButton Style="{StaticResource YesNoToggleButtonStyle}" IsChecked="{Binding IsLocalYes, Mode=TwoWay}">Is it YES</ToggleButton>