Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Conditional Binding. Button.IsEnabled to SelectedIndex >= 0

I want to bind a buttons IsEnabled property to a condition like myObject.SelectedIndex >= 0. Is there a simple way to do this in the xaml (without having to do crazy things to any underlying objects)? I haven't really seen a good example.

Honestly, I wish this was as easy as Flex 3 ... I.E.:

<mx:Button enabled="{dataGrid.SelectedIndex >= 0}" ...
like image 715
Chris Klepeis Avatar asked May 26 '10 01:05

Chris Klepeis


2 Answers

SelectedIndex is -1 if nothing's selected, right? Reverse your logic and use a trigger:

<Button ...>
    <Button.Style>
        <Style TargetType="Button">
            <Setter Property="enabled" Value="True" />

            <Style.Triggers>
                <DataTrigger
                    Binding="{Binding SelectedIndex,ElementName=dataGrid}"
                    Value="-1">

                    <Setter Property="IsEnabled" Value="False" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    <Button.Style>
<Button>
like image 66
Matt Hamilton Avatar answered Sep 20 '22 09:09

Matt Hamilton


I haven't found a particularly easy-to-use way to embed an expression into XAML, so here's what I've been using instead:

BindingOperations.SetBinding(myBtn, Button.IsEnabledProperty, LambdaBinding.New(
    new Binding { Source = myObject,
                  Path = new PropertyPath(ComboBox.SelectedIndexProperty) },
    (int selectedIndex) => selectedIndex >= 0
));

You'll have to write this in C#, for example in the window's constructor.

This also works seamlessly for multi-sourced bindings:

BindingOperations.SetBinding(myBtn, Button.IsEnabledProperty, LambdaBinding.New(
    new Binding { Source = myObject,
                  Path = new PropertyPath(ComboBox.SelectedIndexProperty) },
    new Binding { Source = myObject2,
                  Path = new PropertyPath(Button.ActualHeightProperty) },
    (int selectedIndex, double height) => selectedIndex >= 0 && height > 10.5
));

Observe that the lambda is statically typed, and any type errors are (relatively) noisy, helping track them down. The lambda return type is also taken into account; you could use this to bind the width of one object to be a complex formula based on the width of another one...

This LambdaBinding class isn't built-in; you have to include the LambdaBinding.cs file.

Side note. It's a real shame that XAML doesn't allow expressions. Yes, I realise XAML is supposed to be "for designers" and free of this elusive thing we call application logic, but who are we kidding here... Firstly, the DataTrigger shown in the other answer is basically a conditional expression, and so no different (only much longer) than {Binding source.SelectedIndex >= 0}. Secondly, if the idea is simplicity, then the binding expressions that a designer is supposed to be able to write are far beyond the abilities of a non-programmer... if you need proof, consider something like this:

{Binding RelativeSource={RelativeSource AncestorType={x:Type UIElement}, 
                                        AncestorLevel=1},
         Path=IsEnabled}
like image 27
Roman Starkov Avatar answered Sep 21 '22 09:09

Roman Starkov