繁体   English   中英

在ItemsSource中无结果时隐藏ListView

[英]Hide ListView when no results in ItemsSource

我正在使用Visual Studio 2015和MVVM Light Toolkit来构建WPF应用程序。 当用户单击DataGrid的员工时,我们将显示记录的详细信息以允许进行编辑。 此详细信息区域包含两个选项卡:“人口统计”和“测试”。 “测试”选项卡显示此人的测试的ListView

结构如下:

MainWindow.xaml

<DataTemplate x:Key="EmployeeSearchTemplate">
    <view:EmployeeSearchView />
</DataTemplate>

<ContentControl ContentTemplate="{StaticResource EmployeeSearchTemplate}" />

EmployeeSearchView.xaml

<UserControl.DataContext>
    <viewModel:EmployeeSearchViewModel />
</UserControl.DataContext>

<ContentControl Content="{Binding SelectedEmployee}"
                ContentTemplate="{StaticResource EmployeeViewTemplate}" .../>

当用户选择“测试”选项卡时,我们将搜索数据库并返回该员工的测试(如果有)。

EmployeeView.xaml

<DataTemplate x:Key="TestsViewTemplate">
    <views:TestsView />
</DataTemplate>

<TabControl SelectedIndex="{Binding SelectedTabIndex}">
    <TabItem>
        <!-- Demographic details of record here -->
    </TabItem>
    <TabItem>
        <!-- Employee test info here. When user selects this tab, search db 
             and return tests for this employee, if any -->
        <ContentControl Content="{Binding TestsVm}"
                        ContentTemplate="{StaticResource TestsViewTemplate}" /> 
    </TabItem>
</TabControl>   

这是EmployeeViewModel.cs的构造函数和一些属性:

private TestsViewModel _testsVm;
private int _selectedTabIndex;

public EmployeeViewModel ()
{
    // Other initialization code...

    _selectedTabIndex = 0;

    this.PropertyChanged += (o, e) =>
    {
        if (e.PropertyName == nameof(SelectedTabIndex))
        {
            // If tab 1 selected, the person is on the Tests tab
            // Perform search and populate the TestsVM object's Tests
            // by executing the RelayCommand on it
            if (SelectedTabIndex.Equals(1))
            {
                TestsVm = new TestsViewModel
                {
                    SelectedEmployeeId = EmployeeId
                };
                TestsVm.SearchTestsRelayCommand.Execute(null);
            }
        }
    };
} 

public TestsViewModel TestsVm
{
    get { return _testsVm; }
    set
    {
        if (Equals(value, _testsVm)) return;
        _testsVm = value;
        RaisePropertyChanged();
    }
}

public int SelectedTabIndex
{
    get { return _selectedTabIndex; }
    set
    {
        if (value == _selectedTabIndex) return;
        _selectedTabIndex = value;
        RaisePropertyChanged();
    }
}   

这是TestsView.xamlListView

<ListView ItemsSource="{Binding Tests}"
          Visibility="{Binding HasTests,
                               Converter={helpers:BooleanToVisibilityConverter WhenTrue=Visible,
                                                                               WhenFalse=Hidden}}">
    <ListView.View>
        <GridView>
            <!-- GridView columns here -->
        </GridView>
    </ListView.View>
</ListView>

这是来自TestsViewModel.cs的代码:

private ObservableCollection<TestViewModel> _tests;
private int _selectedEmployeeId;
private bool _hasTests;

public TestsViewModel()
{
    SearchTestsRelayCommand = new RelayCommand(CallSearchTestsAsync);

    this.PropertyChanged += (o, e) =>
    {
        if (e.PropertyName == nameof(Tests))
        {
            HasTests = !Tests.Count.Equals(0);
        }
    };
}  

public RelayCommand SearchTestsRelayCommand { get; private set; }

private async void CallSearchTestsAsync()
{
    await SearchTestsAsync(SelectedEmployeeId);
}

private async Task SearchTestsAsync(int employeeId)
{
    ITestDataService dataService = new TestDataService();

    try
    {
        Tests = await dataService.SearchTestsAsync(employeeId);
    }
    finally
    {
        HasTests = !Tests.Count.Equals(0);
    }
}   

public ObservableCollection<TestViewModel> Tests
{
    get { return _tests; }
    set
    {
        if (Equals(value, _tests)) return;
        _tests = value;
        RaisePropertyChanged();
    }
}

public bool HasTests
{
    get { return _hasTests; }
    set
    {
        if (value == _hasTests) return;
        _hasTests = value;
        RaisePropertyChanged();
    }
}

public int SelectedEmployeeId
{
    get { return _selectedEmployeeId; }
    set
    {
        if (value == _selectedEmployeeId) return;
        _selectedEmployeeId = value;
        RaisePropertyChanged();
    }
}

HasTests属性不会更改,因此在ListView为空时不会隐藏它。 请注意,我还尝试了以下方法以查看ListView可见性,并指出其自身的HasItems无济于事:

Visibility="{Binding HasItems,
   RelativeSource={RelativeSource Self},
   Converter={helpers:BooleanToVisibilityConverter WhenTrue=Visible,
                                                   WhenFalse=Hidden}}"  

我已经在其他地方成功使用了相同的BooleanToVisibilityConverter ,所以这与我的代码有关。 我愿意接受您的建议。 谢谢。

更新 :这是TestView.xaml的XAML:

<UserControl x:Class="DrugComp.Views.TestsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:helpers="clr-namespace:DrugComp.Helpers"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:sys="clr-namespace:System;assembly=mscorlib"
             xmlns:viewModel="clr-namespace:DrugComp.ViewModel"
             xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
             d:DesignHeight="300"
             d:DesignWidth="300"
             mc:Ignorable="d">
    <UserControl.Resources />
    <Grid Width="Auto"
          Height="700"
          Margin="5,7,5,5"
          HorizontalAlignment="Left">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="32" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="32" />
            <RowDefinition Height="32" />
            <RowDefinition Height="32" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0"
                   Grid.ColumnSpan="2"
                   HorizontalAlignment="Left"
                   Style="{StaticResource Instruction}"
                   Text="{Binding Instructions}" />
        <ListView Grid.Row="1"
                  Grid.ColumnSpan="2"
                  Width="Auto"
                  Margin="5"
                  HorizontalAlignment="Center"
                  VerticalAlignment="Top"
                  AlternationCount="2"
                  ItemContainerStyle="{DynamicResource CustomListViewItemStyle}"
                  ItemsSource="{Binding Tests}"
                  SelectedItem="{Binding SelectedTest}">
            <ListView.Style>
                <Style TargetType="{x:Type ListView}">
                    <Setter Property="Visibility" Value="Visible" />
                    <Style.Triggers>
                        <Trigger Property="HasItems" Value="False">
                            <!-- If you want to save the place in the layout, use 
                Hidden instead of Collapsed -->
                            <Setter Property="Visibility" Value="Collapsed" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </ListView.Style>
            <ListView.View>
                <GridView>
                    <GridViewColumn Width="50"
                                    DisplayMemberBinding="{Binding TestId}"
                                    Header="Test ID" />
                    <GridViewColumn Width="90"
                                    DisplayMemberBinding="{Binding EmployeeId}"
                                    Header="Employee ID" />
                    <GridViewColumn Width="90"
                                    DisplayMemberBinding="{Binding OrderedDate,
                                                                   StringFormat='MM/dd/yyyy'}"
                                    Header="Ordered Date" />
                    <GridViewColumn Width="119"
                                    DisplayMemberBinding="{Binding ValidReasonForTest.Description}"
                                    Header="Reason" />
                    <GridViewColumn Width="129"
                                    DisplayMemberBinding="{Binding OrderedByWhom}"
                                    Header="Ordered By" />
                    <GridViewColumn Width="90"
                                    DisplayMemberBinding="{Binding ScheduledDate,
                                                                   StringFormat='MM/dd/yyyy'}"
                                    Header="Scheduled Date" />
                </GridView>
            </ListView.View>
        </ListView>
    </Grid>
