【问题标题】:Bind to ViewModel from ContextMenu in ListBox Item从 ListBox 项中的 ContextMenu 绑定到 ViewModel
【发布时间】:2017-11-14 17:22:43
【问题描述】:

我有一个带有命令“OpenCommand”、标志“IsConextMenuVisible”和可观察列表“链接”的 ViewModel。

public ObservableList<string> Links { get; set; }
public bool IsContextMenuVisible { get; set; }
public ICommand OpenCommand { get; set; }

在 XAML 中,我希望以下工作。

<ListBox ItemsSource="{Binding Links}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding}">
        <TextBlock.ContextMenu>
           <ContextMenu Visibility="{Binding IsContextMenuVisible, Converter={StaticResource BoolToVisibiltyHiddenConverter}}">
             <MenuItem Header="Open" Command="{Binding OpenCommand}" CommandParameter="{Binding}"/>
           </ContextMenu>
        </Textblock.ContextMenu>
      </TextBlock>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

我已经为 ContextMenu 上的内部绑定尝试了一些绑定表达式,但似乎没有任何效果。比如:

Visibility="{Binding Path=DataContext.IsContextMenuVisible,
             Converter={StaticResource BoolToVisibilityCollapsedConverter},
             RelativeSource={RelativeSource AncestorType=ListBox}}"

【问题讨论】:

    标签: wpf binding


    【解决方案1】:

    正如孩子们所说,这是“有问题的”,因为上下文菜单不在可视化树中,所以 RelativeSource 的任何风格都不起作用。

    您通常可以绑定到PlacementTarget 的属性,但在这种情况下,您需要PlacementTarget 的视觉祖先,而RelativeSource 不会做其他东西的祖先。

    在 WPF 中,当可视化树中存在间隙时,最后一个选项始终是 BindingProxy。这是该类的外观(包括我从中窃取的 StackOverflow 问题的 URL——该类已被复制并粘贴在该站点上的许多问题和答案中):

    //  https://stackoverflow.com/questions/24452264/bindingproxy-binding-to-the-indexed-property
    public class BindingProxy : Freezable
    {
        #region Overrides of Freezable
    
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }
    
        #endregion
    
        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }
    
        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }
    

    你会像这样使用它。首先将 BindingProxy 创建为资源,在它可以“看到”所需元素的位置:

    <Window.Resources>
        <local:BoolToVisibiltyHiddenConverter x:Key="BoolToVisibiltyHiddenConverter" />
    
        <!-- {Binding} with no path will be the window's datacontext, the main viewmodel. -->
        <local:BindingProxy Data="{Binding}" x:Key="MainViewModelBindingProxy" />
    </Window.Resources>
    

    然后将其用于绑定的Source。所需的 DataContext 将是代理对象的 Data 属性,因此请提供相对于 Data 的路径:

    <TextBlock.ContextMenu>
        <ContextMenu 
            Visibility="{Binding Data.IsContextMenuVisible, 
                Converter={StaticResource BoolToVisibiltyHiddenConverter}, 
                 Source={StaticResource MainViewModelBindingProxy}}"
            >
            <MenuItem 
                Header="Open" 
                Command="{Binding OpenCommand}" 
                CommandParameter="{Binding}"
                />
        </ContextMenu>
    </TextBlock.ContextMenu>
    

    现在你遇到了另一个问题:菜单仍在弹出。它只是碰巧不可见。如果用户右键单击,它会隐形弹出,并在IsContextMenuVisible变为true时突然出现。那不是你想要的。

    您可以省略转换器,直接绑定到ContextMenu.IsEnabled:它仍然会弹出,但会变灰。这与常见的 Windows UI 实践一致。

    您还可以有一个样式触发器,以便 TextBlock 只有在您希望它有一个 ContextMenu 时才具有它。因为该触发器在 TextBlock 上,所以它在可视树中,我们可以使用常规的 RelativeSource 进行绑定。

    <TextBlock Text="{Binding}">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Style.Triggers>
                    <DataTrigger 
                        Binding="{Binding Data.IsContextMenuVisible, 
                            RelativeSource={RelativeSource AncestorType=ListBox}}"
                        Value="True">
                        <Setter Property="ContextMenu">
                            <Setter.Value>
                                <ContextMenu >
                                    <MenuItem 
                                        Header="Open" 
                                        Command="{Binding OpenCommand}" 
                                        CommandParameter="{Binding}"
                                        />
                                </ContextMenu>
                            </Setter.Value>
                        </Setter>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
    

    【讨论】:

    • 完美运行。谢谢!
    猜你喜欢
    • 2017-07-22
    • 1970-01-01
    • 2012-08-23
    • 2012-12-16
    • 1970-01-01
    • 2012-06-11
    • 1970-01-01
    • 1970-01-01
    • 2015-06-08
    相关资源
    最近更新 更多