none
Setting Dependency Property on UserControl RRS feed

  • Question

  • I have a UserControl called CustomCheckBox based on, well, a CheckBox.

    <UserControl x:Class="RonM.CustomCheckBox" x:Name="ccb"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" 
                 MinHeight="40" MinWidth="20" Height="100" Width="50">
        
        <UserControl.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="CustomControlsResources.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </UserControl.Resources>
        <ContentControl>
            <CheckBox Name="CheckBox1" Style="{StaticResource valve_circle}" IsChecked="{Binding IsEnergized, ElementName=ccb}" />
        </ContentControl>
    </UserControl>
    

    My resource file has several styles to make the checkbox look like an electrical switch, a valve, etc.  This is all controlled by Dependency Properties.  so far, everything is working fine.

    Now, I want to make the electrical switch style be a momentary-on switch if desired.  That is, when the checkbox IsChecked = True, it will stay checked for a certain number of milliseconds, then turn off.  As shown above, the IsChecked property of the checkbox is bound to a DP called IsEnergized.

    I have added a new DP called TimeEnergized, which can be 0 or a number of milliseconds.  My intent is to control this new behavior entirely in the style, using a MultiDataTrigger and perhaps a storyboard to apply the amount of time before de-energizing the switch.

    <!--If auto-off, turn off after x seconds-->
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding IsEnergized, ElementName=ccb}" Value="True"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="{Binding IsEnergized, ElementName=ccb}" Value="False"/>
    </MultiDataTrigger>
    

    Now I know this trigger snippet makes no sense, because there is no lag time, but that will hopefully be achieved using a storyboard.  Presently, this XAML does not work at all.  While it compiles, when I look at the MainWindow.xaml where the user control lives, I get the following error:
    "A 'Binding' cannot be set on the 'Property' property of type 'Setter'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject."

    Search as I may, I can't seem to find any articles on using a Setter in XAML to set the value of a DP.  It's easy enough to do in code-behind, but then I'd need a multi-threaded solution to wait for the delay time before reversing the DP value.

    So my problem is 2-fold:  Create a storyboard animation with a time associated with it (which time can be tied to a DP), and then figure out how to actually set the IsEnergized DP back to False.

    Any ideas?  Thanks...


    Ron Mittelman

    Tuesday, December 18, 2012 5:26 PM

Answers

  • Hi RMittleman,

    I tried animation solution, it will break the binding, and will cause other issues, if you binding IsChecked property to "IsEnergized", I suggest you implement your auto-off behavior in the PropertyChanged event of "IsEnergized", in event handler(or set method), you could create a dispatchTimer, use this timer to control IsEnergized property, after two second, you change IsEnergized property to false, then your CheckBox will auto-off.

    Best regards,


    Sheldon _Xiao
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by RMittelman Thursday, December 20, 2012 6:45 PM
    Thursday, December 20, 2012 5:27 AM
    Moderator