</UserControl>

正如乔所说,您没有收到通知。 而且,如果您出于某种原因而不是隐藏此ListView ,还需要HasTests ,他的答案将有所帮助。 但这不是XAML视图中执行此操作的方法。

更新:

比下面的答案更干净,更简单的方法

<!-- In the view's Resources -->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />

<!-- ... -->

<ListView 
    Visibility="{Binding HasItems, 
      RelativeSource={RelativeSource Self}, 
      Converter=BooleanToVisibility}" />

(第二种)最干净,最简单,最简单的方法是使用样式中的触发器,如下所示:

<ListView>
    <ListView.View>
        <GridView>
            <!-- GridView columns here -->
        </GridView>
    </ListView.View>
    <ListView.Style>
        <Style 
            TargetType="{x:Type ListView}" 
            BasedOn="{StaticResource {x:Type ListView}}">
            <Style.Triggers>
                <Trigger Property="HasItems" Value="False">
                    <!-- If you want to save the place in the layout, use 
                    Hidden instead of Collapsed -->
                    <Setter Property="Visibility" Value="Collapsed" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListView.Style>
</ListView>

只是请注意,您不能像这样设置XAML中的Visibility属性,因为这是一个“本地”值,它将取代Style所做的任何事情:

<ListView Visibility="Visible" ...>

当您想为控件的特定实例覆盖样式时,这是理想的行为,但是在编写触发器时,这会给您带来很多麻烦。

在这种特定情况下,我无法想象您会这样做的任何原因,但这是XAML中带有样式和触发器的普遍“陷阱”。 如果要为将由触发器驱动的属性设置特定的初始值,则可以在Style的未触发的Setter中进行设置:

        <Style 
            TargetType="{x:Type ListView}" 
            BasedOn="{StaticResource {x:Type ListView}}">
            <Setter Property="Visibility" Value="Visible" />
            <Style.Triggers>
                <Trigger Property="HasItems" Value="False">
                    <!-- If you want to save the place in the layout, use 
                    Hidden instead of Collapsed -->
                    <Setter Property="Visibility" Value="Collapsed" />
                </Trigger>
            </Style.Triggers>
        </Style>

然后,这都是一种样式或另一种样式,触发器将起作用。

ItemsControl任何后代都将支持HasItems属性ListBoxComboBoxMenuItem ,即可为其命名。 几乎所有旨在呈现动态项目集合的本机WPF控件 (像DevExpress这样的第三方控件供应商通常都会忽略这一点,并使用他们自己的,通常考虑不周的类层次结构)。 之所以会这样,是因为它总是存在的,非常易于使用,并且物品的来源无关紧要。 无论您做什么将物品放入该物品中,它都会在没有物品的情况下隐藏起来。

您的代码来更新HasTests:

this.PropertyChanged += (o, e) =>
{
    if (e.PropertyName == nameof(Tests))
    {
        HasTests = !Tests.Count.Equals(0);
    }
};

仅在更改整个属性“测试”(即,将其分配给新的ObservableCollection)时才会触发。 大概您不是在执行此操作,而是使用“清除”,“添加”或“删除”来更改测试的内容。

结果,您的HasTests永远不会得到更新。 还尝试更新Tests.CollectionChange事件以捕获添加/删除。

编辑:像这样

        this.PropertyChanged += (o, e) =>
        {
            if (e.PropertyName == nameof(Tests))
            {
                HasTests = !Tests.Count.Equals(0);
                //also update when collection changes:
                Tests.CollectionChanged += (o2, e2) =>
                {
                    HasTests = !Tests.Count.Equals(0);
                };
            }
        };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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