Binding Button IsEnabled to TextBox.CanUndo - wpf

I'm trying to bind a button to a textbox's CanUndo property, except that I can't get it to work. I tried a direct binding like
IsEnabled="{Binding CanUndo, ElementName=txtDocument}"
But that didn't work. Button stayed disabled even after typing in textbox which would change the CanUndo property to true.
I also tried
<Button IsEnabled="False" >
<Button.Resources>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding CanUndo, ElementName=txtDocument}" Value="True">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Resources>
<Image Source="/Images/32/undo.png" />
</Button>
I also tried with two separate datatriggers, one for enabled true and one for false, but still didn't work. Am I missing something fundamental? Like maybe this property doesn't raise the required events for binding changes?
Thanks

Set the Button's command property to the ApplicationCommands.Undo command and specify the CommandTarget property to be the TextBox.
The button will set the IsEnabled property based on ApplicationCommands.Undo command CanExecute state. The button will be disabled when CanExecute is false and enabled when CanExecute is true.
<StackPanel>
<TextBox x:Name="txtDocument">Test
</TextBox>
<Button Command="ApplicationCommands.Undo" CommandTarget="{Binding ElementName=txtDocument}" Content="Undo"/>
</StackPanel>

Related

Binding IsEnabled to Text.Length only works one way

I have a TextBox, which I want to be enabled only if another TextBox has text in it. I am binding the Text.Length property of the first TextBox to the IsEnabled property on the second box. I have also tried binding the Text property of the first box and using a converter to convert to a bool. Both methods result in the second box being enabled when text is entered into the first but when the text is deleted the second box isn't disabled.
I have tried setting NotifyOnSourceUpdated and NotifyOnTargetUpdated to true but neither has any effect.
<TextBox Name="textBox1"/>
<TextBox Name="textBox2" IsEnabled="{Binding ElementName=textBox1, Path=Text.Length}"/>
So my question is what is needed for textBox2 to be disabled when the text in textBox1 is deleted.
This should work -
<TextBox Name="textBox1"/>
<TextBox Name="textBox2">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding Text.Length, ElementName=textBox1}"
Value="0">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Issue with your code is you are binding IsEnabled bool property to Text.Length property whose type is int. So, either you need to use the converter or do it through triggers as i posted above.

Listbox doesn't detect selected item

I have the following listbox:
<ListBox ItemsSource="{Binding AvailableTemplates}" Style="{DynamicResource SearchListBoxStyle}" SelectedItem="{Binding SelectedTemplate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding}" GroupName="group" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
this doesn't detect as selected item changed if i select on the radiobutton. It only detects if i click under the radio button on the listbox row. Any ideeas how to amend to detect as selected item changed when clicking on the radio button?
If you only want to synchronize the RadioButton.IsChecked with ListBoxItem.IsSelected, you can use a binding
<RadioButton Content="{Binding}" GroupName="group"
IsChecked="{Binding Path=IsSelected, RelativeSource={
RelativeSource AncestorType={x:Type ListBoxItem}},Mode=TwoWay}"/>
If you don't want your items synchronized, you can use a Trigger that sets IsSelected whenever the item gets keyboard focus, although this will only keep an item selected as long as it has keyboard focus
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="IsSelected" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
And if you want it selected regardless of if the element still has keyboard focus or not, you have to use a little code behind
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
protected void SelectCurrentItem(object sender, KeyboardFocusChangedEventArgs e)
{
ListBoxItem item = (ListBoxItem)sender;
item.IsSelected = true;
}
You can not mistake: listBox.SelectedItem and radioButton.IsChecked
They are totally different things,
SelectedItem is called the ListBoxItem, your radiobutton is within a listboxitem.
You must make a binding for property IsChecked (RadioButton).
Try setting ListBoxItem.IsHitTestVisible to false (you may do this in xaml). It solved my selection problem elementary. My problem was that selection only worked when I clicked on white space in ListBox line but not a custom content.

Trying to add a trigger to a button to change the button's Content property