All replies

  • Hi RMittleman,

    I suggest you use "BooleanAnimationUsingKeyFrames" to implement this behavior, below sample you could refer to:

    <StackPanel>
        <CheckBox Content="Animation" Name="button" IsChecked="{Binding ElementName=chk, Path=IsChecked, Mode=OneWay}" >
            <CheckBox.Triggers>
                <EventTrigger RoutedEvent="CheckBox.Checked">
                    <EventTrigger.Actions>
                        <BeginStoryboard>
                            <Storyboard>
                                <BooleanAnimationUsingKeyFrames Storyboard.TargetName="button" Storyboard.TargetProperty="IsChecked"  Duration="0:0:2">
                                    <DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:2" />
                                </BooleanAnimationUsingKeyFrames>
                            </Storyboard>
                        </BeginStoryboard>
                    </EventTrigger.Actions>
                </EventTrigger>
            </CheckBox.Triggers>
        </CheckBox>
        <CheckBox x:Name="chk" Content="Control"  />
    </StackPanel>

    Addtional, "<Setter Property="{Binding IsEnergized, ElementName=ccb}" Value="False"/>", this line will not work, because you need apply a property, for example "<Setter Property="IsChecked"" or others.

    Another method is Binding converter, you could add a Binding converter to this line:

    "IsChecked="{Binding IsEnergized, ElementName=ccb, Converter = {StaticResource ...}}" "

    then you could use DispatcherTimer to control the IsChecked property.

    Best regards,


    Sheldon _Xiao
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.


    Wednesday, December 19, 2012 8:39 AM
    Moderator
  • Thanks for the concise answer Sheldon.  I have a couple of follow-up questions based on this answer.

    Because I am using a style, I tried the above suggestion with some changes, right in the style:

    <!--If auto-off, turn off after x seconds-->
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding IsEnergized, ElementName=ccb}" Value="True"/>
        </MultiDataTrigger.Conditions>
        <MultiDataTrigger.EnterActions>
            <BeginStoryboard>
                <Storyboard>
                    <BooleanAnimationUsingKeyFrames Storyboard.TargetName="CheckBox1" Storyboard.TargetProperty="IsChecked" Duration="0:0:2">
                        <DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:2" />
                    </BooleanAnimationUsingKeyFrames>
                </Storyboard>
            </BeginStoryboard>
        </MultiDataTrigger.EnterActions>
    </MultiDataTrigger>
    

    There are 2 problems with this that I see.

    First, I think I am duplicating the trigger condition inside the Storyboard, so I have the condition twice.  I originally wanted a MultiDataTrigger because I want this to happen when the IsChecked is True, but only if another DP has a value.  The other DP would be how many milliseconds to pause before reversing the IsChecked state.  Now I see that the Duration and KeyTime elements are formatted a certain way, so I will back off that theory and make the other DP a boolean indicating whether the checkbox should turn off again at all when turned on.  I can put that in the condition line.

    Second, when I use the code as above, I get this error: Invalid Property.  CheckBox1 name cannot be found in the name scope of 'System.Windows.Controls.ControlTemplate'.  Even though your example is operating on a CheckBox in a stack panel, mine is in the context of a control template inside of a style.

    So:  Should I move the suggested XAML to the check box itself, like in your example, and can I still use a multi-data trigger, or does it need to be an event trigger?

    Thanks...


    Ron Mittelman

    Wednesday, December 19, 2012 4:47 PM
  • I tried moving the XAML out of the style, and into the CheckBox element like this:

    <CheckBox Name="CheckBox1" Style="{StaticResource valve_circle}" IsChecked="{Binding IsEnergized, ElementName=ccb}">
        <CheckBox.Triggers>
            <EventTrigger RoutedEvent="CheckBox.Checked">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <BooleanAnimationUsingKeyFrames Storyboard.TargetName="CheckBox1" Storyboard.TargetProperty="IsChecked"  Duration="0:0:2">
                                <DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:2" />
                            </BooleanAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </CheckBox.Triggers>
    </CheckBox>
    

    Now it compiles ok, without error.  However, when I select the user control in design mode in my MainWindow.xaml, and manually check the "IsEnergized" DP, it doesn't actually do anything.  The visual checkbox object does get energized, and turns on (and off when I uncheck the property), but waiting several seconds after turning it "on", it does not go off automatically.  Worse, when I actually run the project, clicking the object turns it on, but again it doesn't go off after several seconds.  Also, in run mode I can't turn it off again.  It is locked on.  I added: FillBehavior="Stop", and it was no longer locked ON in run mode, but when I turned it OFF again, now it's locked OFF, and I can't turn it ON again.  This is all in run mode.


    Ron Mittelman

    Wednesday, December 19, 2012 5:08 PM
  • Hi RMittleman,

    I tried animation solution, it will break the binding, and will cause other issues, if you binding IsChecked property to "IsEnergized", I suggest you implement your auto-off behavior in the PropertyChanged event of "IsEnergized", in event handler(or set method), you could create a dispatchTimer, use this timer to control IsEnergized property, after two second, you change IsEnergized property to false, then your CheckBox will auto-off.

    Best regards,


    Sheldon _Xiao
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by RMittelman Thursday, December 20, 2012 6:45 PM
    Thursday, December 20, 2012 5:27 AM
    Moderator
  • Thanks Sheldon, for all of your help.  I thought I could modify IsEnergized DP the same way I can use its value:

    <DataTrigger Binding="{Binding IsEnergized, ElementName=ccb}" Value="True">

    Apparently this does not work.  I have implemented your suggestion in code-behind by giving the IsEnergized DP a callback, which runs a method that starts a DispatchTimer for a specified amount of time.  In the tick event I make the IsEnergized DP the not of itself, and this works.  Of course I noticed it was flipping the value infinitely, so I set up a boolean flag, _autoChanging, and only if false, set it to true and start the timer, then in the Tick event set back to false.

    Thanks for all of the suggestions.


    Ron Mittelman

    Thursday, December 20, 2012 6:45 PM