wpfxamlattachedbehaviors

Syntax to bind a ToggleButton IsChecked property to a RichTextBox's attached behavior


WPF.

I have a behavior:

public class RichTextBehavior : Behavior<RichTextBox>
    {
         public bool TextBold
        {
            get { return (bool)GetValue(TextBoldProperty); }
            set { SetValue(TextBoldProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TextBold.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TextBoldProperty =
            DependencyProperty.Register("TextBold", typeof(bool), typeof(RichTextBehavior),
             new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextBoldChanged));

        private static void OnTextBoldChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behavior = d as RichTextBehavior;
            TextRange tr = new TextRange(behavior.AssociatedObject.Selection.Start, behavior.AssociatedObject.Selection.End);
            if (tr == null)
                return;

            // This Works, but also adds keyboard commands.
            //EditingCommands.ToggleBold.Execute(null, behavior.AssociatedObject);

            if ((bool)e.NewValue)
                //TextSelection ts = richTextBox.Selection;

                tr.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);
            else
                tr.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);

        }

That is attached to a RichTextBox as:

 <RichTextBox x:Name="RichTextControl" ...
                            >
                        <i:Interaction.Behaviors>
                            <b:RichTextBehavior TextFont="{Binding ElementName=Fonttype, Path=SelectedItem, Mode=TwoWay}"/>
                        </i:Interaction.Behaviors>

                    </RichTextBox>

I would like to bind a ToggleButton IsChecked property to the 'TextBold' property of the behavior (in XAML), something like:

<ToggleButton Name="ToggleBold"  Command="EditingCommands.ToggleBold" 
       CommandTarget="{Binding ElementName=RichTextControl}"
        IsChecked="{Binding ElementName=RichTextControl, Path=(TextBold), Mode=TwoWay}"
        .......................................           
 </ToggleButton>

How is this done? What is the syntax? Any idea is much appreciated. TIA

Edit:

I believe this should work, but it doesn't. In action, depressing the Bold Toggle button DOES change the typed text on the RichTextBox (as it should with the EditingCommands.ToggleBold), but as before, selecting text already in the RichTextBox does NOT change the state of the ToggleBold when bound to the IsChecked property.

I believe this should have worked ( but does not seem to respect the IsChecked Binding):

 <ToggleButton Name="ToggleBold"  
Command="EditingCommands.ToggleBold" CommandTarget="{Binding ElementName=RichTextControl}"
    IsChecked="{Binding ElementName=RichTextControl, 
 Path=(b:RichTextBehavior.TextBold), Mode=TwoWay}"........

I might add, for some reason, every key press in the RichTextBox seems to call SelectionChanged in the behavior twice, (not once as I would expect), and the second call does not seem to have the already-set value of the first call. Weird.


Solution

  • You can handle SelectionChanged event in the Behavior:

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectionChanged += RichTextBoxSelectionChanged;
        }
    
        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectionChanged -= RichTextBoxSelectionChanged;
        }
    

    and set TextBold propertly, by using GetPropertyValue:

        void RichTextBoxSelectionChanged(object sender, System.Windows.RoutedEventArgs e)
        {
            TextRange tr = new TextRange(AssociatedObject.Selection.Start, AssociatedObject.Selection.End);
            if (tr == null)
                return;
            FontWeight g = (FontWeight)tr.GetPropertyValue(TextElement.FontWeightProperty); 
            TextBold = g == FontWeights.Bold;
        }  
    

    Use two way binding (I use CheckBox):

    <StackPanel> 
        <RichTextBox x:Name="RichTextControl" >
            <i:Interaction.Behaviors>
                <local:RichTextBehavior TextBold="{Binding ElementName=fonttype, Path=IsChecked, Mode=TwoWay}"/>
            </i:Interaction.Behaviors> 
        </RichTextBox>
        <CheckBox x:Name="fonttype"  Content="Is Bold"   /> 
    </StackPanel>