簡體   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