简体   繁体   中英

How to use ElementName binding from resource?

I've added a MenuFlyout to a button in ItemsControl.ItemTemplate. Also I was able to bind current item as CommandParameter. Now I want to bind Command to a MenuFlyoutItem. In codebehind :

LayoutRoot.DataContext = this;

So if i bind to LayoutRoot I will actually bind to my current UserControl. But the following binding is not working:

Command="{Binding ActivateProfileCommand, ElementName=LayoutRoot}"

It gives me not errors in Output but it's not working. Here's the full example:

<controls:HeaderDecorator x:Uid="AccountsHeader" Text="Accounts" x:Name="LayoutRoot" Name="LayoutRoot">
    <controls:HeaderDecorator.Resources>
        <MenuFlyout x:Key="AccountMenuFlyout">
            <MenuFlyoutItem Text="Activate" Name="Activate" 
                        Command="{Binding ActivateProfileCommand, ElementName=LayoutRoot}"
                        CommandParameter="{Binding}" />
        </MenuFlyout>
    </controls:HeaderDecorator.Resources>
    <StackPanel Orientation="Vertical">
        <ItemsControl ItemsSource="{Binding Settings.Profiles}" >
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <HyperlinkButton Content="{Binding}"  FlyoutBase.AttachedFlyout="{StaticResource AccountMenuFlyout}" >
                        <i:Interaction.Behaviors>
                            <ic:ShowFlyoutBehavior />
                        </i:Interaction.Behaviors>
                    </HyperlinkButton>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </StackPanel>
</controls:HeaderDecorator>

Seems the problem is i'm trying to use shared object in Resources. Can I do it? And why not?

The issue you are seeing here is the MenuFlyoutItem is no longer in the datacontext you perhaps think it is. I'll try and explain this as best I can as a few I know who work with xaml have come across this and hit their heads off walls for days about it. It's also known to not show errors in your particular scenario; further increasing confusion.

In a nutshell. When the MenuFlyout is added inside the ItemTemplate of each item in your collection, it does not have access to the datacontext you perhaps think it does. In this case, the datacontext that the control now resides is actually the individual item within the collection it is sitting in.

There is however a solution to this. I have something similar to yourself. An ItemsControl which has it's ItemsTemplate defined that includes a UIElement who's FlyoutBase AP references a MenuFlyout defined in a resource dictionary.

The xaml is pretty much the same except I don't need the ElementName in the binding.

However, I have now turned my attention to the type that the collection holds. I have code that looks something like this.

public class AnItemToList
{
   public AnItemToList(Action commandDel)
   {
        TestCommand = new RelayCommand(commandDel);
   }
   public string Name { get; set; }
   public RelayCommand TestCommand { get; set; }
}

Note that the command is being defined in the item itself and that I'm passing the method that the command will execute via the constructor.

All I have to do for the command binding on the MenuFlyoutItem is

<MenuFlyoutItem Text="Activate"
                Name="Activate"
                Command="{Binding TestCommand}"/>

I don't have a command param set here as I just quickly put together a basic template Windows Phone app and the pre-packed ICommand implementation doesn't have a delegate set to take the param.

If you now stick a break point in the method the command is calling, you'll see it will be called from any of the MenuFlyoutItem 's bound to the command that references it.

Bare in mind that this isn't the only way of solving this problem; but it is one I use myself on occasion. For example, in WPF XAML you can make use of RelativeSource to go looking for the command on a parent control's datacontext .

Hope this helps.

Here's a general "Pair" object:

public class Pair : DependencyObject
{
    public static readonly DependencyProperty FirstProperty = DependencyProperty.Register("First",
        typeof(object), typeof(Pair), new PropertyMetadata(null));

    public static readonly DependencyProperty SecondProperty = DependencyProperty.Register("Second",
        typeof(object), typeof(Pair), new PropertyMetadata(null));

    public object First
    {
        get { return GetValue(FirstProperty); }
        set { SetValue(FirstProperty, value); }
    }

    public object Second
    {
        get { return GetValue(SecondProperty); }
        set { SetValue(SecondProperty, value); }
    }
}

In ItemTemplate i put something like this:

                <DataTemplate>
                    <Grid>
                        <Grid.Resources>
                            <viewModel:Pair x:Key="Tuple" First="{Binding DataContext, ElementName=LayoutRoot}" 
                                                           Second="{Binding}" />
                        </Grid.Resources>
                        <HyperlinkButton Content="{Binding Second.ProfileName}" 
                                     DataContext="{StaticResource Tuple}"
                                     FlyoutBase.AttachedFlyout="{StaticResource AccountMenuFlyout}" 
                        </HyperlinkButton>
                    </Grid>
                </DataTemplate>

Now I can easily reference Tuple elements from my Resource like this:

            <MenuFlyoutItem Text="Activate" Name="Activate" 
                        Command="{Binding First.ActivateProfileCommand}"
                        CommandParameter="{Binding Second}" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM