【问题标题】:WPF Set Visibility on DataTemplate UI Element from MVVM View ModelWPF 从 MVVM 视图模型中设置 DataTemplate UI 元素的可见性
【发布时间】:2016-07-12 16:20:21
【问题描述】:

我有一个设置为 DataTemplate 的控件:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="KEYBOARD_EN">
     <StackPanel>
               <Button Visibility="{Binding Path=RegisterButtonVisible}"  Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>

    </StackPanel>        
</DataTemplate>

在这个 DataTemplate 中有一个控件,我希望在其上设置各种视图模型的可见性:

  <Button Visibility="{Binding Path=RegisterButtonVisible}"  Style="{StaticResource ...} > Register </Button>

我用我的控件做路由事件,所以我尝试设置类似的东西,但无论我尝试什么,RegisterButtonVisible 属性都不会被拾取:

public partial class MainKeyboard : UserControl
{
    public static DependencyProperty RegisterButtonVisibleProperty;
    public Visibility RegisterButtonVisible
    {
        get { return (Visibility)GetValue(RegisterButtonVisibleProperty); }
        set { SetValue(RegisterButtonVisibleProperty, value); }
    }

      static MainKeyboard()
    {
        RegisterButtonVisibleProperty = DependencyProperty.Register("RegisterButtonVisible", typeof (Visibility),
            typeof (MainKeyboard));
    }       
 }

在我的 ViewModel 中我这样做:

    public Visibility RegisterButtonVisible // get, set, raisepropchange, etc

我的带有按钮的 DataTemplate 包含在 userControl 中:

<UserControl x:Class="Bleh.Assets.MainKeyboard"
         x:Name="TheControl"
         Unloaded="UserControl_Unloaded">

<Viewbox>
    <Grid>
        <ContentControl Name="ctrlContent" Button.Click="Grid_Click" />
    </Grid>
</Viewbox>

在我的观点中使用如下:

  <assets:MainKeyboard 
      RegisterButtonVisible="Collapsed"
                                 Loaded="MainKeyboard_Loaded">
                <b:Interaction.Triggers>
                    <b:EventTrigger EventName="Register">
                        <b:InvokeCommandAction Command="{Binding ConfirmEmailAddressCommand}"/>
                    </b:EventTrigger>
                    <b:EventTrigger EventName="Enter">
                        <b:InvokeCommandAction Command="{Binding EnterKeyCommand}"/>
                    </b:EventTrigger>
                </b:Interaction.Triggers>
            </assets:MainKeyboard>

请注意这个属性:

 RegisterButtonVisible="Collapsed" 

这是我的依赖属性。它显示在智能感知中,因此 CLR 已正确注册它,但它没有获取属性分配(Collapsed 被忽略)。

这让我觉得它非常接近,但我记得有人告诉我我不能这样做,因此 EventTriggers(这显然是数据模板和 MVVM 的常见问题)。

所以一种选择是在 Interaction 命名空间中使用某些东西,就像我做我的事件触发器一样(我只需要以某种方式在这个按钮上触发一个“可见性”触发器,至少我认为)。

什么是正确的在MVVM中做这件事的任何方式?

【问题讨论】:

  • 不要纠结于正确/错误...这真的归结为更容易保持前进。恕我直言。我会如您所示在VM上进行可见性,并从其他属性触发它,并在代码中为此效果添加一个很好的注释。
  • 我同意;我只是想以 MVVM 的方式做事;首先查看所有内容。但我绝对同意你的看法。

标签: wpf mvvm wpf-controls visibility datatemplate


【解决方案1】:

修复代码

为了使您现有的代码正常工作,您需要告诉 WPF 应该从哪个对象中读取 RegisterButtonVisible。如果它是一个用户控件,请给 UserControl 一个名称,然后通过 ElementName 引用该元素,如下所示:

<UserControl ... lots of stuff here 
             x:Name="TheControl"
             >

在您的按钮绑定中:

<Button Visibility="{Binding ElementName=TheControl, Path=RegisterButtonVisible}"  Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>

当然,如果由于按钮和用户控件位于不同的文件中而无法做到这一点,您仍然可以使用祖先绑定:

<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type assets:MainKeyboard}},
                             Path=RegisterButtonVisible}"
        Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>    

对于每个按钮,它将向上查找最近的 assets:MainKeyboard 实例,然后绑定到 RegisterButtonVisible 属性。

使用 MVVM

如果您想使用 MVVM(而不是在控件上)实现相同的目的,则需要使用转换器将布尔值转换为可见性属性,如下所示:

<Button Visibility="{Binding IsRegistrationAllowed, Converter={StaticResource BoolToVis}}"  Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>

当然,这假设您的 DataContext 设置正确并指向您的 ViewModel。

【讨论】:

  • 我不能做后者,因为我的数据上下文是不可定义的;它是 IoC 并且需要模拟对象,因此没有允许我设置数据上下文的构造函数。所有 ViewModel 都是通过数据模板定义的。但我现在正在尝试前者。
  • @nocarrier 您也可以在代码隐藏中设置 DataContext。因此,在您的 Window(或 UserControl 等)中,您可以执行 this.DataContext = myViewModelThatWasPassedIntoTheConstructor。然后,您可以随意使用 WPF 代码中的数据上下文。
  • 两个问题@MackieChan - 为了路由这个属性,我将在“TheControl”的代码后面放什么?简单地指定 ElementName=TheControl 似乎没有任何效果。其次,我可以在承载此用户控件的控件中指定一种样式来设置可见性,如以下答案:stackoverflow.com/a/7001109/589509
  • 我用我的数据模板添加了一些编辑,以及它在视图中的使用方式(使用 UC)。
  • @nocarrier (1st) 我添加了一个不使用名称,而是使用祖先绑定的示例。 (2nd) 是的,您可以定义一个也使用祖先绑定的动态样式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-22
  • 2013-03-10
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 2020-02-08
相关资源
最近更新 更多