Defining InputBindings within a Style
Asked Answered
C

2

24

I'm creating a WPF app using the MVVM design pattern, and I'm trying to extend the TabItem control so that it closes the tab when the user clicks the middle mouse button. I'm trying to achieve this using InputBindings, and it works very well until I try to define it within a style. I've learned that you cannot add InputBindings to a style unless you attach it using a DependencyProperty. So I followed this similar post here... and it works... almost. I can close one tab using the middle mouse button, but it won't work on any of the other tabs (all of the tabs are added at runtime and inherit the same style).

So I need some help. Why would this only be working the first time, and not after? Obviously I could create a custom control that inherits from a TabItem and make it work, but I'd like to figure this out as I can see this being expanded in my projects. I'm no expert on DependencyProperties, so please help me out. Thanks!

Style:

<Style TargetType="{x:Type TabItem}">
    <Setter Property="w:Attach.InputBindings">
        <Setter.Value>
            <InputBindingCollection>
                <MouseBinding MouseAction="MiddleClick" 
                              Command="{Binding CloseCommand}"/>
            </InputBindingCollection>
        </Setter.Value>
    </Setter>
    ...
</Style>

Class

public class Attach
{
    public static readonly DependencyProperty InputBindingsProperty =
        DependencyProperty.RegisterAttached("InputBindings", typeof(InputBindingCollection), typeof(Attach),
        new FrameworkPropertyMetadata(new InputBindingCollection(),
        (sender, e) =>
        {
            var element = sender as UIElement;
            if (element == null) return;
            element.InputBindings.Clear();
            element.InputBindings.AddRange((InputBindingCollection)e.NewValue);
        }));

    public static InputBindingCollection GetInputBindings(UIElement element)
    {
        return (InputBindingCollection)element.GetValue(InputBindingsProperty);
    }

    public static void SetInputBindings(UIElement element, InputBindingCollection inputBindings)
    {
        element.SetValue(InputBindingsProperty, inputBindings);
    }
}
Cutlery answered 18/4, 2010 at 1:37 Comment(1)
I have run into the exact problem described above even with the code suggested by Daniel. It seems there is something very strange when using the Attach class above, especially in a style. I found that DataContext was null "sometimes" when the InputBindings were added, so when the binding happened it couldnt locate the commands. I never found a solution but I ended up duplicating the bindings as in the answer below.Gingerly
C
17

Never mind, I figured it out myself. I ended up not even using the Attach class above... instead I used InputBindings on the ControlTemplate for the TabItem (which is a Border), so it looked something like this... I don't know why I didn't think of this in the first place.. :)

<ControlTemplate TargetType="{x:Type TabItem}">
    <Grid SnapsToDevicePixels="true">
        <Border x:Name="Bd" ...>
            <DockPanel>
                ...
            </DockPanel>
            <Border.InputBindings>
                <MouseBinding MouseAction="MiddleClick"
                              Command="{Binding CloseCommand}"/>
            </Border.InputBindings>
        </Border>
    </Grid>
    ...
</ControlTemplate>
Cutlery answered 19/4, 2010 at 1:49 Comment(0)
P
19

Your class "Attach" worked fine for me! If anyone needs, the trick is use style like this, with the x:Shared modifier:

<InputBindingCollection x:Key="inputCollection" x:Shared="False">
        <KeyBinding Key="Del" Command="{Binding DeleteItemCommand}"/>
</InputBindingCollection>

<Style TargetType="{x:Type TabItem}">
    <Setter Property="w:Attach.InputBindings" Value="{StaticResource inputCollection}" />
    ...
</Style>

Thanks!

Postilion answered 18/10, 2011 at 14:33 Comment(0)
C
17

Never mind, I figured it out myself. I ended up not even using the Attach class above... instead I used InputBindings on the ControlTemplate for the TabItem (which is a Border), so it looked something like this... I don't know why I didn't think of this in the first place.. :)

<ControlTemplate TargetType="{x:Type TabItem}">
    <Grid SnapsToDevicePixels="true">
        <Border x:Name="Bd" ...>
            <DockPanel>
                ...
            </DockPanel>
            <Border.InputBindings>
                <MouseBinding MouseAction="MiddleClick"
                              Command="{Binding CloseCommand}"/>
            </Border.InputBindings>
        </Border>
    </Grid>
    ...
</ControlTemplate>
Cutlery answered 19/4, 2010 at 1:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.