【问题标题】:DataTemplate Binding for ListBox to ViewModel propertyListBox 到 ViewModel 属性的 DataTemplate 绑定
【发布时间】:2012-11-25 20:34:16
【问题描述】:

我正在使用带有 DataTemplate 的 ListBox。

                <ListBox Grid.Row="1" Grid.ColumnSpan="3" Grid.RowSpan="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
                                ItemsSource="{Binding Order.OrderLines}" >
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>

                            <TextBlock Grid.Column="0">Qty</TextBlock>
                            <TextBox Text="{Binding LineQty, Mode=TwoWay}" Grid.Column="1" />

                            <TextBlock Grid.Row="1" Grid.Column="0">Weight</TextBlock>
                            <TextBox Text="{Binding Path=LineWeight}" Grid.Row="1" Grid.Column="1" />

                            <TextBlock Grid.Column="0" Grid.Row="2">Pallet Weights</TextBlock>
                            <TextBox Text="{Binding PalletWeights}" Grid.Row="2" Grid.Column="1" />
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>

            </ListBox>

TextBox 值绑定正确。问题是我的 ViewModel 上有一个名为“ViewMode”的属性,我将 TextBox 的 IsEnabled 属性绑定到 App.xaml 样式 DataTrigger:

            <Style TargetType="{x:Type TextBox}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ViewMode}" Value="Add">
                    <Setter Property="BorderBrush" Value="White"></Setter>
                    <Setter Property="BorderThickness" Value="2,2,0,0"></Setter>
                    <Setter Property="BorderBrush" Value="Black"></Setter>
                    <Setter Property="BorderThickness" Value="0,0,2,2"></Setter>
                    <Setter Property="Background" Value="LightBlue"></Setter>
                    <Setter Property="Foreground" Value="Black"></Setter>
                    <Setter Property="FontWeight" Value="Bold"></Setter>
                    <Setter Property="IsEnabled" Value="true"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding ViewMode}" Value="Edit">
                    <Setter Property="BorderBrush" Value="White"></Setter>
                    <Setter Property="BorderThickness" Value="2,2,0,0"></Setter>
                    <Setter Property="BorderBrush" Value="Black"></Setter>
                    <Setter Property="BorderThickness" Value="0,0,2,2"></Setter>
                    <Setter Property="Background" Value="LightBlue"></Setter>
                    <Setter Property="Foreground" Value="Black"></Setter>
                    <Setter Property="FontWeight" Value="Bold"></Setter>
                    <Setter Property="IsEnabled" Value="true"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding ViewMode}" Value="View">
                    <Setter Property="IsEnabled" Value="false"></Setter>
                    <Setter Property="Foreground" Value="Gray"></Setter>
                </DataTrigger>
            </Style.Triggers>
            <Setter Property="Margin" Value="2" />
        </Style>

这适用于我所有的其他文本框。如何让 IsEnabled 属性在 DataTemplate 中工作? ListBox 的 DataContext 引用了 ViewModel 属性“Order”,所以我认为它应该能够看到 ViewModel 属性“ViewMode”。

谢谢, -Sid。

【问题讨论】:

    标签: wpf binding listbox styles datatemplate


    【解决方案1】:

    您需要在绑定中使用RelativeSource 标记扩展,以防您想像这样到达可视树 -

    <DataTrigger Binding="{Binding DataContext.ViewMode,
                 RelativeSource={RelativeSource FindAncestor,
                                   AncestorType = UserControl}}" 
                 Value="Add">
    </DataTrigger>
    

    默认情况下,您的TextBox 的DataContext 将是object of OrderLine,您的属性LineQty 所在的位置。因此,样式是在 OrderLine 的对象中搜索 ViewMode 属性而不是您的 ViewModel,因此您需要使用 RelativeSource 明确要求它在您的 UserControl 的 DataContext 中搜索它,即您的 ViewModel。

    【讨论】:

    • 太棒了!工作得很好,现在很有意义。谢谢解释!!!
    【解决方案2】:

    在 DataTemplate 中,您无法直接从 ViewModel 访问属性(您不会“继承”DataContext)。 假设你的 ViewModel 是整个视图的 DataContext,你可以创建一个代理:

    class BindingProxy : Freezable
    {
    
        #region Freezable Members
    
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }
    
        #endregion
    
        /// <summary>
        /// Saves the DataContext from the whole view
        /// </summary>
        public object DataContext
        {
            get { return (object)GetValue(DataContextProperty); }
            set { SetValue(DataContextProperty, value); }
        }
    
        public static readonly DependencyProperty DataContextProperty =
            DependencyProperty.Register("DataContext", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }
    

    然后,在你的 XAML 文件中,你需要引用 BindingProxy 所在的命名空间:

    <UserControl xmlns:utilities="clr-namespace:MyApp.NamespaceWhereBindingProxyIsLocated"
    ...
    

    稍后,您为视图创建一个 BindingProxy 实例并将其与视图的 DataContext 链接(注意 DataContext={Binding} 部分):

    <UserControl.Resources>
       <utilities:BindingProxy x:Key="proxy" DataContext="{Binding}"/>
    </UserControl.Resources>
    

    最后,您可以将其用作每个 TextBox 的 DataContext:

    <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            ..
                            <TextBox Text="{Binding Path={TemplateBinding DataContext.LineQty}, Mode=TwoWay}" DataContext="{Binding Source={StaticResource proxy}, Path=DataContext}"/>
                            ..
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
    

    希望对你有帮助

    【讨论】:

    • Daniel,我很困惑,因为 TextBox 正确绑定了 value 属性。是 IsEnabled 不起作用。这是作为 DataTrigger 的 App.xaml 样式的一部分。那么它是从 ListBox ItemsSource 中获取它的 DataContext,而不是从 ViewModel 中为 IsEnabled 解释它吗?我会看看你的建议,这似乎有点违反直觉。
    • 是的,就是这样。在样式中,您假设 TextBox 的 DataContext 是 ViewModel,但是当您处理模板时,原始 DataContext 不是“继承”的。因此,即使您的 ListBox“继承”了正确的 DataContext,您用于其项目的模板也没有。因此,为了使您的样式正常工作,您应该将 DataContext 设置为它所期望的。但是当您设置 DataContext 时,您的文本绑定将无法正常工作,因此您必须对其进行修复。我没有正确测试文本绑定,所以也许你可以告诉我它是否有效?
    • Daniel,我现在明白你所说的关于 DataContext 的意思了。 RV 的答案一针见血,将 App.xaml 更改为将我的 DataTrigger 更改为 RelativeSource。感谢您的建议和您的时间! -Sid。
    • 别担心。这容易多了..我只是避免直接修改样式,我不知道为什么:)
    猜你喜欢
    • 2016-01-11
    • 1970-01-01
    • 2011-04-21
    • 2021-05-21
    • 1970-01-01
    • 2018-09-30
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多