views:

300

answers:

3

I'm getting to the point in a WPF application where all of the bindings on my controls are getting quite repetitive and also a little too verbose. Also if I want to change this binding I would have to change it in various places instead of just one.

Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax. I've looked around for such capabilities but I haven't found it.

What I'm doing now

<StackPanel>
    <ToggleButton x:Name="someToggleButton" />
    <Button Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
    <Grid Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
    <TextBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
    <CheckBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>

What I want to be able to do (Pseudocode)

<StackPanel>
    <StackPanel.Resources>
        <Variable x:Name="someToggleButtonIsChecked" 
                  Type="{x:Type Visibility}"  
                  Value="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
    </StackPanel.Resources>

    <ToggleButton x:Name="someToggleButton" />
    <Button Visibility="{VariableBinding someToggleButtonIsChecked}" />
    <Grid Visibility="{VariableBinding someToggleButtonIsChecked}" />
    <TextBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
    <CheckBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
</StackPanel>

Is there any similar type of similar feature or technique that will allow me to declare the binding source once and then reuse it?

+1  A: 

You can just bind someToggleButton's IsChecked property to a property on your viewmodel (the DataContext) and use that. It would look something like this:

<StackPanel>  
<ToggleButton x:Name="someToggleButton" IsChecked="{Binding ToggleVisibility, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}"   /> 
<Button Visibility="{Binding ToggleVisibility}" /> 
<Grid Visibility="{Binding ToggleVisibility}" /> 
<TextBox Visibility="{Binding ToggleVisibility}" /> 
<CheckBox Visibility="{Binding ToggleVisibility}" /> 
</StackPanel> 

This would require that your Window's DataContext has a property called ToggleVisibility of type Visibility.

EDIT:

To eleborate further, your viewmodel could look like this:

public class SomeViewModel : INotifyPropertyChanged
{

    private Visibility toggleVisibility;

    public SomeViewModel()
    {
        this.toggleVisibility = Visibility.Visible;
    }

    public Visibility ToggleVisibility
    {
        get
        {
            return this.toggleVisibility;
        }
        set
        {
            this.toggleVisibility = value;
            RaisePropertyChanged("ToggleVisibility");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

And you would then set an instance of it as the DataContext on the Window or even just on the StackPanel

klausbyskov
This is actually similar to what I had originally but it was quite a bit of code to add to my code behind for for only a single property. In the long run if there are no XAML based solutions I'll probably revert back to doing it a bit more MVVM like.
jpierson
@Jpierson You really should go back to using MVVM. I mean you already have a textbox, the value of which you could bind to the viewmodel, and a button whose command you could also bind to the viewmodel. In my opinion, viewmodels are the right way to go, whereas code in you windows is not.
klausbyskov
I use ViewModels currently in my main code but this code is in a little Dialog and adding a ViewModel too it seemed unecessary since there really was no "Model" to back my ViewModel. Another issue I'm having at the same time is that have the case where I need to chain a BoolToVisibilityConverter and an InverseBoolConverter within a binding on a control. By making a property in my code behind or in the ViewModel specifically called IsNotDeleteMode would work but then I have IsAddMode, IsNotAddMode, IsDeleteMode, and IsNotDeleteMode propeties. Seems like a mess to me regardless of where it goes.
jpierson
By the way, the idea of chaining/piping ValueConverters is where I started to release that the corresponding Binding declaration would start to become overwhelming relative to the rest of the XAML and therefore was looking for a solution for reusing such bindings. So even if I'm using a ViewModel, unless I want to write all of the negated property forms that I listed in my last comment, I still see a need for a more reusable technique for binding declarations.
jpierson
Found a solution for the multiplicity of inverse boolean properties at the following URL (see the post by leblancmeneses). I would still like to know however if there are any other ways to reuse binding declarations otherwise I'll look into a custom solution using markup extenions.http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/f2154f63-ccb5-4d6d-8c01-81f9da9ab347/
jpierson
A: 

Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax.

Perhaps you can do that with PyBinding. I don't know the extent of its capabilities, but I use it all the time ot avoid type converters. Here is an example I use a lot.

Visibility="{p:PyBinding BooleanToVisibility(IsNotNull($[.InstanceName]))}"
  • BooleanToVisibility is a function I wrote in IronPython.
  • $[.InstanceName] binds to the InstanceName property of the current data-bound item.

EDIT: You can also use this to bind one UI's property to another's. Here is some info from the help file.

  • $[NameTextBlock.Text] - The text property of the element with x:Name equal to "NameTextBlock"
  • $[NameTextBlock] - An actual TextBlock instance, rather than one of its properties
  • $[{Self}] - Bind to your self. Equivalent to {Binding RelativeSource={RelativeSource Self}}
  • $[{Self}.Text] - The Text property off your self. Equivalent to {Binding Path=Text, RelativeSource={RelativeSource Self}}

http://pybinding.codeplex.com/

Untested Theory

<StackPanel> 
    <ToggleButton x:Name="someToggleButton" /> 
    <Button Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" /> 
    <Grid Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/> 
    <TextBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/> 
    <CheckBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/> 
</StackPanel> 

Second Attempt

<StackPanel> 
    <ToggleButton x:Name="someToggleButton" /> 
    <Button Name="myButton" Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" /> 
    <Grid Visibility="{p:PyBinding $[myButton.Visibility]}"/> 
    <TextBox Visibility="{p:PyBinding $[myButton.Visibility]}"/> 
    <CheckBox Visibility="{p:PyBinding $[myButton.Visibility]}"/> 
</StackPanel> 
Jonathan Allen
Thats pretty neat, and something I was unaware of. The problem still is with that approach is that I actually want to store the binding in one place. Say for example that at some point I want to modify the visibility of all of these controls to be driven off of the ToggleButton.IsEnabled property. I would have to change it multiple places and that's why I'm interested in declaring it once and then reusing it.
jpierson
You could name your Button and then bind the visibility of all the other controls to it. I know it isn't exactly what you want, but it's closer.
Jonathan Allen
Yep, I've done that too. Taking it to another level I could make a generic custom control with a single property called something like Value (MyBindingControl<T>). Then for each type that I want to reuse a binding for I could make a specialized class such as StringBindingControl : MyBindingControl<string>. This could then be used as a sort of variable to which other controls could bind to. The main problem with this technique is that it's difficult to reuse within generic styles/templates in resource dictionaries since it would most likely rely on named controls.
jpierson
A: 

Just looking at the original code, you could group the necessary elements into their own container and then manage the container Visibility:

<StackPanel>
    <ToggleButton x:Name="someToggleButton" />
    <StackPanel Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
        <Button />
        <Grid />
        <TextBox />
        <CheckBox />
    </StackPanel>
</StackPanel>

Actually, today I would do this with the VSM - have a state with the elements Visible and a state with them not Visible, then use two GoToState Behaviors on the Toggle button to set the state based on the button's toggle state.

Joel Cochran
Good point, however this code was just created to demonstrate the desire to reuse bindings in general. One could perceive cases where the elements that are bound are not neatly located as peers within a single container.
jpierson