如何使用ViewModel的绑定而不是ListView的DataType?

时间:2016-09-28 15:00:32

标签: c# xaml listview mvvm uwp

首先让我为可能有点模糊的标题道歉,我很难解释我的问题!也许这就是为什么我很难得到任何谷歌搜索结果,这篇文章最接近我的问题(我认为):How to bind to a property of the ViewModel from within a GridView

无论如何,我有一份动态生成的新闻文章列表。用户可以选择按“星号”按钮以将文章添加到他/她的收藏夹列表中。因此,只有在用户登录时才能看到此“星形”按钮。

我正在尝试通过将Button的Visibility设置为ViewModel中名为IsLoggedIn的属性来实现此目的。但是,因为这发生在我的ListView中,它试图在Article内找到属性IsLoggedIn而不是ViewModel。

所以我想我的问题归结为:如何绑定到Databound ListView中的ViewModel属性?

<ListView ItemsSource="{x:Bind VM.Articles, Mode=OneWay}" ItemClick="DebuggableListView_ItemClick" IsItemClickEnabled="True" SelectionMode="None" Grid.Row="1" VerticalAlignment="Top">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="models:Article">
            <Grid Margin="0,10,10,10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*"/>
                    <ColumnDefinition Width="4*"/>
                </Grid.ColumnDefinitions>

                <Image Source="{Binding Image}" Margin="0,0,10,0" Grid.Column="0" VerticalAlignment="Top" ImageFailed="Image_ImageFailed"/>
                <Button Visibility="{x:Bind VM.IsLoggedIn, Converter={StaticResource BooleanToVisibilityConverter}, Mode=OneWay}" FontFamily="Segoe MDL2 Assets" Content="&#xE734;" FontSize="30" Background="Transparent" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
                <StackPanel Grid.Column="1">
                    <TextBlock TextWrapping="Wrap" FontWeight="Bold" Text="{Binding Title}"/>
                    <TextBlock TextWrapping="Wrap" Text="{Binding Summary}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

请求的文章类:

public sealed class Article
{
    public int Id { get; set; }
    public int Feed { get; set; }
    public string Title { get; set; }
    public string Summary { get; set; }
    public DateTime PublishDate { get; set; }
    public string Image { get; set; }
    public string Url { get; set; }
    public string[] Related { get; set; }
    public Category[] Categories { get; set; }
    public bool IsLiked { get; set; }
}

好的,所以目前我通过拥有一个获取我的VM单例的属性来实现它,但是我确信必须有一种更简洁的方法来获得像这样的简单工作。我添加了一个示例rar(OneDrive链接:https://1drv.ms/u/s!Ar4fOTiwmGYnyWbwRY6rM0eFsL9x),它在VM中有一个列表,一个ViewModel,一些虚拟数据和一个Visibility属性。如果你可以在没有我的脏方法的情况下使用它,请随意提交作为答案。

1 个答案:

答案 0 :(得分:0)

使用相对绑定可以最好地解决此类型问题。而不是直接绑定到当前的DataContext,即这种情况下ListView的ItemsSource中的单个项,您可以绑定到任何祖先元素的属性。

给出一个简单的ViewModel类

public class ViewModel: ViewModelBase
{
    private bool _isLoggedIn;
    public bool IsLoggedIn
    {
        get { return _isLoggedIn; }
        set
        {
            _isLoggedIn = value;
            RaisePropertyChanged(() => IsLoggedIn);
        }
    }

    public IEnumerable<string> Items
    { get { return new[] {"One", "Two", "Theee", "Four", "Five"}; } }
}

然后可以将视图定义为

<Window x:Class="ParentBindingTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ParentBindingTest"
        Title="MainWindow"
        Width="300" Height="400">

    <Window.DataContext>
        <local:ViewModel />
    </Window.DataContext>

    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <CheckBox Margin="16,8" HorizontalAlignment="Left" Content="Logged In?" IsChecked="{Binding IsLoggedIn, Mode=TwoWay}" />

        <ListView Grid.Row="1" Margin="16,8" ItemsSource="{Binding Items}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <Button Visibility="{Binding Path=DataContext.IsLoggedIn, RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Converter={StaticResource BooleanToVisibilityConverter}}">
                            <Image Source="Images\remove.png" />
                        </Button>

                        <TextBlock Text="{Binding}" />
                    </StackPanel>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Window>

请注意,在DataTemplate项中,TextBlock的Text属性直接绑定到该项。

然而,按钮的可见性不仅限于它自己的DataContext,而是绑定到ListView本身,使用相对绑定。