I've got a UserControl that has a button on it. The UserControl has a DependencyProperty called IsNew. This is a bool value that is set to true if the object being edited in the control was newly created and hasn't been written to the database yet. It is false otherwise.
I've got a button that currently reads "Save". I've been asked to change the button so it reads "Insert" if the row is new. I now have the XAML below for the button:
<Button Background="{DynamicResource ButtonBackground}"
Click="SaveButton_Click"
Content="Save"
FontSize="20"
FontWeight="Bold"
Foreground="{DynamicResource ButtonForeground}"
Height="60"
IsEnabled="{Binding Path=IsDirty, RelativeSource={RelativeSource AncestorType={x:Type cs:Editor}}}"
Margin="5"
Name="SaveButton"
TabIndex="7"
Visibility="{Binding Path=CanModify, Converter={StaticResource BoolToVisibility}}">
<Button.Triggers>
<Trigger Property="Editor.IsNew" Value="True">
<Setter Property="Button.Content" Value="Insert" />
</Trigger>
<Trigger Property="Editor.IsNew>
<Setter Property="Button.Content" Value="Save" />
</Trigger>
</Button.Triggers>
</Button>
I'm getting an exception at run time, whose inner exception reads:
Triggers collection members must be of type EventTrigger.
How do I get this to work? I could do this easily in the code-behind, but I want to do it in the XAML.
Thanks
Tony
You can use only EventTriggers in Control.Triggers. To use other kind of trigger (Trigger, DataTrigger) you should use style:
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<Trigger Property="Editor.IsNew" Value="True">
<Setter Property="Button.Content" Value="Insert" />
</Trigger>
And also your Property="Editor.IsNew" and Property="Button.Content" won't work because triggers belongs to Button and trigger will try to find property "Editor.IsNew" on button. You can fix it by changing trigger to DataTrigger and bind with RelativeSource to your UserControl and its IsNew property. And Property="Button.Content" needs to be changed to Property="Content".
The reason for this is that a control can only implement EventTriggers. The other kind of triggers you need have to be implemented in a style, and then your button uses that style.

How Biniding Mode(TwoWay) works?

I was trying out some dummy application just to test binding modes. So, just curious to know how did the binding modes work. I have this xaml code-
<Button x:Name="btn"
Height="20"
Width="200"
VerticalAlignment="Top">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled"
Value="{Binding CanEnable, Mode=TwoWay}" />
<Style.Triggers>
<DataTrigger Binding="{Binding TextChanged}" Value="true">
<Setter Property="IsEnabled"
Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Here by button IsEanbled is binded to my viemodel property "CanEanble" whose default value is false. Now in my trigger i was listening to "TextChanged" property and setting button IsEnabled to true. Button gets enabled as it should be but the property "CanEnable" did not set to true even the biding mode is set to TwoWay..
Why this is happening??
By setting the value in the trigger you basically remove the binding you previously set in the style setter. Take a closer look at the style. You will notice that you basically you set the property IsEnabled twice. First in the style setter, second in the trigger. It is logical that the second value overrides the previous value.
The desired effect can be achieved from code if you set the value of the dependency property using SetCurrentValue method:
SetCurrentValue(Button.IsEnabledProperty, true);
This way the bindings set on this property will not be removed and it will work as expected.

How do I bind to another control's property from a trigger?

In my particular case, I want to bind to the IsReadOnly property of a TextBox to set the Content property of a Button? They are both part of the same StackPanel.
I've tried doing it with a DataTrigger with a Binding to the ElementName of the TextBox and a Trigger using the TextBox name as the SourceName.
Any thoughts?
You need to specify the trigger as part of a style -- the Triggers collection on the Button itself can only contain event triggers. With that in mind, a DataTrigger works fine. However, there is a wrinkle: the value from the Trigger Setter won't overwrite a local Content property. So you have to set the default Content in the Style as well. Here's how it looks:
<Button> <!-- Note no content set directly on button -->
<Button.Style>
<Style TargetType="Button">
<Setter Property="Content" Value="You may write!!!" /> <!-- Here is the 'normal' content -->
<Style.Triggers>
<!-- Here is how we bind to another control's property -->
<DataTrigger Binding="{Binding IsReadOnly, ElementName=textBox}" Value="True">
<Setter Property="Content" Value="NO NO NO" /> <!-- Here is the 'override' content -->
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Have you tried this:
<StackPanel x:Name="LayoutRoot">
<Button Width="75" Content="{Binding IsReadOnly, ElementName=textBox, Mode=Default}" />
<TextBox x:Name="textBox" VerticalAlignment="Top" Text="TextBox" />
</StackPanel>
??

Resources